JWT refresh tokens in a Node.js and Express

JWT refresh tokens in a Node.js and Express

ยท

3 min read

Implementing JWT refresh tokens in a Node.js and Express application provides a more secure and scalable authentication flow. Below is a step-by-step guide to enhance your existing authentication system with JWT refresh tokens.

Step 1: Set Up Your Project

  1. Make sure you have your existing Node.js and Express application with user authentication using JWTs.

Step 2: Install Necessary Packages

Install the jsonwebtoken and uuid packages to handle JWT operations and generate refresh token identifiers.

npm install jsonwebtoken uuid

Step 3: Modify User Model

Add a field for storing refresh tokens in your user model. For example, in your models/user.js:

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  // Your existing fields

  // New field for storing refresh tokens
  refreshTokens: [{ type: String }],
});

const User = mongoose.model('User', userSchema);

module.exports = User;

Step 4: Generate Refresh Token on Login

When a user logs in and receives a new JWT access token, also generate and store a refresh token in the user's document. Modify your authentication route (routes/auth.js or similar) to include refresh token generation:

const express = require('express');
const jwt = require('jsonwebtoken');
const uuid = require('uuid');
const User = require('../models/user');

const router = express.Router();

router.post('/login', async (req, res) => {
  // Your existing login logic

  // Generate a refresh token
  const refreshToken = uuid.v4();
  user.refreshTokens.push(refreshToken);
  await user.save();

  // Generate and send the new access token and refresh token to the client
  const accessToken = generateAccessToken(user);
  res.json({ accessToken, refreshToken });
});

function generateAccessToken(user) {
  // Your existing access token generation logic
}

module.exports = router;

Step 5: Implement Refresh Token Route

Create a new route for handling token refresh. This route will receive a refresh token, verify it, and send back a new access token:

const express = require('express');
const jwt = require('jsonwebtoken');
const User = require('../models/user');

const router = express.Router();

router.post('/refresh-token', async (req, res) => {
  const { refreshToken } = req.body;

  // Verify the refresh token
  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, async (err, user) => {
    if (err) {
      return res.sendStatus(403);
    }

    // Check if the refresh token is still valid for this user
    const dbUser = await User.findById(user._id);

    if (!dbUser || !dbUser.refreshTokens.includes(refreshToken)) {
      return res.sendStatus(403);
    }

    // Generate a new access token
    const accessToken = generateAccessToken(dbUser);

    res.json({ accessToken });
  });
});

function generateAccessToken(user) {
  // Your existing access token generation logic
}

module.exports = router;

Step 6: Handle Token Refresh on the Client

In your client-side code, implement a mechanism to refresh the access token using the refresh token. When the current access token expires, send a request to the /refresh-token endpoint with the stored refresh token:

async function refreshAccessToken(refreshToken) {
  try {
    const response = await fetch('/auth/refresh-token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ refreshToken }),
    });

    if (!response.ok) {
      throw new Error('Failed to refresh access token');
    }

    const { accessToken } = await response.json();
    // Use the new access token in your application
    return accessToken;
  } catch (error) {
    console.error('Error refreshing access token:', error);
    // Handle error or redirect to login
  }
}

Step 7: Test the Implementation

  1. Start your Express server.

     node server.js
    
  2. Test the login endpoint to get an access token and refresh token.

  3. When the access token expires, use the refresh token to get a new access token by calling the /refresh-token endpoint.

  4. Handle token refresh errors appropriately, such as redirecting the user to the login page.

Congratulations! You've successfully implemented JWT refresh tokens in your Node.js and Express authentication flow. This enhances security by reducing the exposure of long-lived access tokens and provides a way to revoke access on the server side. Customize and expand this implementation based on your specific requirements.

Did you find this article valuable?

Support Revive Coding by becoming a sponsor. Any amount is appreciated!

ย