chore: Add no-store Cache-Control to auth response paths#147
Open
ulascanzorer wants to merge 3 commits into
Open
chore: Add no-store Cache-Control to auth response paths#147ulascanzorer wants to merge 3 commits into
ulascanzorer wants to merge 3 commits into
Conversation
Pin Cache-Control: no-store on the Mymir-owned auth surfaces so a shared cache (CDN, corporate proxy, browser bfcache) cannot store and replay a session-bearing response to a different user. - lib/security/headers.ts: three exact-source rules for /sign-in, /sign-up, /consent (rendered auth pages), independent of isProd. - app/api/auth/[...all]/route.ts: backfill no-store on every allowlisted response when absent, guarded by headers.has so BA's well-known public, max-age=15 hint passes through unchanged. - app/api/auth/oauth2/token/route.ts: module-local withNoStore helper on both return paths, after logTokenGrant on the form path. - tests/auth/cache-control.test.ts: pins the static headerRules shape and the live response header on the BA-core, OAuth, token, and well-known surfaces, isolated on loopback IPs 127.0.0.40+. - tests/security/headers.test.ts: updated rule-count assertions to admit the new auth-page rules. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
biome format:check rejected the one-per-line rules.push entries in lib/security/headers.ts; expand them to multi-line object form so the CI Quality Check job's format:check step passes and the full typecheck/lint/test suite runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…est docstring [PYZ-198] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Task Reference: [PYZ-198]
Auth pages (
/sign-in,/sign-up,/consent) and Better Auth handler routes under/api/auth/*relied on Next.js defaults forCache-Control, which can let browsers or intermediate caches store session-bearing HTML andSet-Cookieresponses. This PR pinsCache-Control: no-storeat every Piyaz-owned auth surface so a shared cache (CDN edge, corporate proxy, browser bfcache) cannot store and replay a session-bearing response to a different user, closing the gap between the existing per-route precedent (app/api/oauth/consent-meta/route.ts:81) and the auth pages / BA handler routes.What changed:
lib/security/headers.ts: three exact-source rules inheaderRules()returningCache-Control: no-storefor/sign-in,/sign-up,/consent, independent ofisProd(dev sign-in HTML is the same fixation risk).app/api/auth/[...all]/route.ts: the catch-allhandlerbackfillscache-control: no-storeon every allowlisted response when absent, guarded byheaders.has('cache-control')so BA-owned directives pass through unchanged — notably the well-known discovery docs, which BA tagspublic, max-age=15before the wrapper runs.app/api/auth/oauth2/token/route.ts: a module-localwithNoStorehelper backfillsno-storeon both return paths (the non-form passthrough and the rewritten-form path, afterlogTokenGrant). BA already setsno-storehere (@better-auth/oauth-provider/dist/index.mjs:600) so this is a no-op-on-write; the guard makes the contract project-owned against a future BA upgrade.tests/auth/cache-control.test.ts(new): pins both the staticheaderRulesshape and the live response header on BA-core (/sign-in/email,/sign-up/email,/sign-out,/get-session), sensitive OAuth (/oauth2/userinfo,/oauth2/introspect,/oauth2/revoke), the token endpoint (via the dedicated routePOST), plus a negative assertion that the well-known doc keeps its public cache hint. Modeled ontests/auth/cookie-attributes.test.ts, isolated on loopback IPs127.0.0.40+.tests/security/headers.test.ts: updated two rule-count assertions to admit the new auth-page rules (intent — HSTS absent in dev, host-scoped in prod — unchanged; assertions now match on rule content rather than absolute array length).The well-known discovery routes (
/.well-known/oauth-authorization-server,/.well-known/openid-configuration) are explicitly NOT downgraded tono-storeper RFC 8414 §3; the guard preserves BA'spublic, max-age=15hint.Type of change
Testing
bun run devbun run lint)bun run typecheck)bun test— 912 pass, 0 fail.tsc --noEmit— clean.eslint .— clean. New file runnable viabun test tests/auth/cache-control.test.ts(10 pass).Notes for reviewer
The plan's token-route snippet assumed the route returned
auth.handler(...)directly, but the live code already wraps it withlogTokenGrant(...)(#108 grant logging). I layeredwithNoStoreon top oflogTokenGrantrather than replacing the body, preserving the grant-outcome logging untouched. Surgical: only header injection added, the MCPresource-defaulting body logic is unchanged.Cache-Control: no-storedisqualifies auth-page responses from browser bfcache (Chrome since 2022, Firefox since 2023) — hitting Back after sign-in will not show a stale signed-out page. This is the intended behavior, consistent with how major SaaS treats auth pages, not a regression.Docs impact