Card 09/ 12

GotchaDifficulty: Advanced2 min

Revoking a JWT Before It Expires

Logging a user out does not stop a stolen JWT, which stays valid until its exp.

A self-contained JWT is accepted by checking its signature and times, with no lookup, so nothing the issuer does afterwards reaches it. Signing a user out, disabling their account or noticing a stolen token leaves the JWT working until its exp. Revoking one early needs a design chosen in advance: short lifetimes, a deny list checked on each request, or asking the issuer.

Picture an employee who left on Friday afternoon. Their account was disabled at 17:00, and on Saturday morning their token, issued with a 24-hour lifetime, still read customer records.

The symptom. Requests from a disabled or signed-out user keep succeeding for hours after access was removed.

Ways to revoke a JWT early, against the delay before it stops working, the cost per request and what each needs
ApproachDelay before it stopsCost per requestNeeds
Short access tokens and a revocable refresh tokenUp to one access-token lifetime, such as 5 to 15 minutesNoneRefresh flow; the refresh token is checked at the issuer
A deny list of jti valuesImmediateOne fast lookupA shared store every API reads
Introspection at the issuerImmediateOne call to the issuer, or a cached answerAn introspection endpoint, RFC 7662
Rotating the signing keyImmediate for all tokensNoneAccepting that every user is signed out

Why it happens. The property that makes JWTs fast, verification without a lookup, is the same property that makes them deaf to later changes.

What it costs. A window, as long as the token's lifetime, in which a stolen token or a removed user still has access.

What to do. Keep access tokens short, minutes rather than hours, and pair them with a refresh token the issuer can revoke; most systems need nothing more. Where a minute of exposure is too much, such as administrative actions, check a deny list or introspect. Deleting the token from a database does nothing, because nothing was stored to delete.