JWT Revocation — Complete Strategies for Invalidating Tokens Before Expiry
In this tutorial, you will learn about JWT Revocation. We cover key concepts, practical examples, and best practices to help you master this topic.
JWT revocation is the ability to invalidate tokens before their natural expiry, trading off some statelessness for the ability to respond to security events immediately.
What You'll Learn
All JWT revocation strategies, their trade-offs, and how to combine them for defense in depth.
Why It Matters
Stateless JWTs cannot be revoked — they are valid until expiry. For logout, account suspension, or compromised tokens, you need revocation. Choosing the right Strategy affects your architecture, scalability, and security.
Real-World Use
Auth0 uses blacklisting for immediate revocation. GitHub uses short TTL with refresh. Banking APIs use short TTL with per-user invalidation counters. Durga Antivirus Pro combines short TTL (15 min access) with Redis blacklisting for emergency revocation.
flowchart TD
A["Revocation Strategy"] --> B["Short TTL\n15 min access"]
A --> C["Blacklist\nRedis-based"]
A --> D["Token Families\nRotation tracking"]
A --> E["User Counter\nGlobal invalidation"]
B --> F["Simple, stateless"]
C --> G["Immediate, needs Redis"]
D --> H["Detect token theft"]
E --> I["Logout all devices"]
style A fill:#dbeafe,stroke:#2563eb
style F fill:#dcfce7,stroke:#16a34a
style G fill:#fef3c7,stroke:#d97706
style H fill:#fef3c7,stroke:#d97706
style I fill:#dcfce7,stroke:#16a34a
Revocation Strategies Comparison
| Strategy | Speed | Complexity | State Required |
|---|---|---|---|
| Short TTL + Refresh | Delayed (until expiry) | Low | No (stateless) |
| Blacklist | Immediate | Medium | Yes (token IDs) |
| Token Families | Immediate (theft detection) | High | Yes (families) |
| User Invalidation Counter | Immediate | Medium | Yes (user counter) |
| Combination | Best of all | High | Yes |
Code Example: User-Level Invalidation
import jwt, redis, datetime
r = redis.Redis(decode_responses=True)
SECRET = "your-secret"
def issue_token(user_id):
"""Issue token with the current invalidation counter."""
counter = r.get(f"user:invalidation:{user_id}") or "0"
payload = {
"sub": user_id,
"inv_counter": int(counter),
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
}
return jwt.encode(payload, SECRET, algorithm="HS256")
def invalidate_all_user_tokens(user_id):
"""Increment counter — all existing tokens become invalid."""
r.incr(f"user:invalidation:{user_id}")
def validate_token(token):
"""Validate token, checking invalidation counter."""
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
user_id = payload["sub"]
current_counter = int(
r.get(f"user:invalidation:{user_id}") or "0"
)
if payload.get("inv_counter", -1) < current_counter:
return None # Token was invalidated
return payload
except jwt.InvalidTokenError:
return None
Choosing a Strategy
| Scenario | Recommended Strategy |
|---|---|
| User logout single device | Short TTL + blacklist the refresh token |
| User logout all devices | User invalidation counter |
| Account suspension | Blacklist + short TTL |
| Token theft suspected | Token families with rotation |
| High-security API | All of the above |
| Public API, low security | Short TTL only |
Common Mistakes
1. Relying Only on Blacklisting
Blacklisting requires Redis and adds latency. Use short TTL as primary, blacklist as emergency backup.
2. Not Invalidating Refresh Tokens
Revoking access tokens but leaving refresh tokens valid means the attacker can simply get a new access token.
3. Using Only User Counter Without Expiry
The invalidation counter never decrements. Over years, the counter grows. Use short TTL so counter checks are only needed within the token lifetime.
4. Not Handling Concurrent Sessions
Invalidating all tokens on password change logs out all devices. For some apps this is desired; for others, use per-device tokens.
5. Ignoring the jti Claim
Without jti (unique token ID), you cannot blacklist individual tokens. Always include jti.
Practice Questions
- Why is revocation harder with stateless JWTs than with session cookies?
- What is the simplest JWT revocation strategy?
- How does a user invalidation counter work?
- What is a token family and how does it detect theft?
- Why should you combine multiple revocation strategies?
Answers:
- Stateless JWTs are self-contained and valid without server lookup. Revocation requires adding server-side state (blacklist, counter), partly defeating statelessness.
- Short TTL (15 minutes). Tokens expire naturally. Combined with refresh token rotation, this covers most scenarios.
- Each token includes the current invalidation counter. Incrementing the counter invalidates all tokens with the old counter value. The server checks the counter on each request.
- A token family groups all refresh tokens derived from the same login. When an already-rotated token is presented, it signals theft, and the entire family is revoked.
- No single strategy is perfect. Short TTL covers normal expiry, blacklist covers emergencies, rotation covers theft — defense in depth.
Challenge: Implement a multi-layered revocation system: short access TTL (15 min), refresh token rotation with family tracking, user invalidation counter for "logout all," and Redis blacklist for emergency single-token revocation.
FAQ
Mini Project
Build a comprehensive revocation system with JWT that supports: short TTL with automatic refresh, refresh token rotation with family tracking, user invalidation counter for "logout all devices," and Redis blacklist for emergency revocation of specific tokens.
What's Next
Now complete the JWT Capstone Project — building a complete JWT authentication service with all security features.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro