Script Valley
Authentication From Scratch
JWT AuthenticationLesson 3.5

How to invalidate JWTs before they expire

JWT statelessness problem, token blacklist, short expiry strategy, token versioning in DB, logout and token invalidation, jti claim

The Stateless Problem

JWTs are stateless — your server does not store them. This means you cannot simply delete a token to invalidate it. If a user logs out or you want to force all sessions to end (after a password change), you have a problem.

Strategy 1: Short Expiry

Keep access tokens short (15 minutes). A stolen token is only valid for a brief window. This is the simplest approach and requires no database. Combined with refresh token rotation, it covers most use cases.

Strategy 2: Token Blacklist

On logout, store the token's jti (JWT ID) claim in a database or Redis with a TTL equal to the token's remaining validity. On each request, check the blacklist. This is the most complete solution but adds latency.

// When signing, include a jti
const token = jwt.sign(
  { sub: user.id, jti: crypto.randomUUID() },
  SECRET,
  { expiresIn: '15m' }
);

// On logout, add jti to blacklist with TTL
await redis.set(`blacklist:${payload.jti}`, '1', 'EX', 900);

// On each request, check blacklist
const revoked = await redis.get(`blacklist:${payload.jti}`);
if (revoked) return res.status(401).end();

Strategy 3: Token Version

Store a tokenVersion integer per user in the database. Include it in the token payload. On each request, compare the token's version to the database version. Incrementing the DB version instantly invalidates all existing tokens for that user.