Severity: High
Two related auth-flow problems in sisyphus-web.
1. Both access AND refresh tokens in localStorage
sisyphus-web/src/auth/useAuth.ts:10, 65:
const STORAGE_KEY = `nyxid_tokens_${NYXID_CLIENT_ID}`;
// ...
localStorage.setItem(STORAGE_KEY, JSON.stringify({ accessToken, refreshToken }));
Long-lived refresh tokens in localStorage are XSS-exfiltrable. Any script on the origin (and there are many third-party deps in this app — CodeMirror, ReactFlow, three.js, force-graph) can read them. A single XSS gives the attacker permanent persistence.
2. Token refresh forces window.location.reload()
sisyphus-web/src/auth/useAuth.ts:65-67 — after silent refresh, the new tokens are written to localStorage, then the page is reloaded. This:
- Kills any in-flight SSE / WebSocket streams (research loop, runner events).
- Aborts uploads in progress.
- Loses unsaved edits in the workflow / schema YAML editors.
- Makes timing-sensitive UX (token-rotate-during-active-stream) very visible.
Remediation
Token storage:
- Move the refresh token to an httpOnly + SameSite=Lax + Secure cookie (server-set).
- Keep the access token in memory only (the NyxID SDK already supports an in-memory storage; switch to it).
- If httpOnly cookie isn't an option, accept the XSS risk for the access token only (short TTL) and enforce strict CSP (
script-src 'self').
Refresh flow:
- Dispatch the new token through React context (the NyxID SDK already exposes a token store) and let
apiFetch wrappers re-read it on next call.
- Keep all open streams alive across refresh.
- Drop
window.location.reload() from the path entirely.
Severity: High
Two related auth-flow problems in
sisyphus-web.1. Both access AND refresh tokens in
localStoragesisyphus-web/src/auth/useAuth.ts:10, 65:Long-lived refresh tokens in
localStorageare XSS-exfiltrable. Any script on the origin (and there are many third-party deps in this app — CodeMirror, ReactFlow, three.js, force-graph) can read them. A single XSS gives the attacker permanent persistence.2. Token refresh forces
window.location.reload()sisyphus-web/src/auth/useAuth.ts:65-67— after silent refresh, the new tokens are written tolocalStorage, then the page is reloaded. This:Remediation
Token storage:
script-src 'self').Refresh flow:
apiFetchwrappers re-read it on next call.window.location.reload()from the path entirely.