A JWT is a signed snapshot. Roles inside it were true when it was issued and stay in it until the token expires. If you demote someone, their existing token still says admin. Shrink the gap with short lifetimes, a live lookup for sensitive actions, or a revocation mechanism.
Why it happens
RFC 7519 defines the exp claim as the time on or after which the JWT must not be accepted. Until then, any server that validates the signature will accept it. Nothing in a signed token updates when your database changes.
The OWASP JSON Web Token Cheat Sheet makes the same point from the session side. JWTs are often suggested for stateless sessions, but if you use them for user sessions you need a way to manage invalidation.
The trade-off in one table
| Approach | How it handles a role change | Cost |
|---|---|---|
| Short expiry | Old role lasts only until expiry | More refreshes |
| Live lookup of roles per request | Always current | Added latency and a dependency |
| Token status list | Issuer marks tokens revoked, consumers check the list | Extra fetch, extra design |
| Deny list by token ID | Revoked tokens rejected until they expire | Stateful, no longer a stateless session |
The cheat sheet describes the status list approach: the JWT carries a status claim pointing to a list that aggregates revocation status for many tokens, and consumers fetch it. It also describes a deny list keyed on the jti and iss claims, and notes that this makes sessions no longer completely stateless, which can defeat the reason you chose JWTs.
One specific warning: do not key a deny list on the raw token or a hash of it. The sheet says that can allow a bypass through JWT malleability, where an attacker modifies an alternative representation that still passes signature verification.
A practical split
Not every action needs the same freshness.
- Low risk reads. Trust the claim until expiry, with a short lifetime.
- Sensitive writes, billing, admin changes. Look up the current role or membership at request time.
- Offboarding and incident response. Revoke tokens and sessions explicitly, and test that it works.
The OWASP multi-tenant sheet reflects this in its sample code. It reads the tenant from a verified claim, then still calls a membership lookup, noting that authorization depends on the issuer's guarantees or a current membership check.
Keep claims small
Put identity in the token and avoid copying a long list of permissions into it. Big claim sets go stale more visibly and also hit size limits when sent in headers. Let the service resolve detailed permissions from its authoritative store.
Measure your gap
Write down your effective revocation delay. It is the token lifetime plus any cache time in gateways and services. If your access token lasts an hour and a gateway caches decisions for ten minutes, a removed user may keep access for seventy minutes. Ask whether that fits your risk.
Test it directly: demote a test user, keep using the old token and record how long it works. Then repeat after a forced logout.
Hardening that helps
- Short access tokens with refresh tokens you can revoke.
- Sender-constrained tokens, which the cheat sheet notes limit use of a stolen token without its private key, though they do not prevent disclosure.
- Validation of issuer, audience and token type, not signature alone.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how recurring checks catch change after release.
Safeguard offers dependency scanning for the JWT libraries you use, along with reachability analysis to rank advisories. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is a refresh token the fix?
It helps. Short access tokens plus a revocable refresh token limit how long a stale role lives, since the next refresh can reissue with current roles.
Do I need a deny list for every JWT system?
No. Many systems rely on short lifetimes and live checks for sensitive actions. Add a deny list or status list when your revocation requirement is tighter than the token lifetime.
Sources
- RFC 7519, JSON Web Token, exp claim
- OWASP JSON Web Token Cheat Sheet
- OWASP Multi-Tenant Security Cheat Sheet
Last reviewed: 19 October 2026.