You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Auth: concurrent multi-tab refresh can trip reuse-detection and log both tabs out #169
Surfaced in the #145 (PR #167) review (codex). Rotating single-use refresh tokens + reuse-detection (replay revokes the whole family) interact badly with multiple tabs/windows: two tabs of the SPA share one refresh cookie, and each can issue a refresh. If two refreshes race with the same cookie value, the second presents an already-rotated token → reuse-detection treats it as theft → revokes the whole family → both tabs are logged out.
This is pre-existing to the rotating-refresh design (pre-#145, two tabs shared the same localStorage refresh token with the same race). #145 makes it marginally more likely: the access token is memory-only, so every page load/tab-open now does an eager silent refresh (vs pre-#145 only refreshing on a 401). Single-flight refresh is per-tab, so it does not help across tabs.
Impact
Opening the app in two tabs, or reloading two tabs near-simultaneously, can spuriously log the user out of both. Annoying, not a security hole (the revocation fails safe).
Options (pick during triage)
Cross-tab coordination: a Web Lock / BroadcastChannel / localStorage-mutex so only one tab refreshes at a time and others wait for the result. Most correct; some complexity.
Server-side grace window: briefly (a few seconds) accept the immediately-previous refresh token as a valid rotation rather than a replay, so concurrent refreshes don't trip theft-detection. Simpler server change; slightly weakens reuse-detection.
Accept + document: treat multi-tab as "may require re-login."
Acceptance
Two tabs (or two near-simultaneous reloads) do not spuriously revoke the session.
Reuse-detection still revokes a genuinely replayed/stolen token.
Tests cover the concurrent-refresh race.
Out of scope for #145 (that PR moved storage, not rotation semantics).
Activity
F169 review: bound the cross-tab lock, verify shared lock name, doc r…
F169: serialise refresh across tabs (Web Locks) — no spurious multi-t…
F176: idempotent refresh grace — close the #169 tab-death residual (#180