Skip to content

fix: revoke the JWT on logout via a jti denylist - #50

Merged
lesnik512 merged 1 commit into
mainfrom
jti-denylist
Oct 10, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
jti-denylist

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Closes #20.

Before this change, logout only deleted the cookie. A copy of the token taken before logout stayed valid for the rest of its 7 days.

What changes

  • Login and register issue every token with a random jti.
  • Logout writes that jti and the token's expiry to a new revoked_tokens table. It also prunes rows that have already expired, so no scheduled job is needed. The insert is ON CONFLICT DO NOTHING, so two concurrent logouts of one token don't collide.
  • jwt_cookie_auth gets a revoked_token_handler that rejects listed tokens, and require_claims=["jti"], so a token without a jti gets a 401.
  • ADR-0011 records the decision and the per-request cost.

Design notes

  • Only the current session is revoked. Other sessions of the same user keep working (test_logout_keeps_other_sessions). A token version on users was rejected because one logout would end every session.
  • The check runs in auth middleware, before the request container exists, so it builds a short REQUEST child container and closes it before the handler runs. That adds one primary-key lookup per authenticated request, and a request still holds one pooled connection at a time. retrieve_user_handler still reads nothing, so the ADR-0010 invariant test is unchanged apart from dropping the "logout does not revoke" remark from its docstring.
  • Deploying this logs everyone out, because tokens issued before it have no jti and are rejected. The alternative was to let them through for up to 7 days without any way to revoke them.

Tests

  • New API tests:
    • a copied pre-logout token gets a 401;
    • another session survives logout;
    • a token without a jti gets a 401.
  • New use-case tests: pruning, and that revoking twice is a no-op.
  • Two existing tests now give their hand-made tokens a jti, so they still reach the paths they pin.
  • just test: 123 passed, 100% coverage. just test-migrations: 4 passed. just lint and just adr-check are clean.

@lesnik512
lesnik512 merged commit 979fbb5 into main Oct 10, 2026
4 checks passed
@lesnik512
lesnik512 deleted the jti-denylist branch October 10, 2026 10:06
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.

Logout does not revoke the JWT

1 participant