fix: logout + Back no longer views/interacts with the dashboard (hard-nav + bfcache guard) - #14
Merged
Merged
Conversation
…ow a stale dashboard After logout, pressing Back restored the authenticated dashboard from the browser's back/forward cache (bfcache). The session is already invalidated server-side (any interaction 401s -> redirect), so this is a defense-in-depth + UX gap, not an auth bypass. Root cause: dashboard pages are dynamically rendered (cookies/headers) and Next owns their Cache-Control — it serves `no-cache, must-revalidate`, not `no-store`. Only `no-store` disables bfcache in Chromium/Firefox, and neither next.config headers() nor the edge proxy can override a dynamic route's Cache-Control (verified empirically — both are dropped/replaced). Fix: a client BfcacheGuard in the dashboard layout reloads any page restored from bfcache (`pageshow` with `persisted`); the reload re-hits the auth check and a logged-out visitor is redirected to /login (MDN's documented pattern for this exact problem). Scoped to the dashboard; the static marketing/landing stays cacheable (Phase 3 LCP work untouched). e2e: pins that a logged-out reload of the dashboard lands on /login (real bfcache Back is verified manually — headless bfcache is unreliable to trigger and window.location.reload can't be spied in Chromium). Verified: biome, next build (31 routes), full e2e 12/12. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PA9EzergAZ12Dy1QBqx5kp
…shboard
Follow-up to the BfcacheGuard: that alone was insufficient. The logout handlers
did `await signOut(); router.push("/login")` — a SOFT (client-side) navigation.
A soft nav leaves the dashboard SPA and Next's client Router Cache alive, so
after logout, Back restored the still-mounted dashboard and its prefetched
authenticated RSC — the logged-out user could not just VIEW it but navigate and
interact via cached soft navigations (no server round-trip, so the dead session
was never re-checked).
Fix: `window.location.href = "/login"` — a hard navigation that tears down the
SPA and discards the Router Cache. Combined with the BfcacheGuard (browser
bfcache restore → reload) and the edge proxy (no cookie → /login), Back after
logout now lands on /login through every path. Dropped the now-unused useRouter.
e2e: the auth flow test now presses Back after logout and asserts /login (it
reproduced the bug on the soft-nav version). Full suite 12/12; build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PA9EzergAZ12Dy1QBqx5kp
Nova Analytics v1.0.0. Aligns the package version (inherited 2.2.0 from the arhamkhnz template) with the v1.0.0 release tag. Version field only — no dependency or lockfile-format changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PA9EzergAZ12Dy1QBqx5kp
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.
The bug (found in prod during release testing)
After logout, pressing Back re-showed the dashboard — and, as Roberto found on a second pass, it was not just viewable but interactive while logged out. The session is revoked server-side, so this is a defense-in-depth + UX gap, not an auth bypass — but it must not happen.
Root cause — two layers
await signOut(); router.push("/login").router.pushis a soft navigation: it leaves the dashboard SPA and Next's client Router Cache (prefetched authenticated RSC) alive. Pressing Back restored the still-mounted dashboard, and cached soft navigations rendered other dashboard pages with no server round-trip — so the dead session was never re-checked. That's why a logged-out user could browse/interact.Cache-Control(no-cache, neverno-store— the only directive that disables bfcache). Neithernext.configheaders()nor the edge proxy can override a dynamic route's Cache-Control (verified empirically), so Back could restore a stale render from the browser's back/forward cache.Fix — two parts
window.location.href = "/login"instead ofrouter.push— a full load tears down the SPA and discards the Router Cache. (Dropped the now-unuseduseRouter.)BfcacheGuardin the dashboard layout: reloads any page restored from bfcache (pageshow+persisted); the reload re-hits the auth check. MDN's documented pattern.Together with the edge proxy (no cookie → /login), Back after logout now lands on /login through every path. Scoped to
/dashboard; the static landing stays cacheable (Phase 3 LCP work untouched).Test plan
biome·next build(31 routes) green.window.location.reloadcan't be spied in Chromium, so the browser-bfcache path is hand-verified.Base is
develop(recreated frommain— the release merge auto-deleted it). Promote develop→main to ship.🤖 Generated with Claude Code
https://claude.ai/code/session_01PA9EzergAZ12Dy1QBqx5kp