Skip to main content

Authentication in SPAs: Why localStorage Is Usually a Bad Idea

How to think about storing credentials in the browser without turning implementation convenience into silent product risk.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

Track

Senior Frontend Interview Trail

Step 14 / 15

The problem

Some SPA auth implementations follow this path:

  1. log in
  2. receive a token
  3. save the token in localStorage
  4. send the token on every request

It works.

And because it works easily, it becomes the default in many projects.

The problem is that easy access for your code also means easy access for any script that manages to run on that page.

Mental model

Think about it this way:

the problem with localStorage is not storing data in the browser. The problem is storing sensitive credentials in a place accessible to page JavaScript.

If there is XSS or a compromised script, the attacker does not need to guess much.

They can simply read the credential.

That increases the blast radius a lot.

Breaking the problem down

localStorage is easy for you and for the attacker

Your app can read it easily.

An injected script can too.

That is the central point.

It is not a matter of taste.

It is a matter of exposure surface.

HttpOnly cookies change the design

When the credential lives in a cookie with HttpOnly, page JavaScript cannot read the value directly.

That does not eliminate all problems.

But it removes an important class of trivial token access.

Of course, other concerns still exist:

  • SameSite
  • Secure
  • CSRF where relevant
  • backend authorization

But the exposure design improves.

Not every SPA can follow the same flow in the same way

Architecture matters.

If there is your own backend and session through cookies, the default usually gets better with an HttpOnly cookie.

If the context is different, the conversation changes too.

The mistake is turning implementation convenience into the main security criterion.

”Usually a bad idea” is not the same as “never”

This point matters.

If the requirement is different, a team may accept the trade-off consciously.

But to do that, it needs to answer:

  • why is this risk worth it?
  • what mitigations exist?
  • which alternative was rejected and why?

Without that conversation, localStorage becomes just a shortcut.

Simple example

Imagine a SPA with a JWT stored in localStorage.

Now imagine an XSS issue somewhere in the application or in a loaded library.

The malicious script can:

  • read the token
  • send it out
  • reuse it while it stays valid

If the design uses an HttpOnly cookie, that direct access to the value no longer happens the same way.

That does not make the application magical.

It just reduces one very relevant surface.

Common mistakes

  • Defending localStorage only because it is easy to program.
  • Treating “stateless” like it solves security by itself.
  • Assuming an HttpOnly cookie removes every remaining concern.
  • Discussing storage without talking about XSS and exposure.
  • Saying “never use it” without explaining the risk mechanism.

How a senior thinks about it

People with more experience try to leave dogma behind and go back to the right question:

Who can read this credential in this design? If a script is compromised, what is the blast radius?

That makes the conversation more honest.

Because the debate stops being a framework war and becomes risk modeling again.

What the interviewer wants to see

In interviews, this topic measures applied security maturity.

The evaluator wants to see whether you:

  • understand the exposure problem to page JavaScript
  • compare alternatives with trade-offs
  • avoid empty absolutism
  • know how to defend a safer default

A strong answer often sounds like this:

I would avoid localStorage as the default for auth credentials in a SPA because any script running on the page can read that value. When the design allows it, I prefer an HttpOnly cookie to reduce that exposure. It is not that localStorage is magically forbidden forever, but it is usually a worse default from a risk perspective.

localStorage is not bad because it persists. It is bad because it exposes the credential to the page JavaScript environment.

Implementation convenience should rarely be the only thing deciding where your credential lives.

Quick summary

What to keep in your head

Practice checklist

Use this when you answer

You finished this article

Part of the track: Senior Frontend Interview Trail (14/15)

Next article Thinking About Security in SPA, SSR, and APIs Previous article Justifying Trade-Offs to PM, Design, and Leadership

Keep exploring

Related articles