Token Revocation
Last updated
This page explains what RevokeJWT() does at logout, how the blacklist takes effect, and when nothing gets revoked.
Revocation Flow
| Step | Action |
|---|---|
| 1 | Resolve the Refresh ID: refresh_id argument → X-Refresh-ID header → refresh cookie; return immediately if none |
| 2 | Clear the access and refresh cookies |
| 3 | Read refresh:<id>; stop if it does not exist |
| 4 | Shorten the TTL of refresh:<id> to 5 seconds |
| 5 | Write revoke:<access_token> with value "1" and TTL accessTokenExpires |
How the Blacklist Takes Effect
The first thing VerifyJWT() does is look up revoke:<access_token>; on a hit it returns a guest (isError: false) without checking the signature. The blacklist TTL equals the Access Token lifetime, so the entry disappears once the token would have expired anyway.
When Nothing Is Revoked
| Situation | Result |
|---|---|
| The request carries no Refresh ID | Returns immediately: cookies are not cleared and nothing is blacklisted |
| The refresh data is already gone from Redis | Cookies are cleared, but the Access Token is not blacklisted and keeps passing verification until it expires |
| Redis error | Logged with console.error, not thrown |
To guarantee revocation, clients should send both the Access Token and the Refresh ID.
Example
import { Request, Response } from "express";
import { JWTAuth } from "@pardnchiu/jwt-auth";
export async function logout(req: Request, res: Response) {
try {
await JWTAuth.RevokeJWT(req, res);
res.json({ message: "Successfully logged out" });
} catch (err) {
// Throws only when not initialized
res.status(500).json({ error: (err as Error).message });
}
}
Related: Redis Keys, Verification Results