video-accessibility/docs/reference/adrs/2026-04-29-jwt-memory-storage.md
Vadym Samoilenko a3b300b76a docs: add canonical documentation + audit cleanup
- AGENTS.md: canonical project entry point (Quick Nav, pipeline, constraints)
- docs/: complete docs tree — architecture, API spec, DB schema, infra,
  runbook, requirements, tech stack, principles, reference ADRs, guides,
  tasks backlog, testing strategy
- tests/README.md: test commands, structure, known gaps
- README.md / CLAUDE.md / DEPLOYMENT.md: updated with canonical doc links
- .archive/: backup of pre-documentation-pipeline originals
- backend/uv.lock: uv dependency lockfile
- Delete committed __pycache__ .pyc files (should have been gitignored)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 14:22:51 +01:00

1.6 KiB

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

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.