Authentication
LaunchKit Pro uses JWT access tokens and refresh tokens stored in the database. The frontend persists tokens in localStorage and attaches Authorization: Bearer <accessToken> to API requests.
Endpoints (summary)
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /auth/register | Public | Create account |
POST | /auth/login | Public | Sign in |
POST | /auth/refresh | Public | Rotate tokens |
POST | /auth/logout | Public | Revoke refresh token |
GET | /auth/me | Bearer | Current user profile |
POST | /auth/forgot-password | Public | Request reset email (mock) |
POST | /auth/reset-password | Public | Reset with token |
POST | /auth/resend-verification | Public | Resend verification email |
POST | /auth/verify-email | Public | Verify email with token |
Register
curl -s -X POST http://localhost:4000/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "ChangeMe123",
"name": "Demo User"
}' | jqPassword rules: minimum 8 characters with at least one letter and one number.
Login
curl -s -X POST http://localhost:4000/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "ChangeMe123"
}' | jqResponse includes accessToken, refreshToken, and user.
Refresh token
When the access token expires, the client calls:
curl -s -X POST http://localhost:4000/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "<refreshToken>"}' | jqLogout
curl -s -X POST http://localhost:4000/auth/logout \
-H "Content-Type: application/json" \
-d '{"refreshToken": "<refreshToken>"}'Forgot password
curl -s -X POST http://localhost:4000/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'With EMAIL_PROVIDER=MOCK, reset links appear in email logs — not real inbox delivery.
Reset password
curl -s -X POST http://localhost:4000/auth/reset-password \
-H "Content-Type: application/json" \
-d '{
"token": "reset-token-from-email-log",
"newPassword": "NewPass123"
}'Email verification
curl -s -X POST http://localhost:4000/auth/verify-email \
-H "Content-Type: application/json" \
-d '{"token": "verification-token-from-email-log"}'Resend:
curl -s -X POST http://localhost:4000/auth/resend-verification \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'Token security
- Use long random
JWT_ACCESS_SECRETandJWT_REFRESH_SECRETin production - Rotate secrets if compromised (invalidates existing tokens)
- Never log access or refresh tokens
- Never commit tokens to git or support tickets
- Frontend stores tokens in
localStoragetoday — acceptable for starter kit; consider hardened storage for high-security products
Future improvement: HttpOnly cookies
A future module may move refresh tokens to HttpOnly, Secure, SameSite cookies to reduce XSS exposure. Current implementation uses JSON body + localStorage for simplicity and SPA compatibility.
Protected route redirect (from)
When an unauthenticated user opens a protected page (e.g. /dashboard/billing):
GuestGuardredirects to/login?from=/dashboard/billing- After successful login, the app navigates to the sanitized
frompath - Invalid or external URLs fall back to
/dashboard
Implementation: sanitizeRedirectPath() — blocks open redirects and login loops.
Logout behavior
Logout clears local session and uses hard navigation to / so:
- No stale
?from=query from the previous page - Workspace and auth context reset cleanly
Suspended accounts
When User.status = SUSPENDED:
| Layer | Behavior |
|---|---|
| Login | 403 — friendly message in UI (not raw USER_SUSPENDED code) |
| Refresh | Blocked for non-active users |
| JWT | Rejects non-ACTIVE users |
Login page message:
Your account is suspended. Please contact your workspace administrator or support team.
Contact Support opens a mailto link (NEXT_PUBLIC_SUPPORT_EMAIL, default support@launchkitlabs.com).
Normal invalid-password errors do not show the support CTA.
Workspace context on login
Login and GET /auth/me return:
activeWorkspace— first ACTIVE workspace onlyhasActiveWorkspace/workspaceUnavailableReasonwhen user has only suspended memberships
See Workspaces & Team and UI/UX Polish.
Related
- Environment Variables — JWT env vars
- Demo Mode — blocks password change for protected demo users
- API Documentation — interactive auth endpoints in Swagger
- Admin User Management — suspend/activate users