Where should a single-page app store an authentication token, and what are the security trade-offs between localStorage and an httpOnly cookie?
technical-conceptual · Mid level · software-engineering
What the interviewer is really asking
Assesses understanding of token-storage threat models — XSS exposure of localStorage vs. CSRF exposure of cookies — and how to mitigate each.
What to say
- Lay out the two threats: anything readable by JavaScript (localStorage, non-httpOnly cookies) is exposed if you have any XSS, while cookies are sent automatically so they invite CSRF — different attacks, different mitigations.
- Recommend an httpOnly, Secure, SameSite cookie for the token so JavaScript can't read it (closing the XSS-exfiltration path), and pair it with SameSite=Lax/Strict plus anti-CSRF measures to cover the cookie's automatic-send risk.
- Acknowledge the nuance: httpOnly cookies reduce but don't eliminate risk (an attacker with XSS can still ride the session in-page), so token storage is one layer on top of preventing XSS in the first place and using short-lived tokens.
What to avoid
- Saying 'just use localStorage, it's easy' without acknowledging that any XSS reads the token straight out of it.
- Claiming httpOnly cookies are 'completely safe' — they block JS read/exfiltration but not CSRF or an attacker acting within an already-compromised page.
- Confusing the two attack classes — proposing CSRF tokens to fix an XSS exposure, or SameSite to fix a localStorage read.
Example answers
Strong: It's a trade between two attacks. localStorage is readable by any script, so a single XSS leaks the token. Cookies aren't readable if they're httpOnly, but they're sent automatically, which opens CSRF. I'd store the token in an httpOnly, Secure, SameSite cookie so JS can't exfiltrate it, then add SameSite and anti-CSRF protection for the auto-send risk. None of that excuses skipping XSS prevention or short token lifetimes.
Weak: I'd put the JWT in localStorage because it's simple to read on each request and attach to the Authorization header. As long as we're on HTTPS it's encrypted in transit, so it should be fine.