feat(auth): revocation-aware token introspection endpoint (TODO 4) - #117
Closed
lopugit wants to merge 2 commits into
Closed
feat(auth): revocation-aware token introspection endpoint (TODO 4)#117lopugit wants to merge 2 commits into
lopugit wants to merge 2 commits into
Conversation
/api/v1/auth/jwks lets external platforms verify a token's signature,
issuer, and expiry offline, but can't answer the one question offline
verification never can: is the session behind the token still live?
Adds POST /api/v1/auth/introspect (introspection.ts + route). It
re-verifies the token through the shared verifier, then checks the
backing session in Mongo via the same session->user path getCurrentUser
uses, so introspection can never disagree with it. Returns
{active, sub, jti, exp, iat, iss} plus purpose: app tokens report
purpose:'app'+client_id (the "Login with Thingtime" grants the general
auth path rejects on purpose), account sessions report
purpose:'session'+username. Every invalid/expired/revoked token returns
an identical {active:false} so the endpoint is not an oracle for why a
token stopped working.
Anonymous like jwks (a token holder can already probe liveness against
any authed route) but rate limited per IP (auth.introspect, 120/min) so
it can't be used for bulk token scanning. Registered in the Nitro
dispatcher; API doc + two apiTests added.
Verified live against local Nitro: a fresh session token introspects
active:true with its identity, then active:false after logout revokes
the session; missing/blank token -> 400; garbage/unsigned token ->
active:false with no identity fields.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 21, 2026
Owner
Author
|
Consolidating duplicate TODO-4 PRs: #116 is the single winner — its introspection path deliberately reports purpose:'app' sessions (the Login-with-Thingtime tokens external platforms actually hold), which this PR's resolution path would default-deny as active:false. See the consolidation note on #116. |
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.
Fixes TODO 4 — Add revocation-aware token introspection for external platforms.
What
Adds
POST /api/v1/auth/introspect./api/v1/auth/jwksalready lets external platforms verify a token's signature, issuer, and expiry offline — introspection adds the online half JWKS structurally cannot answer: is the session behind the token still live?remix/app/api/utils/auth/introspection.ts—introspectToken: re-verifies the token via the sharedverifyJwt, then checks the backing session in Mongo through the same session→user pathgetCurrentUseruses (resolveSessionUser), so introspection can never disagree with normal auth.remix/app/routes/api/v1/auth/introspect/_introspect.tsx— the route: 16 KiB body cap, per-IP rate limit, private no-store.auth.introspectrate rule (120/min/IP); API doc entry; two apiTests.Response shape
{ active: true, sub, jti, exp, iat, iss, purpose: 'session', username }purpose: 'app', client_id(these are the grants the general auth path rejects on purpose — introspection reports them so integrations can poll revocation without holding full account credentials){ active: false }(no oracle for why)Anonymous like JWKS — a token holder can already probe liveness against any authed route — but rate limited so it can't be used for bulk token scanning.
Verification (live against local Nitro)
active:true+ sub/jti/exp/iat/purpose:session/usernameactive:false✅ (revocation works)tokenactive:false, no identity fieldsAlso annotates TODO 4 in
TODO/TODO.md.Session 3 of 10 parallel todo sessions (previous: #94, #102, #105, #112); claim branch pushed before work started.
🤖 Generated with Claude Code