1. Set Up Your Node.js Application:
Ensure you have a Node.js application set up with a web framework like Express.js. If not, you can create a basic Express.js application:
npm install express
2. Install Required Packages:
You'll need several npm packages to implement JWT-based authentication with refresh tokens and Redis:
npm install express express-validator jsonwebtoken bcrypt redis
3. Create a Configuration File:
Create a configuration file to store secret keys, token expiration times, and other sensitive information. It's crucial to keep these values secret and not hardcode them in your code.
// config.js
module.exports = {
jwtSecret: 'your-secret-key',
jwtExpiration: '15m', // Token expiration time
refreshTokenExpiration: '7d', // Refresh token expiration time
redisUrl: 'redis://localhost:6379', // Redis server URL
};
4. Implement User Authentication:
Set up user authentication logic, typically involving username and password validation. Use bcrypt to securely hash and compare passwords. When a user successfully logs in, issue a JWT and a refresh token.
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const config = require('./config');
const redis = require('redis');
const client = redis.createClient(config.redisUrl);
const app = express();
// Authenticate user and issue tokens
app.post('/login', async (req, res) => {
const { username, password } = req.body;
// Check user credentials (replace with your logic)
if (validUser) {
const user = { id: 1, username: 'user1' }; // Replace with your user object
// Create a JWT
const accessToken = jwt.sign(user, config.jwtSecret, { expiresIn: config.jwtExpiration });
// Create a refresh token
const refreshToken = jwt.sign(user, config.jwtSecret, { expiresIn: config.refreshTokenExpiration });
// Store the refresh token in Redis
client.set(username, refreshToken);
res.json({ accessToken, refreshToken });
} else {
res.status(401).json({ message: 'Invalid credentials' });
}
});
// Other routes and middleware for protected endpoints
5. Protect Routes with JWT Middleware:
Use JWT middleware to protect routes that require authentication. This middleware verifies the JWT and extracts the user information.
const jwtMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) return res.status(401).json({ message: 'Access denied' });
try {
const user = jwt.verify(token, config.jwtSecret);
req.user = user;
next();
} catch (error) {
res.status(400).json({ message: 'Invalid token' });
}
};
// Protect routes with JWT middleware
app.get('/protected', jwtMiddleware, (req, res) => {
// Access the user via req.user
res.json({ message: 'Protected resource', user: req.user });
});
6. Implement Token Refresh:
Allow users to refresh their access tokens using refresh tokens. Create an endpoint that accepts a refresh token and issues a new access token.
app.post('/refresh-token', (req, res) => {
const { refreshToken } = req.body;
// Verify the refresh token
jwt.verify(refreshToken, config.jwtSecret, (err, user) => {
if (err) {
return res.status(401).json({ message: 'Invalid refresh token' });
}
// Check if the refresh token is still valid in Redis
client.get(user.username, (err, storedToken) => {
if (err || refreshToken !== storedToken) {
return res.status(401).json({ message: 'Invalid refresh token' });
}
// Issue a new access token
const accessToken = jwt.sign(user, config.jwtSecret, { expiresIn: config.jwtExpiration });
res.json({ accessToken });
});
});
});
7. Handle Token Revocation:
To handle token revocation (e.g., when a user logs out or changes their password), you can remove the associated refresh token from Redis.
app.post('/logout', jwtMiddleware, (req, res) => {
// Remove the refresh token from Redis
client.del(req.user.username, (err, reply) => {
if (err || !reply) {
return res.status(500).json({ message: 'Error revoking token' });
}
res.json({ message: 'Token revoked' });
});
});