Skip to content

[Bug] Frontend tokens stored in localStorage; refresh forces full-page reload #39

@chronoai-shining

Description

@chronoai-shining

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.

Metadata

Metadata

Labels

bugSomething isn't workingsecuritySecurity & trust

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions