Sessions & Tokens
How Leadmetrics issues, validates, and refreshes authentication tokens across web portals and the mobile app.
Related: Auth Overview | API Auth Endpoints
Token Types
| Token | Lifetime | Format | Storage | Purpose |
|---|---|---|---|---|
| Access token | 15 minutes | Signed JWT (HS256) | httpOnly cookie per portal | Authorize API requests |
| Refresh token | 7 days | Opaque (random string) | httpOnly cookie per portal | Obtain a new access token |
Access tokens are verified locally by each portal’s middleware (no database lookup). Refresh tokens are stored in the database and can be revoked.
JWT Payload
{
sub: string; // user ID (ULID)
role: string; // 'admin' | 'member' | 'reviewer' | 'super_admin'
appAccess: string[]; // apps the user is allowed to access
tenantId?: string; // absent for super_admin
name?: string;
email?: string;
iat: number; // issued at (Unix seconds)
exp: number; // iat + 900 (15 min)
}Signing algorithm: HS256 (HMAC-SHA256) with JWT_SECRET. The same secret is used by the Fastify API (to sign) and all portal middleware (to verify).
Session Lifecycle
User logs in
│
▼
loginAction() → POST /auth/v1/login
│
▼
Access token (15 min) + Refresh token (7 days) set as httpOnly cookies
│
▼ (token expires)
Next.js middleware intercepts 401
│
▼
POST /auth/v1/refresh → new access token
│
├── Success → new cookie set, request continues
└── Failure → redirect to /loginSilent Refresh
When a request arrives with an expired access token, the Next.js middleware (createJwtAuthMiddleware from @leadmetrics/middleware) transparently refreshes it:
- Reads the access token cookie — JWT verify fails (expired)
- Reads the refresh token cookie
- Calls
POST /auth/v1/refreshwith the refresh token - On success: sets a new access token cookie, forwards the request
- On failure (refresh token missing, expired, or revoked): redirects to
/login
The user never sees an error — the refresh is completely transparent.
Middleware Configuration per Portal
Each portal passes a defaultPath option to createJwtAuthMiddleware. This controls the callbackUrl used when an unauthenticated user visits / — after login they land on that path. Without it, the factory defaults to /dashboard, which only exists in the dashboard app.
| Portal | defaultPath |
|---|---|
dashboard (:3000) | (omit — factory default /dashboard is correct) |
manage (:3001) | /dashboards/overview |
DM (:3002) | /overview |
Each portal’s middleware.ts must keep its export const config as an inline literal — Next.js cannot statically analyse imported values for the matcher.
Cookie Security
All auth cookies are set with:
HttpOnly — not accessible to JavaScript
Secure — HTTPS only (production)
SameSite — Lax (allows top-level navigation)
Path — /Logout
Calling logoutAction() (which calls POST /auth/v1/logout):
- API deletes the refresh token record from the database
clearAuthCookies()deletes both the access and refresh token cookies from the browser- User is redirected to
/login
After logout, the access token cookie is gone. Even if a previously issued access token were somehow retained, it expires within 15 minutes and cannot be refreshed (the refresh token is revoked).
Password Reset
Password reset uses a one-time token delivered by email:
POST /auth/v1/forgot-password { email }
→ API sends reset email with token (1-hour expiry)
→ User clicks link → /reset-password?token=<token>
POST /auth/v1/reset-password { token, newPassword }
→ API validates token, hashes new password, deletes tokenTokens are stored in the verification table. Duplicate requests for the same email are suppressed for 20 minutes. Rate limit: 5 requests per 15 minutes.
Mobile Sessions
Mobile uses the same HS256 JWT system. Access tokens are stored in secure device storage (not cookies). The mobile app calls POST /auth/v1/refresh directly when the access token expires.
See Mobile Patterns for the mobile-specific refresh implementation.
Features Planned but Not Yet Implemented
- RS256 asymmetric signing with JWKS endpoint
- Refresh token rotation (currently refresh tokens are not rotated on use)
- Token revocation beyond logout
- Biometric device tokens
- Two-factor authentication
- Session list / remote revoke endpoints
- Email verification on registration
- HIBP breach-check on password set