diagram.mmd — flowchart
Token Revocation Flow flowchart diagram

Token revocation is the process of immediately invalidating an issued token before it naturally expires. It is a critical operational capability for handling logout, account compromise, permission changes, and security incidents.

For opaque tokens (random strings validated by database lookup), revocation is straightforward: delete the record from the token store. Any subsequent request presenting that token will fail the lookup and be rejected. This is one reason session-based and opaque-token systems are easier to secure against active threats.

JWT revocation is fundamentally harder because JWTs are self-contained and stateless — the resource server validates them by checking the signature and expiry, without querying any database. A revoked JWT is still cryptographically valid until its exp timestamp passes. There are two main strategies. The first is a token denylist: when a token is revoked, its jti (JWT ID) claim is added to a denylist (typically backed by Redis with TTL set to the token's remaining lifetime). The resource server checks every incoming token's jti against the denylist. This reintroduces a centralized store lookup, partially defeating the statefulness advantage. The second strategy is short expiry: keep access token lifetimes so short (5–15 minutes) that the damage window of a compromised token is limited. Combine this with Refresh Token Rotation — when a refresh token is revoked, no new access tokens can be obtained.

A third approach uses token introspection (RFC 7662): the resource server calls the authorization server's /introspect endpoint on every request to check the token's current status. This is accurate but adds latency and load. Session Based Authentication avoids these trade-offs by design — revocation is instant and free.

Free online editor
Edit this diagram in Graphlet
Fork, modify, and export to SVG or PNG. No sign-up required.
Open in Graphlet →

Frequently asked questions

Token revocation is the process of immediately invalidating a token before its natural expiry. It is essential for handling logout, responding to account compromise, enforcing permission changes, and containing security incidents. Revocation mechanisms differ significantly between opaque tokens and JWTs.
For opaque tokens (random strings validated by database lookup), revocation is straightforward: delete the record from the token store. Any subsequent request presenting that token will fail the lookup and receive a 401 response. This simplicity is one of the main reasons session-based and opaque-token systems are preferred when immediate revocability is a hard requirement.
JWTs are self-contained and stateless, so revocation requires either a token denylist or keeping expiry windows very short. A denylist stores the `jti` (JWT ID) claim of revoked tokens in Redis with TTL matching the token's remaining lifetime; the resource server checks every incoming `jti` against the denylist. Short expiry (5–15 minutes) combined with refresh token rotation limits the damage window without requiring a denylist, at the cost of accepting a brief period during which a revoked token remains technically valid.
RFC 7009 defines a standard `/revoke` endpoint that clients can call to explicitly invalidate an access token or refresh token. The client sends a POST with the token and `token_type_hint`. The authorization server invalidates the token and, for refresh tokens, may also invalidate all associated access tokens. This is the standard mechanism for logout flows in OAuth2 systems.
mermaid
flowchart TD TRIGGER([Revocation trigger:\nlogout / compromise / permission change]) --> TYPE{Token type} TYPE -- Opaque token --> OPQ[Delete token record from store] OPQ --> OPQ_RESULT([Token immediately invalid]) TYPE -- JWT access token --> JWT_STRATEGY{Revocation strategy} JWT_STRATEGY -- Denylist --> DL[Add token jti to Redis denylist] DL --> DL_TTL[Set Redis TTL to token remaining lifetime] DL_TTL --> DL_CHECK[Resource server checks jti on each request] DL_CHECK --> DL_RESULT([Token rejected if jti in denylist]) JWT_STRATEGY -- Short expiry --> SE[Keep access token TTL to 5-15 min] SE --> SE_RT[Revoke refresh token in store] SE_RT --> SE_RESULT([No new access tokens can be issued]) JWT_STRATEGY -- Introspection --> INTRO[Resource server calls /introspect on each request] INTRO --> INTRO_CHECK{Token active?} INTRO_CHECK -- Yes --> ALLOW([Allow request]) INTRO_CHECK -- No --> REJECT([401 Unauthorized]) TYPE -- Refresh token --> RT[Delete refresh token from store] RT --> RT_RESULT([No new access tokens can be issued])
Copied to clipboard