Subscribe to the Non-Human & AI Identity Journal

What breaks when tokens are stored in localStorage?

localStorage makes tokens readable by JavaScript, so any XSS flaw, malicious browser extension, or injected script can steal them. That turns a frontend vulnerability into a session compromise and extends the attack surface beyond the user interface.

Why This Matters for Security Teams

Storing tokens in localStorage turns a session credential into browser-readable data, so the security boundary shifts from the server to whatever JavaScript executes in the page. That is why an XSS flaw, compromised dependency, or hostile extension can become direct account takeover rather than just a UI defect. NIST’s NIST Cybersecurity Framework 2.0 emphasizes reducing exposure and limiting blast radius, but localStorage works against both goals when tokens are long-lived and broadly accessible.

The practical risk is not theoretical. Token theft patterns are consistent with broader secret-sprawl failures described in NHIMG research, including the Guide to the Secret Sprawl Challenge and the Salesloft OAuth token breach, where stolen tokens enabled access without needing a password. In practice, many security teams encounter token abuse only after a frontend script, browser session, or third-party widget has already been trusted too much.

How It Works in Practice

localStorage is convenient because it persists across refreshes, but that persistence is exactly what makes it brittle for authentication data. Anything running in the origin can read it, which includes injected script, malicious browser extensions, compromised analytics tags, and browser-based supply chain dependencies. Once a token is available to JavaScript, the application no longer controls where it goes or how long an attacker can reuse it.

Safer patterns usually move token handling away from script-accessible storage. Common practice is to keep refresh tokens in HTTP-only, Secure cookies, use short-lived access tokens, and rotate or revoke credentials aggressively. For high-trust applications, this reduces the chance that a single XSS flaw becomes a full session compromise. The same logic appears in NHIMG reporting on the Dropbox Sign breach and JetBrains GitHub plugin token exposure, where exposed credentials extended the impact well beyond the initial entry point.

  • Use localStorage only for non-sensitive state, not bearer tokens or long-lived secrets.
  • Prefer server-side session handling or HTTP-only cookies for browser authentication.
  • Shorten token lifetime so stolen credentials expire quickly.
  • Bind session behaviour to context where possible, such as device posture or reauthentication on risk.
  • Audit third-party scripts, browser extensions, and injected content as if they were part of the attack surface.

For teams managing broader secret exposure, the pattern fits the same control logic described in NHIMG’s Guide to the Secret Sprawl Challenge: if a secret is readable by untrusted client-side code, assume it will eventually be copied. These controls tend to break down in single-page applications with heavy third-party scripting, because the browser origin becomes a crowded trust boundary and token theft can happen without any server-side alert.

Common Variations and Edge Cases

Tighter session storage often increases implementation complexity, so teams have to balance usability against exposure. That tradeoff matters most in mobile web apps, embedded apps, and legacy SPAs where cookie-based flows, CSRF protections, and refresh-token rotation are harder to retrofit cleanly.

There is no universal standard for every frontend architecture yet, but current guidance suggests treating browser storage choices as a risk decision, not a developer preference. If an application must persist something client-side, the safer approach is to store only low-risk state and keep credentials out of JavaScript reach. This is especially important when the browser environment includes extensions, federated login flows, or unreviewed third-party widgets, because those conditions make localStorage a high-value target rather than a harmless cache.

Another edge case is token scope. A narrowly scoped, short-lived token is still exposed if placed in localStorage, but the blast radius is lower than with broad API access. Security teams should still treat that as compensating control, not as a justification for client-side token storage. The operational lesson from incidents such as the Salesloft OAuth token breach is that reusable credentials in the wrong place convert one weakness into many downstream compromises.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP Non-Human Identity Top 10 address the attack and risk surface, while NIST CSF 2.0 and NIST AI RMF set the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
OWASP Non-Human Identity Top 10 NHI-03 Covers secret exposure and token lifecycle weaknesses in client-side storage.
NIST CSF 2.0 PR.AC-4 Access control depends on limiting who or what can use a valid token.
NIST AI RMF AI governance principles apply when browser scripts and extensions create unpredictable access paths.

Reduce token blast radius with least privilege, rotation, and session reauthentication.