Conversation
added 5 commits
April 29, 2026 17:18
…aces Frontend (single-flight, all paths): - Audio-element auth recovery now goes through the shared refreshSession() promise instead of issuing its own POST /api/v1/auth/refresh. The 1.3.1 RTK Query single-flight did not cover this path, so a 401 on an <audio> fetch racing the scheduled refresh would replay the single-use cookie and revoke the token family (~25-min logoffs + random logoffs near audio playback errors). Backend (OAuth 2.0 Security BCP \u00a74.13 grace window): - PostRefresh now caches the successor (access JWT + raw refresh cookie + user identity) keyed by the old token hash on every successful rotation, with a 30s TTL. - A second presentation of the same already-rotated cookie within the grace window returns the cached successor verbatim instead of revoking the family. This absorbs scenarios the per-tab single-flight cannot cover: parallel tabs, service-worker retries, reload mid-rotation, and any other harmless duplicate. - Replays after the grace window (or with no cache entry — e.g. server restart between rotation and replay) still revoke the entire family, preserving the theft-detection signal. Tests: - New TestRefreshToken_ReplayWithinGrace_Idempotent: same cookie replayed twice returns identical access JWT + refresh cookie, family stays active. - New TestRefreshToken_ReplayAfterGrace_RevokesFamily: cookie revoked out-of-band (simulating expired cache entry) still triggers family revocation on replay.
…eceives it The refresh-token cookie was scoped to Path=/api/auth, but 1.3.0 moved the frontend to /api/v1/auth/refresh. Per RFC 6265 \u00a75.1.4 path-matching, /api/auth does NOT cover /api/v1/auth/refresh, so the browser silently stopped sending the refresh cookie on every silent-refresh attempt. The server returned 401 "no refresh token" the moment the 15-minute access JWT expired and the user was bounced to the login screen. This is the actual root cause of the ~15-minute logoffs that 1.3.1 and the single-flight/grace-window fixes were chasing — the rotation logic was never even reached. The defense-in-depth changes still stand for genuine parallel-tab and SW-race scenarios that surface once the cookie is delivered, but this one-line scope fix is what unbreaks the v1 surface.
The 1.3.0 cookie-path bug shipped because the existing tests only checked that ck.Path equalled the auth.RefreshCookiePath constant — a tautology that would have passed even if the constant were /cheese. The handler-level refresh tests used httptest.NewRequest + req.AddCookie(...), which force-attaches the cookie regardless of RFC 6265 path-matching, so they couldn't have caught a path mismatch either. Two layers added: 1. cookie_test.go: assertCookiePathCovers walks every URL the cookie must reach (refresh + session endpoints, both legacy and v1) and fails if the cookie's Path is not a prefix per RFC 6265 §5.1.4. Adding a new endpoint? Append it to refreshEndpointPaths / sessionEndpointPaths. 2. refresh_test.go: TestRefreshCookie_DeliveredToEveryRefreshEndpoint uses a real http.Client + cookiejar.Jar against httptest.NewServer so the standard library enforces path-matching exactly the way a browser does. Login once, then POST every refresh URL the frontend can hit and assert 200. Reverting the path fix makes both layers fail loudly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cuts v1.3.2.
Primary fix: refresh-token cookie path widened from
/api/authto/apiso the browser actually sends it to/api/v1/auth/refresh— the root cause of the ~15-minute logoffs reported against 1.3.0 and 1.3.1.Also includes:
See CHANGELOG.md for full details.