fix(auth): CF Access logout revocation, refresh-family binding, safe logout redirect#1193
Merged
Merged
Conversation
…g-derived logout redirect Three gaps in the Cloudflare Access SSO paths (#1058 follow-up) left CF users with weaker token-theft protection than password users: - GET /cf-access-logout only cleared the cookie; access tokens stayed valid to expiry and the 7-day refresh token remained fully usable. Now resolves the refresh cookie, verifies it, and calls revokeAllUserTokens + revokes the refresh jti, mirroring POST /logout. Best-effort: missing/invalid cookie or Redis errors still clear + redirect. - Both CF token-mint paths (cfAccessLogin middleware + cf-access-login redirect route) called createTokenPair without a refresh-token family, so stolen-token reuse detection (family revocation on rotation replay) was silently disabled for the CF cohort. Both now mint + bind a family, same as /login. - The logout redirect origin was built from the attacker-controllable Host header (open redirect). Now derived from DASHBOARD_URL/PUBLIC_APP_URL, with an https-pinned Host fallback only when neither is configured. - Docs: cloudflare-access-trust.mdx now lists cf-access-login/logout as paths the Access application must cover. 32/32 tests pass across both touched test files (11 new), plus login/helpers regression run 11/11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploying breeze with
|
| Latest commit: |
e53a304
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://6ce4c809.breeze-9te.pages.dev |
| Branch Preview URL: | https://fix-cf-access-token-revocati.breeze-9te.pages.dev |
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.
Root cause: Verification of the v0.69.0..main Codex review confirmed three gaps in the Cloudflare Access SSO paths added in #1058 — the CF cohort had strictly weaker token-theft protection than password users:
GET /cf-access-logout(cfAccessRedirectLogin.ts:230) was synchronous and only calledclearRefreshTokenCookie— norevokeAllUserTokens, no jti revocation. After "Sign out", an exfiltrated access token stayed valid to expiry and the 7-day refresh token remained fully usable.Header.tsxroutes CF logout exclusively through this endpoint and skipsapiLogout().cfAccessLogin.ts:164,cfAccessRedirectLogin.ts:160) calledcreateTokenPairwith norefreshFam, so CF-minted tokens fall into the/refreshhandler's legacy skip path and the family-revocation reuse-detection (RFC 9700 §4.13.2) never fires for them — exactly the invariantrefreshTokenFamily.ts's docstring warns about.Hostheader (open redirect off the Breeze origin).Fix:
revokeAllUserTokens(sub)+revokeRefreshTokenJti(jti)— mirroringPOST /logout. Missing/invalid cookie or Redis failure still clears + 302s (no 500).mintRefreshTokenFamily→createTokenPair(..., { refreshFam })→bindRefreshJtiToFamily, same as/login.DASHBOARD_URL || PUBLIC_APP_URL(the established pattern inlogin.ts/password.ts); https-pinnedHostfallback only when neither is set./api/v1/auth/cf-access-login+/cf-access-logoutin the Access application and warn against an/api/*bypass swallowing them.Tests: 11 new tests (logout revocation incl. no-cookie/invalid-cookie/Redis-failure paths, family binding on both mint paths incl. the MFA temp-token short-circuit, spoofed-Host redirect cases).
cfAccessRedirectLogin.test.ts+cfAccessLogin.test.ts: 32/32 pass;login.test.ts+helpers.test.tsregression: 11/11 pass.🤖 Generated with Claude Code