Invalidating JWT Token
Invalidate JWT Token Using jwt-redis & Nest.js
Published On 2023-04-13

By Swasti Jain
INTRODUCTION
JWT stands for JSON Web Token — a compact, URL-safe way to securely transmit information between two parties (usually client and server) as a digitally signed token.
There are a few ways to invalidate a JSON Web Token (JWT).
1. Change the secret:
When you sign a JWT, you use a secret key. If you change the secret key, all existing JWTs will become invalid. This is the simplest way to invalidate JWTs, but it also requires all users to obtain a new token.
2. Add an expiration date:
You can add an expiration date to your JWTs. When the expiration date passes, the JWT will become invalid. This is a good option if you want to revoke access to a user's account after a certain period.
3. Blacklist the token:
You can create a blacklist of JWTs that have been revoked. When you receive a JWT, you can check the blacklist to see if it has been revoked. If it has, you can reject the request. This is a good option if you need to revoke access to a user's account immediately.
Use cases
There are multiple methods of invalidating a JWT token. Each way caters to a different use case, which is described here:
1. Invalidate all: If you need to invalidate all existing JWTs, then changing the secret is the simplest option.
2. Time bomb: If you want to revoke access to a user's account after a certain period, then adding an expiration date is a good option.
3. Invalidate specific token: If you need to revoke access to a user's account immediately, then blacklisting the token is the best option.
Here are some additional details about each method:
Changing the secret:
When you change the secret key, you need to update all of your code that generates or validates JWTs. This can be a time-consuming process, but it is the most secure way to invalidate JWTs.
Adding an expiration date:
When you add an expiration date to your JWTs, you need to make sure that the expiration date is in the future. If the expiration date is in the past, then the JWT will be invalid.
Blacklisting the token:
When you blacklist a JWT, you need to store the JWT in a database. When you receive a JWT, you can check the database to see if it has been blacklisted. If it has, you can reject the request.
It is important to note that JWTs are designed to be stateless. This
means that the server does not need to keep track of which users have
valid tokens. However, if you need to invalidate JWTs, then you will
need to keep track of which tokens have been invalidated. This can be
done by storing the invalidated tokens in a database or by using a
blacklist.
What about the latency of blacklist database lookup?
Can the database lookup be made so fast that the existence of this additional database doesn’t matter?
YES!Redis
is the answer. It can serve millions of requests in sub-milliseconds.
Invalidating JWT tokens with jwt-redis
Redis is a popular in-memory data store that is often used for caching and session management. jwt-redis
makes it easy to store JWT tokens in Redis so that you can easily invalidate them when they expire or are revoked. `jwt-redis` allows you to store the token label in Redis to verify validity. The absence of a token label in Redis makes the token not valid. To destroy the token in `jwt-redis`, there is a _destroy_ method. This makes it possible to make a token not valid until it expires. jwt-redis
supports the node_redis
client.
jwt-redis
uses the blacklist method (described above) behind the scenes.
To use jwt-redis
, you first need to install it with npm:
1npm install jwt-redis
Once you have installed jwt-redis
, you can start using it to store JWT tokens. Here is an example:
1import { createClient, RedisClientType } from 'redis';
2import JWTR from 'jwt-redis';
3import { Injectable, UnauthorizedException } from '@nestjs/common';
4import { createClient, RedisClientType } from 'redis';
5import { handleSendFallbackError } from 'src/utils/handleError'; // adjust path as needed
6
7@Injectable()
8export class LocalJwtService {
9 private redisClient: RedisClientType;
10 private secret: string;
11 private jwtr: JWTR;
12
13 constructor() {
14 this.redisClient = createClient();
15 this.secret = 'SECRET';
16
17 this.redisClient
18 .connect()
19 .then(() => {
20 this.jwtr = new JWTR(this.redisClient);
21 })
22 .catch((e) => {
23 console.error('Redis connection error:', e);
24 });
25 }
26
27 async sign(payload: any) {
28 try {
29 return await this.jwtr.sign(payload, this.secret);
30 } catch (e) {
31 console.error('JWT sign error:', e);
32 return await handleSendFallbackError(e, 'Failed to sign token');
33 }
34 }
35
36 async verify(payload: string) {
37 try {
38 await this.jwtr.verify(payload, this.secret);
39 return this.jwtr.decode(payload);
40 } catch (e) {
41 console.error('JWT verify error:', e);
42 return new UnauthorizedException();
43 }
44 }
45
46 async destroy(payload: string) {
47 try {
48 return await this.jwtr.destroy(payload);
49 } catch (e) {
50 console.error('JWT destroy error:', e);
51 return await handleSendFallbackError(e, 'Failed to destroy token');
52 }
53 }
54}
If you are using JWTs for authentication, then jwt-redis
can help you to improve the security of your application by making it easy to invalidate expired or revoked tokens.