DEV Community

Tolumide Shopein
Tolumide Shopein

Posted on

How to Invalidate JWT tokens on user logout with Redis

In this article, I will be sharing briefly how to invalidate JWT tokens on user logout (backend implementation) using Redis.

Redis is an in-memory data structure store that can be used as a database, message-broker, or cache. I will be using the sorted set data structure of Redis.

Sorted sets in Redis allows you to save a member and its score in the Redis sorted set.
Think of the sorted set as an object in javascript or dictionary in python, where the key is the score and the value is the member.

For the sake of our goal, I will be assuming the score of each member (in this case the JWT token) of the sorted set as the expiry date or time of such JWT token.

NB: This is not a full code implementation.

  1. Issue Token When the token is issued to the user on login/signup depending on your implementation. Save the token on Redis.

i. Create a token that expires within 24hrs:
Something that looks like this:

  //a. Ensure the user exist and the password is correct
  //b. Issue the token if (a) is okay

    const token = ({userId, email}) => {
      return jwt.sign({
        data: {id, email}
      }, `${process.env.SECRET}` , { expiresIn: '24h' });
    }
    //c. Add the token to redis using the userId as the token key
    const tokenKey = new Date(Date.now() + (1000*60*60*24));
    await redis.zadd(`${userId}-tokens`, `${tokenKey}`, token);

    //d. Send the token to the user

ii. Check and remove invalid tokens on user login (you could use a cron
job for this instead):
Get expired tokens for this user on Redis and remove them

    const expiredTokens = await redis.zrangebyscore(`${user.id}-tokens`, 
      `-inf`, Date.now());

      // Remove expired tokens on user login/use a cronjob
     expiredTokens.length >= 1 && expiredTokens.forEach(async token => {
       await redis.zrem(`${user.id}-tokens`, token)
    });
  1. Remove specific token on user logout: So what I want is to not log out all devices the user might be connected with (you might do this if you have a feature to logout all devices feature), we want to logout the specific device that has this token, and ensures the token can never be used again even if it has not expired yet.
    // Get the token sent from the frontend
    const {token} = args;
    const isTokenValid = await verifyToken(token)
    if(isTokenValid){
      const {userId} = isTokenValid.data
      const allTokens = await 
      redis.zrangebyscore(`${userId}-tokens`, `-inf`, 
        `+inf`);
         if (allTokens.includes(token)){
           await redis.zrem(`${validateToken.data.id}-tokens`, token);
           return Response({status: 'Success'})
         }
     }
    return Response({status: 'Failure'})

What this means: On every user request for content that might need authentication, you should ensure that such token is in the user's Redis store i.e ${userId}-tokens. If the token is not in the Redis store, then it is not a valid token anymore.

I do understand that this might not be a perfect implementation, I am therefore open to any constructive feedback.

Credits:

  1. How to logout when using jwt
  2. How to create and expire list-items in redis

Top comments (0)