Authentication routes for email/password and OAuth (GitHub, Google).
Endpoints (INF-005: all under /api/v1/auth/)
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Email/password registration |
| POST | /api/v1/auth/login |
Email/password sign-in |
| POST | /api/v1/auth/logout |
Token revocation + cookie clear |
| POST | /api/v1/auth/refresh |
Refresh session (extend cookie TTL) |
| GET | /api/v1/auth/me |
Return current user from cookie |
| GET | /api/v1/auth/export |
Export user-owned account data (SEC-003) |
| DELETE | /api/v1/auth/account |
Delete account + owned data (SEC-003) |
| GET | /api/v1/auth/verify |
Verify email via token (SEC-001) |
| POST | /api/v1/auth/resend-verification |
Resend verification email (SEC-001) |
| POST | /api/v1/auth/forgot-password |
Request a password reset token |
| POST | /api/v1/auth/reset-password |
Reset password using a valid token |
| GET | /api/v1/auth/github/callback |
GitHub OAuth token exchange |
| GET | /api/v1/auth/google/callback |
Google OAuth token exchange |
Security measures
- Passwords hashed with scrypt (64-byte key, 16-byte random salt)
- JWT stored in HttpOnly; Secure; SameSite=Strict cookie — never in localStorage
- A companion
token_expcookie (Non-HttpOnly) exposes only the exp timestamp so the frontend can proactively warn before expiry without ever touching the JWT - JWT signed with HS256, 8-hour expiry
- Rate limiting: separate per-endpoint buckets (login: 10, forgot/reset: 5 per IP per 15 min)
- Revoked tokens kept in an in-memory Map (production: use Redis — see ENH-002)
- Password reset tokens persisted in DB table
password_reset_tokens(migration 003) - Input validation and sanitisation on every endpoint
- OAuth state parameter validated on the frontend to prevent CSRF
- CSRF double-submit cookie protection on all mutating endpoints (via appSetup.js)
- No sensitive data (passwords, raw OAuth tokens, JWT strings) returned to client
- Source:
Members
(static, constant) EXP_COOKIE
Expiry hint cookie — Non-HttpOnly so the frontend can read the exp timestamp.
- Source:
(static, constant) JWT_TTL_SEC
JWT TTL in seconds (8 hours). Must match signJwt default.
- Source:
(static, constant) requireAuth
Backward-compatible alias. All files that do
import { requireAuth } from "./routes/auth.js"
continue to work — requireUser is the same JWT cookie → bearer → query
middleware that requireAuth used to be.
- Source:
Methods
(static) setAuthCookie(res, token, expSec)
Set the HttpOnly auth cookie + a readable expiry hint cookie on a response. Called after every successful authentication (login, OAuth, refresh, workspace switch).
Parameters:
| Name | Type | Description |
|---|---|---|
res |
Object | Express response object. |
token |
string | The signed JWT string. |
expSec |
number | Unix timestamp of token expiry (seconds). |
- Source:
(inner) checkRateLimit(bucket, ip) → {Object}
Check rate limit for a specific bucket.
Parameters:
| Name | Type | Description |
|---|---|---|
bucket |
string | — key in rateBuckets (e.g. "login", "forgotPassword") |
ip |
string |
- Source:
Returns:
- Type
- Object
(inner) clearAuthCookies(res)
Clear both auth cookies, effectively logging the user out client-side.
Parameters:
| Name | Type | Description |
|---|---|---|
res |
Object | Express response object. |
- Source:
(inner) ensureUserWorkspace(user)
Ensure the user belongs to at least one workspace, creating a personal workspace if needed. Called from every auth path (login, OAuth) so no user can end up without a workspace.
Parameters:
| Name | Type | Description |
|---|---|---|
user |
Object | — User row from the database. |
- Source:
(inner) isOAuthOnlyUser(user) → {boolean}
Check whether a user registered via OAuth only (no password set).
Parameters:
| Name | Type | Description |
|---|---|---|
user |
Object |
- Source:
Returns:
- Type
- boolean
(inner) validatePasswordStrength(password) → {string|null}
Validate password strength.
Parameters:
| Name | Type | Description |
|---|---|---|
password |
string |
- Source:
Returns:
Error message, or null if valid.
- Type
- string | null
(async, inner) verifyAccountPassword(user, password) → {Promise.<boolean>}
Verify the authenticated user's password for sensitive account actions. For OAuth-only users (no passwordHash), password verification is skipped — the OAuth session itself serves as proof of identity.
Parameters:
| Name | Type | Description |
|---|---|---|
user |
Object | |
password |
string |
- Source:
Returns:
- Type
- Promise.<boolean>