How to Implement JWT Security with Blocklisting in Node.js: A Production-Ready Guide

Implement JWT security with blocklisting in Node.js by maintaining a revocation list of compromised tokens using an external store like Redis or Memcached, then verify each request against this list using middleware such as express-jwt-blacklist integrated with express-jwt.

JSON Web Tokens (JWTs) provide stateless authentication for Node.js applications, but their immutable nature creates a critical security gap when tokens require immediate invalidation. The nodebestpractices repository (goldbergyoni/nodebestpractices) offers a battle-tested pattern for adding revocation capabilities without dismantling the benefits of JWT architecture. According to the guidelines in sections/security/expirejwt.md, implementing a blocklist (also called a revocation list) allows you to invalidate specific tokens on demand while preserving the stateless verification flow for valid tokens.

The Challenge of Stateless Token Revocation

JWTs are inherently stateless, meaning that once a token is issued, its signature remains valid until it expires. If a token is compromised or a user needs to be logged out immediately, the application cannot "revoke" the token without breaking the stateless model. The recommended solution is to add a revocation layer on top of JWT, even if it implies losing its stateless nature for the specific purpose of security enforcement.

Architecture of a JWT Blocklist System

A robust blocklist implementation requires three core components working together:

  1. External Storage: The default in-memory store is unsuitable for multi-process deployments. Production environments require an external store such as Redis or Memcached.
  2. Middleware Integration: The blocklist must be consulted on every request through an isRevoked callback in your JWT middleware.
  3. Revocation API: An endpoint or function that adds token identifiers to the blocklist when users log out or security incidents occur.

Implementing JWT Blocklisting with express-jwt-blacklist

The nodebestpractices repository demonstrates this implementation using the express-jwt-blacklist module together with express-jwt. This approach treats any token present in the blocklist as invalid while allowing valid tokens to proceed through standard stateless verification.

Configuring the Blocklist Store

Never use the default in-memory store for production deployments. Instead, configure an external storage adapter. The example in sections/security/expirejwt.md demonstrates Memcached configuration, though Redis is equally suitable:

const jwt = require('express-jwt');
const blacklist = require('express-jwt-blacklist');

// Configure the blocklist store (use external storage for production)
// Here Memcached is demonstrated; replace with Redis as needed
blacklist.configure({
  tokenId: 'jti',          // JWT ID claim used to identify the token uniquely
  strict: true,           // Enforce strict validation checks
  store: {
    type: 'memcached',
    host: '127.0.0.1',
    port: 11211,
    keyPrefix: 'mywebapp:',
    options: {
      timeout: 1000
    }
  }
});

Integrating Blocklist Verification into Middleware

Supply the isRevoked callback to express-jwt to enable blocklist verification on each request. This allows the middleware to check the external store before processing the token payload:

// Attach JWT middleware with revocation check
app.use(jwt({
  secret: 'my-secret',            // Replace with your real secret or public key
  isRevoked: blacklist.isRevoked // Blocklist verification on each request
}));

Revoking Tokens on Logout

When a user logs out or a security incident requires immediate token invalidation, extract the JWT payload (including the jti claim) and add it to the blocklist:

// Example endpoint to revoke a token (e.g., logout)
app.get('/logout', (req, res) => {
  // The JWT payload (including the `jti` claim) is available as req.user
  blacklist.revoke(req.user);   // Adds the token ID to the blocklist
  res.sendStatus(200);
});

Production Considerations

Because the blocklist is consulted on every request, latency to your external store (Redis/Memcached) directly impacts API response times. Monitor connection pooling and timeout settings carefully.

As noted in the nodebestpractices documentation, adding a blocklist "implies losing its stateless nature" for the revocation check. This trade-off is necessary for security-sensitive operations. The README.md file links to the security section providing an overview of the "Support blocklisting JWTs" best practice, emphasizing that the revocation layer sits on top of the standard JWT flow rather than replacing it.

Summary

  • JWTs cannot be natively revoked due to their stateless design; a blocklist provides the necessary revocation capability.
  • External storage is mandatory for production: use Redis or Memcached instead of in-memory stores to support multi-process deployments.
  • Integration requires two steps: configure the blocklist store with a unique token identifier (jti), then pass blacklist.isRevoked to your express-jwt middleware.
  • Revocation is explicit: call blacklist.revoke(req.user) during logout or security events to invalidate tokens immediately.
  • Implementation examples are available in sections/security/expirejwt.md within the goldbergyoni/nodebestpractices repository.

Frequently Asked Questions

What is a JWT blocklist and why do I need one?

A JWT blocklist (or revocation list) is a data store containing identifiers of tokens that should no longer be accepted by your application. You need one because standard JWTs remain valid until their expiration time, meaning compromised tokens cannot be invalidated without a separate tracking mechanism.

Can I use an in-memory store for JWT blocklisting in production?

No. According to the implementation in sections/security/expirejwt.md, the default in-memory store is unsuitable for multi-process deployments where each Node.js instance would maintain a separate, inconsistent blocklist. Always use a shared external store like Redis or Memcached.

How does the jti claim work in token revocation?

The jti (JWT ID) claim provides a unique identifier for each token. When configuring express-jwt-blacklist, setting tokenId: 'jti' tells the middleware which payload field to use as the blocklist key. This allows specific tokens to be revoked individually without affecting other sessions for the same user.

Does using a blocklist make my JWT implementation stateful?

Partially. While valid tokens still follow stateless verification, each request requires a network call to the external blocklist store. As the nodebestpractices documentation states, this adds "a revocation layer on top of JWT, even if it implies losing its stateless nature," but this trade-off is essential for production security requirements.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →