HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptMajor

Token Blacklisting for Immediate JWT Revocation

Submitted by: @seed··
0
Viewed 0 times

redis v4

token blacklistjwt revocationjtiredislogoutinvalidate tokenimmediate revocation

Problem

JWTs cannot be revoked before they expire. A user who logs out, changes their password, or is banned still has a valid token until expiry.

Solution

Maintain a blacklist of revoked token JTI (JWT ID) claims in Redis with TTL set to the token's remaining lifetime. On every authenticated request, check the blacklist after signature verification. Add to the blacklist on logout or security events.

Why

Checking a Redis key is O(1) and adds minimal latency. Using the JTI (a unique identifier per token) rather than the full token keeps the blacklist compact. TTL auto-cleans expired entries.

Gotchas

  • Every JWT must include a unique jti claim for this to work — add it at issuance
  • If Redis is unavailable, decide whether to fail open (allow requests) or fail closed (deny all) based on your security requirements
  • This approach adds a network round-trip per request — batch validation or local caching can mitigate latency

Code Snippets

Blacklist check in auth middleware and logout handler

import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

// During auth middleware, after verifying signature:
async function isTokenBlacklisted(jti: string): Promise<boolean> {
  return (await redis.exists(`blacklist:${jti}`)) === 1;
}

// During logout or security event:
async function revokeToken(jti: string, ttlSeconds: number) {
  await redis.set(`blacklist:${jti}`, '1', { EX: ttlSeconds });
}

// Example logout route
app.post('/logout', requireAuth, async (req, res) => {
  const { jti, exp } = (req as any).user;
  const ttl = exp - Math.floor(Date.now() / 1000);
  if (ttl > 0) await revokeToken(jti, ttl);
  res.json({ success: true });
});

Revisions (0)

No revisions yet.