How to revoke a JWT before it expires (logout, removed users, stolen tokens)

Logging out deleted the token from the browser, but a copy of it still works. That is how stateless tokens behave, and there are four standard fixes.

Why logout does not kill the token

Client-side logout deletes the browser's copy. Any other copy (another tab, a proxy log, a stolen token) still carries a valid signature and an exp in the future. If your API verifies only the signature and exp, it has no way to know the user logged out or was removed.

Four ways to revoke

MethodRevokesCost
Short access token + revocable refresh tokenEverything, within one access-token lifetimeRefresh tokens stored server-side; rotate them on each use and reject reuse
jti denylistOne specific token, instantlyA lookup per request; entries can be dropped once their exp passes, so the list stays small
Per-user “tokens valid after”All tokens for one user issued before a moment (logout everywhere, password change, user removed)One value per user, checked against iat
Rotate the signing keyEvery token signed with the old keyEveryone signs in again; use for leaks, not routine logout

Most apps combine the first and third: short access tokens keep the window small, and the per-user timestamp gives an instant “sign out everywhere” and “this person no longer works here”.

Removing a user's access to one thing

If permissions are written inside the token (roles, a list of projects), removing someone from a project does nothing until a new token is issued. Either keep tokens short, or check fine-grained permissions in the database on each request and keep only identity in the token. That is the same lesson as with share links: decide access at request time.

Read the timing claims

exp, iat and nbf are Unix seconds (not milliseconds; see seconds vs milliseconds). Paste a token you are allowed to inspect into the JWT decoder to see them as dates, or convert one number in the Unix time converter. Decoding is not verifying: anyone can read a JWT's payload, which is plain Base64 (see decoding JWTs safely).

Checklist

Related

Can you revoke an S3 presigned URL? · Removed someone but they can still see the file · Designing share links you can revoke · HTTP 401 vs 403 · no-cache vs no-store · JWT decoder · Wirebook: does the old link still work? · Fusenote: one-time notes

Questions

Can you invalidate a JWT?

Not by itself: a valid signature and a future exp are enough for a server that checks nothing else. Revoking needs server-side state such as a jti denylist, a per-user tokens-valid-after time, revocable refresh tokens, or a key rotation.

Why does my JWT still work after logout?

Logout only deleted the client's copy. Any other copy stays valid until exp unless the server checks a denylist or a per-user revocation time.

How short should access tokens be?

Commonly 5 to 15 minutes, paired with a refresh token that the server can revoke. The shorter the lifetime, the smaller the window after revocation.

Is a JWT denylist the same as going back to sessions?

Partly. It adds a lookup, but only for tokens that were revoked before their exp, so the list stays small and entries can be removed once they expire.

Are exp and iat in seconds or milliseconds?

Seconds since 1970-01-01 UTC. Using milliseconds makes tokens look valid for thousands of years or already expired.

Published .

Try it

Related guides

Newest guides