SECTION 0 — WHAT YOU’RE REALLY BUILDING
Auth is not “login.”
Auth is a long-lived trust system across devices, networks, time, and failure.
If you’re senior fullstack, your job is to make sure:
-
users can always regain access safely
-
sessions don’t get stolen
-
refresh doesn’t become a reliability incident
-
UI states don’t lie
-
contracts remain backwards compatible
SECTION 1 — REQUIREMENTS
Functional
-
Login via email+password (or SSO).
-
Keep users signed in across browser sessions.
-
Support mobile apps (native) + web.
-
Logout from current device, optionally “logout all devices.”
-
Token/session refresh without user friction.
Non-functional
-
Security: mitigate XSS/CSRF/session fixation/token theft.
-
Reliability: refresh storms must not take down auth.
-
Correctness: predictable state transitions; no phantom “logged-in.”
-
Compatibility: versioned contracts across web/mobile releases.
SECTION 2 — CORE INVARIANTS (THE RULES)
-
No privilege without a valid server-verified credential.
-
Refresh tokens are never exposed to XSS on web (prefer HttpOnly cookie).
-
Access tokens are short-lived (blast radius reduction).
-
Rotation is enforced (replay detection).
-
Logout revokes server-side capability, not “just delete local storage.”
SECTION 3 — RECOMMENDED MODEL: SESSION + ROTATING REFRESH
Web (browser)
-
Refresh token: HttpOnly, Secure cookie, SameSite=Lax (or Strict when possible)
-
Access token: returned in response body OR set in memory only (not localStorage)
Mobile
-
Refresh token stored in secure storage (Keychain/Keystore)
-
Access token in memory
Why
-
Web is XSS-prone → don’t expose refresh token to JS.
-
Mobile can protect secrets better.
SECTION 4 — API CONTRACT (STABLE, EXPLICIT)
Login
-
POST /auth/login-
body:
{ email, password } -
returns:
{ accessToken, accessTokenExpiresAt, user } -
sets refresh cookie (web) OR returns refresh token (mobile)
-
Refresh
-
POST /auth/refresh-
web: refresh token comes from cookie
-
mobile: body
{ refreshToken } -
returns:
{ accessToken, accessTokenExpiresAt } -
rotates refresh token (web: set-cookie; mobile: return new refresh)
-
Logout
-
POST /auth/logout(revoke current session) -
POST /auth/logout-all(revoke all sessions)
Session introspection (optional)
GET /auth/session→{ authenticated: boolean, user?, sessionId? }
Contract rules:
-
Refresh returns typed errors:
INVALID_REFRESH,REVOKED,EXPIRED,REPLAY_DETECTED. -
Never return “200 OK but you’re actually logged out.”
SECTION 5 — DATA MODEL (SERVER SOURCE OF TRUTH)
Tables (conceptual):
-
users -
sessions-
id -
user_id -
created_at,last_seen_at -
revoked_at -
device_fingerprint(optional)
-
-
refresh_tokens-
session_id -
token_hash -
rotated_from_hash(optional) -
expires_at -
revoked_at
-
Senior rule:
Store hashes of refresh tokens, not plaintext.
Rotation logic:
-
when refresh happens, mark old token as used/revoked
-
issue new refresh token hash
-
if an old token is used again → replay detected → revoke the session (or all sessions depending on policy)
SECTION 6 — FRONTEND STATE MACHINE (THE TRUTH MODEL)
UNKNOWN (boot)
-> AUTHENTICATED
-> UNAUTHENTICATED
AUTHENTICATED
-> REFRESHING (token expiring)
-> AUTHENTICATED
-> UNAUTHENTICATED (refresh fails)
UNAUTHENTICATED
-> LOGGING_IN
-> AUTHENTICATED
-> ERROR
Senior UX rules:
-
Never show “logged in” until you have a validated session.
-
Handle “access token expired” as a normal path (refresh).
-
Prevent refresh loops (max attempts + backoff).
SECTION 7 — FAILURE MODES
Refresh storms
Cause:
- clients refresh at the same time (token expiry aligned)
Mitigations:
-
add random jitter to token expiry / refresh time
-
single-flight refresh in UI (dedupe concurrent refresh requests)
-
rate limit refresh per session/user
CSRF
Risk:
- cookie-based refresh/login can be CSRF’d
Mitigations:
-
SameSite cookies + CSRF token for unsafe methods
-
require
Origin/Refererchecks -
avoid GET side effects
XSS
Mitigation:
-
keep refresh token out of JS (HttpOnly)
-
CSP + output encoding + dependency hygiene
Session fixation
Mitigation:
-
rotate session identifiers on login
-
invalidate anonymous session upon privilege elevation
SECTION 8 — OBSERVABILITY
Metrics:
-
login success/failure by reason
-
refresh success/failure by reason
-
replay-detected count
-
logout-all usage
Logs/traces:
- include
sessionId,userId,clientType(web/mobile),requestId
Alerts:
-
spike in refresh failures
-
spike in replay detected
-
spike in 401s on a critical endpoint
SECTION 9 — ROLLOUT + COMPATIBILITY
-
version refresh responses (add fields, don’t break)
-
keep old refresh behavior for one mobile release cycle
-
feature-flag rotation strictness if migrating from non-rotating refresh tokens
SECTION 10 — EXERCISES
-
Write your auth invariants (max 8). What must never happen?
-
Design your refresh rotation algorithm including replay detection.
-
Create a “refresh storm” test plan (load + jitter).
-
Define your CSRF protections for cookie-based refresh.