Skip to content

fix(auth): refuse revoked sessions on every request - #65

Open
christian-at-sevenlab wants to merge 1 commit into
mainfrom
fix/auth-session-revocation
Open

christian-at-sevenlab wants to merge 1 commit into
mainfrom
fix/auth-session-revocation

Conversation

@christian-at-sevenlab

Copy link
Copy Markdown
Contributor

What this changes

Adds server/middleware/02.session-revocation.ts to the nuxt4:auth template. On every request that carries a session cookie, it checks that the sessions row exists, belongs to the user and has not expired. If any check fails, the session is cleared for that request and the cookie is deleted.

Why

Logging out, revoking a device and deleting a user all remove only the sessions row. The sealed cookie itself stays valid. nuxt-auth-utils calls the fetch hook in server/plugins/session.ts only from GET /api/_auth/session. It never calls it from getUserSession or requireUserSession. As a result, every API route still accepted a revoked cookie. The generated AGENTS.md/README said the plugin "validates the row on every request", which was not true.

This was found and fixed first in a downstream project, then ported here.

Type of change

  • Bug fix
  • New feature or template
  • Refactor with no behaviour change
  • Documentation
  • Build, CI or release tooling

AI assistance

  • No AI agent involved
  • Claude Code
  • Cursor
  • GitHub Copilot
  • Codex
  • Other, named below

Agent, service and model, if you know it: Claude Code, Opus 5.5

How much of this is agent-written?

  • Whole change, reviewed by me before opening
  • Substantial parts, with my own edits on top
  • Small parts, or just the tests or docs
  • Only the commit message or this description

Feature versions

  • I changed what a feature emits, and bumped that feature's version (nuxt4:auth 1.10.1, nuxt4:vitest 1.0.7)
  • I changed a collectDocs or collectEnv contribution, and also bumped the feature that writes the target file (nuxt4:docs 1.0.25)
  • Not applicable

Testing

pnpm tsc   # passes
pnpm test  # 989 passed; 1 failure in core/test/browser.test.ts (expects xdg-open, gets wslview under WSL), which fails on main too

The e2e auth suite (test/e2e/api/auth.test.ts) now checks that both a logged-out cookie and a session revoked from another device get a 401 on /api/auth/sessions. These tests need a running app, and I have not run them against a fresh scaffold yet. The same middleware, with an extra check for deactivated users, runs in the downstream project, where these cases pass.

  • pnpm tsc passes
  • pnpm test passes (see the WSL-only failure above)
  • I removed the behaviour each new assertion covers, watched the test fail, and put it back

Checklist

  • Scoped to one concern. Unrelated cleanups are in their own commit or PR
  • Public-facing docs updated where behaviour changed (docs/, README.md)
  • No internal tracker numbers, post-mortem narrative, or internal infrastructure details in the diff
  • Files under packages/*/templates/** are LF (.gitattributes enforces it; the copy path never translates line endings)
  • No secrets, tokens or real credentials, including in test fixtures

Anything else

  • clearUserSession does not work here: the next getUserSession in the same request re-reads the still-valid request cookie. The middleware therefore strips h3's per-request cache (event.context.sessions[name]) in place. If that cache is missing, it fails closed with a 401.
  • This adds one indexed primary-key lookup per request that carries a session cookie.
  • Changes to a user's role still reach an existing cookie only through the client's session fetch, as before. This PR does not change that.

Logout, revoking a device and deleting a user only remove the `sessions`
row; the sealed cookie stayed valid. nuxt-auth-utils calls the `fetch` hook
in server/plugins/session.ts only from GET /api/_auth/session, never from
getUserSession or requireUserSession, so every API route kept accepting a
revoked cookie. The docs claimed the plugin validated the row on every
request.

server/middleware/02.session-revocation.ts now checks, on each request that
carries a session cookie, that the row exists, belongs to the user and has
not expired. If not, it strips h3's per-request session cache in place and
deletes the cookie. clearUserSession does not work here: the next
getUserSession re-reads the still-valid request cookie. Without that cache
it fails closed with a 401.

The auth e2e suite now asserts that a logged-out cookie and a session
revoked from another device both get a 401.

This branch has not been deployed

No deployments
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.

1 participant