Auth, Session & Storage — Cheat Sheet

Lesson 18 · FE Lead Interview · 2026-06-12

The triangle (pick two)

Memorize

XSS-resistant (HttpOnly) ← can't also be JS-accessible
CSRF-resistant (not a cookie) ← can't also be HttpOnly
JS-accessible (needed for Bearer headers) ← readable by XSS

Win: memory + HttpOnly cookie (two-token split)

Storage comparison

WhereXSSCSRFPersists
Memory (JS var)Low*NoneNo
HttpOnly cookieNoneSameSiteYes
localStorageHighNoneYes
sessionStorageHighNoneTab only
IndexedDBHighNoneYes

*Memory: no persist — attacker can't exfiltrate a string after page close

JWT anatomy — header.payload.signature

Structure

Header{"alg":"RS256","typ":"JWT"} → Base64URL
Payload — claims (sub, roles, exp, iat) → Base64URL — NOT encrypted
SignatureRS256(header + "." + payload, privateKey) → Base64URL

StepCreate (auth server)Verify (API server)
1Base64URL(header) → HSplit on "." → H, P, S
2Base64URL(payload) → PRecompute: RS256(H+"."+P, publicKey)
3S = RS256(H+"."+P, privateKey)Timing-safe compare S === expected
4JWT = H + "." + P + "." + SDecode P → check exp, iss, aud

JWT vs Opaque

JWTOpaque
Verify costCryptoDB lookup
RevocableNot before expiryInstant
StatelessYesNeeds store
Use forAccess (15 min)Refresh (7–30d)

Refresh token rotation flow

  1. Login → short JWT (memory) + opaque refresh (HttpOnly cookie, Path=/auth/refresh)
  2. API call uses Authorization: Bearer <jwt>
  3. JWT expires → call POST /auth/refresh
  4. Server: validate → issue NEW access + NEW refresh → invalidate old refresh
  5. Reuse detection: old token presented again → revoke whole family → logout

Cookie attributes

AttributeProtects againstNotes
HttpOnlyXSS token theftJS-blind; essential for auth cookies
SecureMITMHTTPS only; required with SameSite=None
SameSite=LaxCSRFDefault Chrome 80+; sent on top-level GET nav
SameSite=StrictCSRF (max)Breaks email-link logins and SSO redirects
SameSite=NoneRequires Secure; needs separate CSRF defense
Path=/auth/refreshCSRF scopeLimits cookie to refresh endpoint only
Domain=.example.comCross-subdomain; any subdomain can read it

Gold-standard cookie header

Set-Cookie: refresh=<token>;
  HttpOnly;
  Secure;
  SameSite=Lax;
  Path=/auth/refresh;
  Domain=.example.com;
  Max-Age=2592000

Singleton refresh gate

let refreshPromise = null;

async function getAccessToken() {
  if (!isExpired(token)) return token;
  if (!refreshPromise) {
    refreshPromise = callRefresh()
      .then(t => {
        token = t;
        refreshPromise = null;
        return t;
      });
  }
  return refreshPromise;
}

CSRF defenses (when SameSite=None)

Don't fail the interview

JWT is not encrypted

JSON.parse(atob(token.split('.')[1])) reads the full payload in plain text. Signature = tamper-proof, not secret. Need secret payload? Use JWE.

RS256 > HS256: RS256 = private key signs, public key verifies — API servers never hold the secret. HS256 = same secret both sides — any compromised service can forge tokens.

Refresh race has 3 tiers

Intra-tab (concurrent fetches): singleton promise — one module-level refreshPromise, all callers share it.

Cross-tab (two tabs expire together): singleton fails — each tab has its own heap. Fix: Web Locks (turn-taking) + BroadcastChannel (share new token). Locks alone still fails — Tab B's in-memory token stays stale until the broadcast updates it.

Cross-browser / device: not a race. Server issues one refresh token row per session; each device rotates independently.

localStorage XSS blast radius

Token exfiltrated → replayed from any machine → dwell time = token expiry (hours/days). Memory: dies with tab.

SameSite=Strict — what actually breaks

Email verification links are fine — well-designed tokens are self-contained; server finds the user via the token, no session needed. Works from any device.

What actually breaks: OAuth/SSO redirects (return hop from Google/IdP is cross-site → cookie dropped → login state lost) and stateful resume links (cart, multi-step flows that require an existing session).

Fix: use Lax — allows cookie on cross-site top-level GET navigations.

Wildcard CORS + refresh cookie (L17 tie)

ACAO: * forbidden with HttpOnly credentials. Refresh endpoint needs explicit origin allowlist.

CORS headers missing on error responses

Also applies here: 401 from auth endpoint without CORS headers = CORS error masks real 401.