Auth and sessions
Session stores, JWT trade-offs, refresh tokens, revocation, and authorization boundaries.
Use this page to choose a login and session shape you can defend under compromise. You will practise separating identity, policy, storage and revocation instead of reaching for JWT by habit.
Read this if your last attempt…
- You said "we'll use JWT" without knowing what happens when a token is compromised
- You don't know the OAuth authorization-code flow
- You can't explain refresh tokens
- You confuse authentication with authorization
The concept
Authentication (authN) is "who are you?". Authorization (authZ) is "what can you do?". Tokens carry identity (authN); policy decisions (authZ) usually happen at the server with the token's identity as input.
Session cookies:
- Server generates a random session id, stores (session_id → user + metadata) in a session store (Redis, DB).
- Client sends it back as a cookie; server looks it up each request.
- Revocation: delete the row. Instant.
- Cost: a lookup per request. Cheap with Redis, non-trivial at very high scale.
Short JWT for speed; long refresh token stored server-side for revocation.
Session vs JWT — trade-off at a glance.
| Session cookie | Plain JWT | Access + refresh | |
|---|---|---|---|
| Revocation | Instant (delete row) | Not without denylist | Fast (delete refresh row) |
| Per-request cost | One store lookup | Signature verify only | Sig verify (access) + occasional refresh |
| Stateless? | No (server state) | Yes | Mostly (refresh is stateful) |
| Multi-service friendly | Shared session store | Verifiable anywhere | Access verifiable anywhere |
| Best for | Server-rendered web apps | Service-to-service, short-lived | Most web + mobile apps |
How interviewers grade this
- You distinguish authN from authZ.
- You pick access+refresh over plain JWT, with revocation story.
- You name the OAuth/OIDC flow (authorization code with PKCE, for SPAs, mobile and server apps alike).
- You address cookie security (HttpOnly, Secure, SameSite) and CSRF protection for state-changing requests.
- You rotate on sensitive actions (password change → invalidate all sessions).
Variants
Session cookie + server store
Classic: random id in a cookie, state in Redis.
Simple, revocable, well-understood. Requires a session store on the hot path — fine with Redis at millions of sessions, a bottleneck at very high multi-region scale.
Pros
- +Trivial revocation
- +No token-tampering risk
- +Simple mental model
Cons
- −Hot-path lookup per request
- −Multi-region session store is non-trivial
- −Cookies bring CSRF concerns — handle with SameSite + CSRF tokens
Choose this variant when
- Server-rendered web apps
- Single-region products
- Revocation is primary concern
Access + refresh token
Short JWT access token + server-stored refresh token.
The modern default for APIs + SPAs + mobile. Access JWT (5–15 min) is verified locally — fast. When it expires, client exchanges refresh token at the auth service — rotate, return new pair. Revoke = delete refresh-token row.
Pros
- +Fast per-request (no lookup on access JWT)
- +Revocable (delete refresh)
- +Works across services with just the signing key
Cons
- −Complexity in client (handle rotation)
- −Brief window between compromise and expiry where access JWT is live
Choose this variant when
- APIs, SPAs, mobile apps
- Multi-service architectures
- Most modern products
OAuth 2.0 social login (code flow + PKCE)
Delegate authentication to Google/Apple/etc.
Your app gets tokens from the IdP after the user consents. You then mint your own internal session + refresh tokens so future requests don't need the IdP. IdP tokens are for calling the IdP's APIs; your tokens are for your app.
Pros
- +No password storage
- +User trust — they know Google
- +Single-tap login on mobile
Cons
- −Dependency on IdP availability
- −Email-ownership edge cases (merge accounts?)
- −Token refresh with IdP is an ongoing concern
Choose this variant when
- Consumer apps with social login
- Enterprise SSO (OIDC)
- Anywhere you'd rather not run password UX
Worked example
Design: auth for a SaaS app with web + mobile clients.
Login flow:
- Email + password → Argon2id hash compare. Rate-limit per identity (5 attempts / 15 min). Optional 2FA.
- On success: mint access JWT (15 min, user id + org id + roles claim) + refresh token (30 days, random 256-bit, stored in DB).
- Cookies (web): HttpOnly + Secure + SameSite=Lax. Mobile: Authorization: Bearer header.
Request flow:
- Every request verifies the access JWT signature locally (no lookup).
- On 401, client exchanges refresh token for a new access + rotated refresh. Server deletes the old refresh, creates a new one — prevents token replay if stolen.
Revocation:
- User logs out → delete their refresh-token row. Access JWT still live for up to 15 min — acceptable for most products.
- Password change → delete ALL refresh tokens for the user → next access-token refresh fails → forced re-login everywhere.
- Compromised server-side: rotate signing key → all access JWTs invalidate instantly.
Multi-region:
- Signing key distributed via KMS. Any region verifies JWTs.
- Refresh-token DB is per-region with cross-region replication (for failover). Accept seconds of replication lag on refresh across regions.
Good vs bad answer
Interviewer probe
“Sessions or JWT?”
Weak answer
"JWT because it's stateless."
Strong answer
"Access + refresh. Short JWT access token (15 min) verified locally for per-request speed. Long refresh token (30 days) stored server-side; rotated on each use. Revocation = delete the refresh row; the access JWT is live for up to 15 min which is our accepted blast radius. Plain JWT without a refresh-tier gives up revocation entirely — fine for pure M2M but not for human users. Plain session cookies are also fine, just pay the lookup per request — acceptable until you're multi-region where session-store consistency gets painful."
Why it wins: Names the hybrid, quantifies the revocation window, contrasts the alternatives, and notes when each pure model wins.
When it comes up
- Login, signup, "how do users authenticate?"
- Multi-service or multi-region request flows that carry identity
- Web + mobile clients sharing one backend
- Third-party / social login or enterprise SSO
Order of reveal
- 11. Separate authN from authZ. Authentication answers who you are; authorization answers what you can do. The token carries identity; policy decisions happen at the service.
- 22. Access + refresh default. Short-lived access JWT (5–15 min) verified locally for speed, plus a long-lived refresh token stored server-side.
- 33. Revocation story. Revoke by deleting the refresh-token row; the access token’s blast radius is just its short lifetime. Rotating the signing key invalidates everything at once.
- 44. Token storage + cookie flags. For browser apps: HttpOnly + Secure + SameSite cookies (often via a backend-for-frontend), plus CSRF tokens on writes; not localStorage, because any XSS can read it. Native apps keep tokens in the platform keystore.
- 55. Federated login. For social/SSO, OpenID Connect on top of the OAuth 2.0 authorization-code flow, with PKCE for every client type.
Signature phrases
- “AuthN is who you are; authZ is what you can do.” — The distinction sloppy candidates blur and interviewers probe.
- “Short access JWT for speed, server-stored refresh for revocation.” — Names the hybrid that gets both performance and a kill switch.
- “Revoke the refresh row; the access token's blast radius is its 15-minute life.” — Shows you reason about the compromise window quantitatively.
- “HttpOnly, Secure, SameSite on auth cookies, and CSRF tokens on writes.” — Demonstrates baseline web-security hygiene.
Likely follow-ups
?“How do you revoke a JWT immediately?”Reveal
You cannot revoke a bare JWT before it expires — that is the trade-off for stateless verification. Options: (1) keep access tokens short (5–15 min) and revoke the refresh token so the chain dies at next refresh; (2) maintain a small denylist of revoked token ids checked on each request (this reintroduces a lookup); (3) rotate the signing key to invalidate every token at once for a mass-compromise event.
?“Service B receives a request with a token minted by the auth service. How does it trust it?”Reveal
B verifies the JWT signature locally using the auth service’s public key, distributed via a JWKS endpoint (or KMS). No central lookup on the hot path — that is the whole point of a signed token. B reads the identity + roles claims and applies its own authorization. Key rotation is handled by publishing the new key to JWKS with an overlap window.
?“Where should the browser store the tokens?”Reveal
For a browser app, usually in an HttpOnly, Secure, SameSite cookie — not localStorage or sessionStorage. localStorage is readable by any JavaScript on the page, so a single XSS exfiltrates the token. HttpOnly keeps it out of JS; SameSite blunts CSRF; Secure keeps it off plaintext HTTP. Cookies bring CSRF back into scope, so add a CSRF token (or double-submit cookie) on state-changing requests. Alternatives: a backend-for-frontend that keeps OAuth tokens server-side and gives the browser only a session cookie, or short-lived access tokens held in memory. Native mobile apps store tokens in the OS keystore instead.
Common mistakes
If a refresh token is stolen and not rotated on use, attacker keeps refreshing forever. Rotate on every exchange; invalidate the old. If an old one is used again, that's a theft signal — invalidate the whole chain.
Compromised JWT lives forever. Keep access JWT expiry short (5–30 min); pair with refresh for UX.
HttpOnly prevents JS theft. Secure prevents HTTP downgrade. SameSite=Lax blocks cookies on most cross-site subrequests and cross-site POSTs, which removes much of the CSRF risk, but not all of it (same-site subdomains, top-level GET navigations, older clients). Keep CSRF tokens (or a double-submit cookie) on state-changing requests, and use SameSite=Strict where the UX allows. All three flags belong on session/refresh cookies.
Vulnerable to XSS: any script on the page can read it. For browser apps, prefer an HttpOnly cookie set by your backend (often a backend-for-frontend that keeps tokens server-side), or keep short-lived access tokens in memory only. Native mobile apps have no cookie jar to lean on: store tokens in the platform keystore (iOS Keychain, Android Keystore) and send them as a bearer header. Each option trades XSS exposure against CSRF handling; say which you chose and why.
Practice drills
User reports their account was accessed from another city. What do you do?Reveal
Revoke every refresh token for that user. Access JWTs expire within 15 min naturally. Force re-authentication everywhere; step up with 2FA on next login. Review logs for the session's actions. Emit a "new device" email so the user can confirm / escalate.
Interviewer: "why not JWT everywhere, no server state?"Reveal
Because revocation is the feature you'll wish you had. A stolen JWT is live until expiry. For short-lived M2M tokens (5 min) that's acceptable. For 24-hour user tokens it's a liability. The hybrid (short JWT + server refresh) gets you most of the performance with a fast revocation knob.
Explain PKCE in one paragraph.Reveal
Proof Key for Code Exchange. The client generates a code_verifier (random string), hashes it → code_challenge, and sends the challenge with the authorization request. After getting the authorization code, the client sends it back along with the original verifier. The server checks that sha256(verifier) == challenge. This proves the code-redeemer is the same client that started the flow, so an intercepted code is useless. It was designed for clients that can't hold a client_secret (mobile apps, SPAs), and current guidance (RFC 9700, OAuth 2.1 drafts) recommends it for confidential server-side clients too.
Cheat sheet
- •AuthN = who. AuthZ = what can they do. Keep distinct.
- •Modern default: short access JWT + long server-stored refresh.
- •Rotate refresh on every use; detect replay.
- •Cookies: HttpOnly + Secure + SameSite (Lax or Strict), plus CSRF tokens on state-changing requests.
- •OAuth 2.0 / OIDC: authorization code + PKCE for every client type; OIDC ID token for login.
- •Sensitive action → invalidate all sessions.
- •Rotate signing keys periodically.
Practice this skill
No problem is tagged directly to Auth and sessions yet. These published problems still exercise the same interview category.
Read this if