# ADR-002: Access Tokens Stored in JS Memory, Not localStorage

<!-- SCOPE: adr-002 | owner: ln-120 | generated: 2026-04-29 -->

**Status:** Accepted
**Date:** 2026-04-29

## Context

SPAs need to persist access tokens for authenticated API calls. The traditional approach is `localStorage`, but this is vulnerable to XSS attacks — any injected script can read `localStorage` and exfiltrate tokens. The platform handles sensitive client video content, so the threat model warrants stronger protection.

## Decision

Access tokens (15-minute JWT) are stored in React context / Zustand in-memory state only. They are lost on page refresh. Refresh tokens (7-day JWT) are stored in HttpOnly cookies — inaccessible to JavaScript. On page load, the app silently calls `/auth/refresh` to obtain a new access token using the cookie. This exchange is transparent to the user.

## Consequences

**Benefits:**
- XSS cannot steal the access token — it is not in any DOM-accessible storage
- Refresh tokens are protected by the browser's HttpOnly cookie isolation
- Complies with OWASP token storage guidance

**Trade-offs:**
- Page refresh requires a round-trip to `/auth/refresh` before the first authenticated API call
- `Authorization` header must be set on every request (Axios interceptor handles this)
- If the refresh endpoint is unavailable, the user must log in again

**Implementation:** `frontend/src/lib/auth.ts` — in-memory store. Axios interceptor in `frontend/src/lib/api.ts` attaches the token on every request and calls refresh on 401.

## Maintenance

**Update triggers:** Token TTL changes, new auth provider added.

<!-- END SCOPE: adr-002 -->
