Skip to content

Security — Stateful JWT with Redis

Introduction: The Problem with Purely Stateless JWTs

Section titled “Introduction: The Problem with Purely Stateless JWTs”

Standard JWT authentication is stateless — the server doesn’t store anything after issuing a token. Verification is done by checking the token’s signature and expiration.

While simple and scalable, this approach has one critical drawback: there is no way to forcefully invalidate a token. If a JWT is compromised, or a user wants to log out from all devices, the token remains valid until it naturally expires.

This document outlines a hybrid, stateful architecture that combines the convenience of JWTs with the control of server-side sessions, using Redis as the session store.


  • JWT (JSON Web Token): Remains the primary method for authenticating API requests. It carries user information (sub, username) and — crucially — a unique Session ID in the jti (JWT ID) claim.

  • CSRF Token: A random, unique token used to prevent Cross-Site Request Forgery attacks. It acts as a second secret that an attacker’s website cannot guess or access.

  • Redis: A high-performance, in-memory key-value store used as the source of truth for active sessions. If a session record exists in Redis, the user is logged in. If not, they are logged out.

  • Session ID (jti): A unique identifier (e.g., a UUID) embedded within the JWT. This ID serves as the Redis key, linking a specific JWT to its session record.


+--------+ +-----------------+ +---------+
| Client | <-----> | NestJS API | <-----> | Redis |
+--------+ +-----------------+ +---------+
| | |
|--- 1. Login -------->| |
| |--- 2. Create Session ->|
|<-- 3. JWT & CSRF ----| |
| | |
|--- 4. API Request -->| |
| (JWT + CSRF Header) |--- 5. Verify in Redis->|
| |<-- 6. Session OK ------|
|<-- 7. API Response --| |

  1. Authentication: The user submits credentials (e.g., username/password).
  2. Token Generation: Upon successful authentication, the API generates three pieces of data:
    • A unique Session ID (uuidv4())
    • A secure, random CSRF Token
    • A JWT containing the user’s details and the Session ID in the jti claim
  3. Store Session in Redis: The API creates a JSON object containing userId and the generated csrfToken. This object is stored in Redis using the Session ID as the key (SET "session:<SessionID>" "{...data}"). A TTL is set on this key, matching the JWT’s expiration time.
  4. Send Tokens to Client: The API returns the JWT and the CSRF Token. The client stores them securely (JWT in memory, CSRF token in memory).

B. Authenticated Request Flow (Verification)

Section titled “B. Authenticated Request Flow (Verification)”

Client Request: For every protected API call, the client sends two things:

  • The JWT in the Authorization: Bearer <token> header
  • The CSRF Token in a custom header (e.g., X-CSRF-TOKEN)

API Verification (Guard):

  1. Extract the JWT and CSRF token from the headers.
  2. Decode the JWT (without fully verifying the signature) to extract the Session ID (jti).
  3. Query Redis using the jti (GET "session:<jti>"):
    • Check 1 — Session Existence: If the key does not exist, the session is invalid (logged out or expired). Reject with 401 Unauthorized.
    • Check 2 — CSRF Token Match: If the key exists, compare the stored csrfToken with the X-CSRF-TOKEN header. Mismatch = potential CSRF attack. Reject with 401 Unauthorized.
    • Check 3 — JWT Signature: If both checks pass, fully verify the JWT’s signature and expiration.
  4. If all checks pass, process the request.

  1. Client Request: The client sends a request to the /logout endpoint with valid JWT and CSRF token.
  2. API Action: Runs the same verification guard.
  3. Invalidate Session: Extracts the Session ID (jti) from the JWT and issues DEL "session:<jti>" to Redis.
  4. Result: The session is immediately deleted. Any subsequent attempt to use the same JWT fails at Check 1, effectively logging the user out.

  • Immediate Token Revocation: Log users out instantly (individually or from all devices) by deleting their session keys from Redis. Critical for “Log out from all other devices” features.
  • Enhanced CSRF Protection: Validating the CSRF token against a server-side store ensures its integrity on every request.
  • Centralized Session Management: A single source of truth for all active user sessions, enabling monitoring and auditing.
  • Scalability: Redis is designed for high-throughput, making this solution scalable for large user bases.

Trade-offDetail
Increased ComplexityMore complex than a purely stateless JWT implementation
Redis DependencyRedis becomes a critical authentication dependency — if Redis is down, no one can log in or validate tokens
Slightly Higher LatencyEach authenticated request requires a Redis lookup, adding a few milliseconds — negligible in practice