# Auth state is not stored in localStorage

- **Pattern:** `ab-002208` (`saas-authentication.session-management.auth-state-not-localstorage`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002208
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002208)

## Why it matters

localStorage is synchronously readable by every JavaScript file executing on your domain — your own code, third-party analytics, CDN-hosted widgets, and any XSS payload. Auth tokens stored there are exposed to the entire JavaScript surface area of your application. CWE-922 (Insecure Storage of Sensitive Information) and CWE-312 (Cleartext Storage of Sensitive Information) both apply. OWASP A02 lists insecure credential storage as a cryptographic failure. NIST 800-63B §7.1 requires that session secrets be protected from disclosure; localStorage provides no such protection.

## Severity rationale

Medium because exploitation requires either a successful XSS attack or a malicious third-party script to be running on the page, but either condition is common.

## Remediation

Replace localStorage token storage with HttpOnly cookies set by the server. The browser sends HttpOnly cookies automatically with requests and JavaScript cannot read them at all:

```ts
// Remove this pattern entirely:
localStorage.setItem('token', jwtToken)

// Set the token server-side on the login response:
res.setHeader('Set-Cookie',
  `session=${token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=604800`
)

// For non-sensitive UI data, localStorage is still fine:
localStorage.setItem('displayName', user.name)
```

For React SPAs that need to make authenticated API calls, configure your HTTP client to include credentials (`credentials: 'include'` in fetch, `withCredentials: true` in Axios) so cookie-based auth works across your origin.

## Detection

- **ID:** `auth-state-not-localstorage`
- **Severity:** `medium`
- **What to look for:** Search the codebase for `localStorage.setItem` or `localStorage.getItem` calls that reference tokens, session data, or user credentials. Check client-side auth state management code, login components, and any client-side API clients. Note: storing minimal non-sensitive user display data (name, email for UI) in localStorage is acceptable — only auth tokens and session credentials are at risk. Count all instances found and enumerate each.
- **Pass criteria:** JWT tokens, session tokens, refresh tokens, and API keys are not stored in localStorage. Auth state is maintained via HttpOnly cookies or in-memory state only. At least 1 implementation must be confirmed.
- **Fail criteria:** Bearer tokens, JWT access tokens, refresh tokens, or API keys are stored in `localStorage` or `sessionStorage`.
- **Skip (N/A) when:** No client-side auth state management found — fully server-rendered application with no client-side auth token handling. Signal: no client-side JavaScript that accesses auth tokens.
- **Detail on fail:** `"JWT access token stored in localStorage at src/lib/auth-client.ts line 42 — accessible to any JavaScript on the page"` or `"Refresh token written to localStorage in useAuth hook"`.
- **Remediation:** localStorage is readable by any JavaScript running on your page, including third-party scripts and XSS payloads. Auth tokens stored there are trivially stolen:

  ```ts
  // Wrong: token in localStorage
  localStorage.setItem('token', jwtToken)

  // Correct: rely on HttpOnly cookies (set server-side)
  // The browser sends them automatically with requests
  // JS cannot read them at all

  // If you need client-accessible auth state, store only
  // non-sensitive info like user ID or display name:
  localStorage.setItem('userId', user.id) // Acceptable for UI
  ```

  Move token storage to HttpOnly cookies. For SPAs that need to make authenticated API calls, use cookie-based auth (the server sets the cookie, the browser sends it automatically) rather than manually attaching tokens.

## External references

- cwe CWE-922
- cwe CWE-312
- owasp A02
- nist SP-800-63B §7.1

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-002208
