All notes

JWTs Are Stateless... So How Do You Revoke Them?

One of the most common backend interview questions. If an attacker steals a JWT, how do you invalidate it before it expires?

4 min read

Imagine a user logs into your application.

The authentication server generates a JWT.

Client
 
↓
 
Login
 
↓
 
JWT Issued
 
↓
 
Subsequent API Requests

Everything works perfectly...

Until one day the user's access token gets stolen.

Maybe their laptop was compromised.

Maybe an XSS attack leaked the token.

Maybe someone copied it from browser storage.

Now the attacker can send requests using the exact same JWT.

The first question most interviewers ask is:

"How do you revoke the token?"

The tricky part is that JWTs are stateless.

Once a JWT is issued, the server doesn't normally store it anywhere.

Every request is simply verified using the secret key.

String username = jwtService.extractUsername(token);
 
if(jwtService.isTokenValid(token)){
    // Allow request
}

The server never checks a database.

So if an attacker steals the JWT, it remains valid until it expires.

Why Not Store Every JWT?

Imagine millions of active users.

Storing every access token would defeat one of JWT's biggest advantages.

Without JWT
 
↓
 
Database Lookup
 
↓
 
Validate Session
 
↓
 
Process Request

Instead, JWT verification is extremely fast.

Verify Signature
 
↓
 
Extract Claims
 
↓
 
Accept Request

No database required.

That's why JWTs scale so well.

But it also means immediate revocation isn't built in.

Solution 1: Short Lived Access Tokens

This is the most common solution.

Instead of issuing a token valid for several hours, issue one that expires quickly.

Access Token
 
Expires In
 
10 Minutes

Even if an attacker steals it, the damage is limited.

Solution 2: Refresh Tokens

Instead of forcing users to log in every ten minutes, applications also issue a refresh token.

Login
 
↓
 
Access Token
(10 mins)
 
+
 
Refresh Token
(30 days)

When the access token expires:

Client
 
↓
 
Refresh Token
 
↓
 
New Access Token

The user stays logged in without entering their password again.

Solution 3: Revoke The Refresh Token

Suppose the user reports suspicious activity.

Simply delete the refresh token.

DELETE
FROM refresh_tokens
WHERE user_id = 101;

Now the attacker cannot obtain new access tokens.

Existing access tokens eventually expire.

Solution 4: Token Blacklisting

Sometimes waiting ten minutes is unacceptable.

For example:

A common solution is Redis.

Redis
 
↓
 
Blacklisted JWT IDs

Whenever a request arrives:

if(redis.exists(jwtId)){
    throw new UnauthorizedException();
}

Now compromised tokens can be revoked immediately.

The downside is that every request now requires an additional Redis lookup.

Solution 5: Refresh Token Rotation

A more secure approach is rotating refresh tokens.

Login
 
↓
 
Refresh Token A
 
↓
 
Exchange
 
↓
 
Refresh Token B

The old refresh token becomes invalid immediately.

If someone later attempts to use Token A again:

Token Reuse Detected
 
↓
 
Possible Token Theft
 
↓
 
Terminate Session

Many modern authentication providers use this strategy.

Solution 6: Token Versioning

This is one of my favorite interview answers because it's simple and elegant.

Each user stores a token version.

Users Table
 
id
 
token_version
 
1

When generating a JWT:

{
    "userId":101,
    "version":1
}

Every request compares the JWT version against the database.

if(jwt.version != user.tokenVersion){
    rejectRequest();
}

Need to log the user out from every device?

Simply increment the version.

UPDATE users
SET token_version = token_version + 1
WHERE id = 101;

Every existing JWT immediately becomes invalid.

No blacklist required.

Additional Security Layers

Large applications often combine multiple techniques.

Even if a valid JWT is presented, suspicious activity can trigger additional verification.

What Interviewers Want To Hear

If an interviewer asks:

"JWTs are stateless. How do you revoke them?"

A strong answer is:

JWTs are stateless by design, so immediate revocation requires introducing some server side state. In practice, I would use short lived access tokens with revocable refresh tokens. For highly sensitive applications, I'd also maintain a Redis blacklist or implement token versioning to invalidate existing sessions immediately.

Final Thoughts

JWTs are excellent for scalability because they eliminate database lookups for every request.

But that convenience comes with a tradeoff.

Once issued, a JWT cannot magically disappear.

The best authentication systems don't rely on a single technique.

They combine short lived access tokens, refresh token rotation, revocation mechanisms, and server side validation to balance both performance and security.

That's the difference between simply using JWTs and designing an authentication system that's ready for production.