Building a Token Blacklisting System with Redis Cloud in Node.js

Sibabale Joja - Nov 5 - - Dev Community

Building a Token Blacklisting System with Redis Cloud in Node.js

Photo by [Mohammad Rahmani](https://unsplash.com/@afgprogrammer?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) on [Unsplash](https://unsplash.com/photos/black-flat-screen-computer-monitor-CDBkMNZmd7o?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash)

In this guide, we’ll walk through creating a token blacklisting system in a Node.js application. Our approach will use Redis Cloud for storing blacklisted tokens, ensuring tokens cannot be reused after logout. Redis Cloud provides a convenient way to manage our Redis setup without hosting and managing the infrastructure ourselves.

Why Token Blacklisting?

Token-based authentication is widely used in modern applications, and JSON Web Tokens (JWTs) are a popular choice. However, JWTs remain valid until they expire, even after a user logs out. To address this, we can “blacklist” tokens upon logout, ensuring they cannot be reused.

Setting Up Redis Cloud

  1. Create a Redis Cloud account: Go to Redis Cloud and sign up or log in.

  2. Create a Redis instance: Create a Redis database instance, if one is not created. After setup, you’ll be provided with:

  • Public endpoint: e.g., (redis-12345.c56.us-east-1–3.ec2.cloud.redislabs.com:18285)

  • Port: This will be the last 5 digits on your Public endpoint. Cut them and delete the semicolon (we will use it later).

  • Password: Scroll down to the security section and copy the Default user password (we will use it later)

  1. Store credentials securely For security, never hardcode these details in your code. Instead, we’ll use environment variables to load them dynamically.

Configuring Redis in Node.js

First, initialize the project

mkdir token-blacklist
cd token-blacklist
npm init -y
Enter fullscreen mode Exit fullscreen mode

Next, install the necessary packages:

npm install dotenv ioredis express
Enter fullscreen mode Exit fullscreen mode

Then, ensure you have this project structure:

/token-blacklist
│
├── /config                   # Configuration files
│   └── redis-client.js       # Redis client configuration
│
├── /controllers              # Controllers handling requests and responses
│   └── logOutController.js   # Controller for logging out and blacklisting tokens
│
├── /middleware               # Middleware functions for request processing
│   └── authMiddleware.js     # Middleware for checking token validity and blacklisting
│
├── .env                      # Environment variables (Redis credentials, etc.)
├── package.json              # Project metadata and dependencies
├── package-lock.json         # Exact versions of installed dependencies
└── node_modules/             # Installed Node.js modules
Enter fullscreen mode Exit fullscreen mode

Then, set up environment variables in a .env file to securely store the Redis credentials:

REDIS_HOST=redis-12345.c56.us-east-1–3.ec2.cloud.redislabs.com
REDIS_PORT=18285
REDIS_PASSWORD=your_secure_password
Enter fullscreen mode Exit fullscreen mode

Now, configure the Redis client to connect to Redis Cloud in your config/redis-client.js file:

const dotenv = require('dotenv');
const Redis = require('ioredis');

dotenv.config();

const redisClient = new Redis({
    host: process.env.REDIS_HOST,
    port: parseInt(process.env.REDIS_PORT),
    password: process.env.REDIS_PASSWORD,
});

redisClient.on('connect', () => {
    console.log('Connected to Redis Cloud');
});

redisClient.on('error', (error) => {
    console.error('Redis Client Error:', error);
});

// Optional: Test the connection to ensure it's working
redisClient
    .ping()
    .then((result) => {
    console.log('Ping response:', result);
    })
    .catch((error) => {
    console.error('Error pinging Redis:', error);
    });

module.exports =  redisClient;
Enter fullscreen mode Exit fullscreen mode

Explanation of Optional Logs:

- Connect Log: Prints to the console “Connected to Redis Cloud” when the Redis client successfully connects.

  • Error Log: Logs any Redis connection errors.
  • Ping Test: This step pings Redis on startup to confirm the connection is active. If successful, it logs the response; otherwise, it logs the error.

Building the Blacklisting Controller

Now, we’ll create our controller in thecontrollers/logoutContoller.js file to blacklist tokens. Upon logout, the token will be stored in Redis with a 5-minute expiration time (TTL), after which Redis automatically deletes it.

const redisClient = require('../config/redis-client');

const LogOutController = async (req, res) => {
    try {
        // Extract token from headers
        const token = req.headers.authorization?.split('Bearer ')[1];

        if (!token) {
          return res.status(401).json({ message: 'No token provided' });
        }

        // Blacklist the token with a TTL of 5 minutes (300 seconds)
        const ttl = 300; // 5 minutes in seconds
        await redisClient.setex(token, ttl, 'blacklisted');

        res.status(200).json({ message: 'Logout successful' });
   } catch (error) {
       console.error('Error in LogOutController:', error);
       res.status(500).json({ message: 'Could not blacklist token' });
   }
};

module.exports = LogOutController;
Enter fullscreen mode Exit fullscreen mode

This controller simply takes the token from the request, and if provided, stores it in Redis with a TTL, effectively blacklisting it for a specified period.

Creating Auth Middleware

To secure routes, we’ll create a middleware/authMiddleware.js file that checks if a token is blacklisted. Any blacklisted token will immediately cause the middleware to block access.

const redisClient = require('../config/redis-client');

const authMiddleware = async (req, res, next) => {
 try {
    const token = req.headers.authorization?.split('Bearer ')[1];

    if (!token) {
      return res.status(401).json({ message: 'No token provided' });
    }
    // Check if token is blacklisted
    const isBlacklisted = await redisClient.get(token);
    if (isBlacklisted) {
       return res.status(401).json({ message: 'Token has been blacklisted' });
    }
    // You can add token verifcation logic here
    next();
  } catch (error) {
     console.error('Authorization error:', error);
     res.status(500).json({ message: 'Authentication failed' });
  }
};

module.exports = authMiddleware;
Enter fullscreen mode Exit fullscreen mode

Applying Token Blacklisting to Routes

Here we ensure all routes with sensitive information are protected.

const express = require('express');
const authMiddleware = require('./middleware/authMiddleware');
const LogOutController = require('./controllers/logoutController');

const app = express();
app.use(express.json());

// Public route
app.post('/logout', LogOutController);

// Protected route
app.get('/secure-data', authMiddleware, (req, res) => {
    res.json({ message: 'This is secured data!' });
});

app.listen(3000, () => {
    console.log('Server running on http://localhost:3000');
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

With Redis Cloud, we have an efficient, cloud-based token blacklisting system for token-based authentication. By storing tokens with a TTL, we ensure they expire without manual cleanup, and our middleware ensures that blacklisted tokens are denied access, keeping our system secure.

With this setup, you’ve created a secure and scalable token blacklisting solution using Redis Cloud in Node.js.

.
Terabox Video Player