Skip to content

Auth: concurrent multi-tab refresh can trip reuse-detection and log both tabs out #169

Description

@mforce

Context

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

  1. added 2 commits that reference this issue on Jul 23, 2026
    d611740
    a50b8e8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions