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.
Core Concepts
Section titled “Core Concepts”-
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 thejti(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.
The Architecture: A High-Level View
Section titled “The Architecture: A High-Level View”+--------+ +-----------------+ +---------+| 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 --| |Detailed Workflows
Section titled “Detailed Workflows”A. User Login Flow
Section titled “A. User Login Flow”- Authentication: The user submits credentials (e.g., username/password).
- 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
jticlaim
- A unique Session ID (
- Store Session in Redis: The API creates a JSON object containing
userIdand the generatedcsrfToken. 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. - 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):
- Extract the JWT and CSRF token from the headers.
- Decode the JWT (without fully verifying the signature) to extract the Session ID (
jti). - 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
csrfTokenwith theX-CSRF-TOKENheader. Mismatch = potential CSRF attack. Reject with 401 Unauthorized. - Check 3 — JWT Signature: If both checks pass, fully verify the JWT’s signature and expiration.
- If all checks pass, process the request.
C. User Logout Flow
Section titled “C. User Logout Flow”- Client Request: The client sends a request to the
/logoutendpoint with valid JWT and CSRF token. - API Action: Runs the same verification guard.
- Invalidate Session: Extracts the Session ID (
jti) from the JWT and issuesDEL "session:<jti>"to Redis. - Result: The session is immediately deleted. Any subsequent attempt to use the same JWT fails at Check 1, effectively logging the user out.
Key Advantages
Section titled “Key Advantages”- 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-offs
Section titled “Trade-offs”| Trade-off | Detail |
|---|---|
| Increased Complexity | More complex than a purely stateless JWT implementation |
| Redis Dependency | Redis becomes a critical authentication dependency — if Redis is down, no one can log in or validate tokens |
| Slightly Higher Latency | Each authenticated request requires a Redis lookup, adding a few milliseconds — negligible in practice |