docs(auth): record accepted-risk decision for SPA OIDC token storage (MAPPS-362) #425

Merged
nrupard merged 1 commit from docs/MAPPS-362-oidc-token-storage into main 2026-07-14 16:05:40 +02:00
Owner

What

MAPPS-362 (accepted-risk tracking, LOW severity). Records the deliberate decision about SPA OIDC token storage, and answers the browser-held-refresh-token question. Docs + one cross-reference comment; no behavior change.

Changes

  • New docs/oidc-token-storage.md: the token-storage model (access/id/refresh in sessionStorage, src/modules/oidc/storage.rs; ID token decoded unverified for display, tokens.rs:59), the risk (web-storage/WASM readable by scripts -> XSS exfil; OAuth BCP discourages a browser-held refresh token), the mitigations (short access-token TTL, OP-side refresh rotation + family-reuse detection, TLS-only delivery, PKCE, nonce validation + return_to sanitization, sessionStorage over localStorage, backend re-validation of the access token), the decision (keep the SPA public-client model, accept the risk), the deferred httpOnly-cookie BFF option with its cost, and the triggers to revisit.
  • Cross-reference the doc from the storage.rs module comment.

Decision recorded

Accept the SPA public-client model per the issue's framing (deliberate tradeoff, BFF "not mandated"). If you'd rather commit to the BFF migration instead, that's a materially larger change and would be its own issue - say so and I'll re-scope.

AC status

  • Tradeoff + mitigations documented in the auth docs.
  • Decision recorded (accept SPA model vs BFF), with rationale + revisit triggers.
  • Browser-held refresh token confirmed necessary in the current model (SPA silent renewal via use_token_refresh -> grant_type=refresh_token, offline_access requested; no backend session to hold it -> relocating it = the BFF). Documented; not moved (that is the deferred BFF).

Testing

Docs + a module-doc comment only; cargo fmt --all --check clean, no em-dash. No runtime change.

## What MAPPS-362 (accepted-risk tracking, LOW severity). Records the deliberate decision about SPA OIDC token storage, and answers the browser-held-refresh-token question. Docs + one cross-reference comment; no behavior change. ## Changes - New `docs/oidc-token-storage.md`: the token-storage model (access/id/refresh in `sessionStorage`, `src/modules/oidc/storage.rs`; ID token decoded unverified for display, `tokens.rs:59`), the risk (web-storage/WASM readable by scripts -> XSS exfil; OAuth BCP discourages a browser-held refresh token), the mitigations (short access-token TTL, OP-side refresh rotation + family-reuse detection, TLS-only delivery, PKCE, `nonce` validation + `return_to` sanitization, `sessionStorage` over `localStorage`, backend re-validation of the access token), the **decision** (keep the SPA public-client model, accept the risk), the deferred **httpOnly-cookie BFF** option with its cost, and the triggers to revisit. - Cross-reference the doc from the `storage.rs` module comment. ## Decision recorded **Accept the SPA public-client model** per the issue's framing (deliberate tradeoff, BFF "not mandated"). If you'd rather commit to the BFF migration instead, that's a materially larger change and would be its own issue - say so and I'll re-scope. ## AC status - [x] Tradeoff + mitigations documented in the auth docs. - [x] Decision recorded (accept SPA model vs BFF), with rationale + revisit triggers. - [x] Browser-held refresh token confirmed necessary in the current model (SPA silent renewal via `use_token_refresh` -> `grant_type=refresh_token`, `offline_access` requested; no backend session to hold it -> relocating it = the BFF). Documented; not moved (that is the deferred BFF). ## Testing Docs + a module-doc comment only; `cargo fmt --all --check` clean, no em-dash. No runtime change.
docs(auth): record accepted-risk decision for SPA OIDC token storage
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 4m18s
Create release / Create release from merged PR (pull_request) Has been skipped
6e7f646767
Adds docs/oidc-token-storage.md capturing the deliberate tradeoff of storing the OIDC access/id/refresh tokens in sessionStorage (readable by any script in the origin, so an XSS foothold can exfiltrate them, and the OAuth BCP discourages a browser-held refresh token), the mitigations in place (short access-token TTL, OP-side refresh rotation + family-reuse detection, TLS-only delivery, PKCE, nonce validation, return_to sanitization, sessionStorage over localStorage, backend re-validation of the access token), and the decision: keep the SPA public-client model and accept the risk, with the httpOnly-cookie BFF recorded as the deferred migration option (not mandated), plus the trigger conditions to revisit.

Confirms the MAPPS-362 refresh-token question: the browser genuinely needs the refresh token today because the SPA does its own silent renewal (use_token_refresh -> grant_type=refresh_token, offline_access requested) with no backend session to hold it, so relocating it server-side is exactly the BFF migration. Cross-references the doc from the src/modules/oidc/storage.rs module comment. Docs + comment only; no behavior change.

#MAPPS-362

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard scheduled this pull request to auto merge when all checks succeed 2026-07-14 16:03:09 +02:00
nrupard deleted branch docs/MAPPS-362-oidc-token-storage 2026-07-14 16:05:40 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!425
No description provided.