Skip to content

test(auth): prove two tabs refreshing at once stay signed in (#700) - #896

Merged
alex-dembele merged 4 commits into
masterfrom
fix/700-concurrent-refresh-two-tabs
Oct 7, 2026
Merged

alex-dembele merged 4 commits into
masterfrom
fix/700-concurrent-refresh-two-tabs

Conversation

@alex-dembele

Copy link
Copy Markdown
Member

Closes #700

What

The fix itself already shipped. D-048 was implemented in #777, so a refresh replayed inside the 10-second grace window gets the same successor as the request it raced, instead of REFRESH_REUSE_DETECTED. This PR proves that on the running product and adds the tests the issue asked for. It changes no auth code; the only production line touched is a comment in handler.go that still described a concurrent race as reuse.

  • backend/internal/handler/auth/refresh_concurrency_e2e_test.go: a burst of 6 refreshes on one cookie must all answer 200, carry the same or_refresh and clear nothing. A token replayed after the window must still get 401 REFRESH_REUSE_DETECTED, clear the three cookies and kill the family.
  • frontend/e2e/session-tabs.spec.ts: one browser context, two tabs. The first test drops or_access and reloads both tabs together. The second fires /auth/refresh from both tabs at the same instant, as in the issue's reproduction.
  • docs/700_CONCURRENT_REFRESH.md: the spec and the results.

Verified (throwaway Postgres 16 + Redis 7, master cefe453d)

Check master before #777 (9dd1c3ee)
API, 2 concurrent refreshes, 3 runs 3/3: both 200, one or_refresh, jar intact, /auth/me 200, next refresh 200 one 200 and one 401 REFRESH_REUSE_DETECTED; the jar ends empty
API, 8 concurrent refreshes 8 × 200, one or_refresh
Original token replayed 11 s later 401 REFRESH_REUSE_DETECTED, 3 cookies cleared; the legitimate successor then gets 401
refresh_tokens per family exactly 1 live token, no fork
Playwright session-tabs.spec.ts --repeat-each 3 6 passed 6 failed
go test ./internal/handler/auth -run TestRefreshHandler -race 3 passed burst test fails, reuse test passes
go test ./internal/auth -run TestRefresh_ -race (existing, unchanged) 12 passed
go test ./... 80 packages ok

Depends on

The Playwright run needs #893 (PR #894). The sign-in screen crashes on master since PR #880, so session-tabs.spec.ts cannot sign in until that merges. The numbers above were taken with that one-line fix applied locally.

Not done

…nt burst (#700)

A burst of refreshes on one cookie must answer 200 to every request, hand
each the same refresh cookie and never clear the session another tab is
setting. A rotated token replayed after the grace window must still answer
REFRESH_REUSE_DETECTED, clear the cookies and take the family with it.

The burst test fails on the code before #777 and passes on master. The
handler comment still described a lost concurrent race as reuse; it no
longer is since D-048.

Signed-off-by: alex-dembele <alexandredembele16@gmail.com>
…700)

One browser context, two tabs. The first test drops the access cookie and
reloads both tabs together; the second fires /auth/refresh from both at the
same instant, as in the issue's reproduction. Both tabs must keep a working
or_access / or_refresh pair and stay off /login.

Fails 6/6 against the backend before #777, passes 6/6 on master.

Signed-off-by: alex-dembele <alexandredembele16@gmail.com>
Signed-off-by: alex-dembele <alexandredembele16@gmail.com>
Signed-off-by: alex-dembele <alexandredembele16@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two tabs refreshing at once sign the user out of every tab — a lost rotation race clears all session cookies

1 participant