How to Implement JWT Authentication in Node.js
This article provides a straightforward, step-by-step guide on
implementing JSON Web Token (JWT) authentication in a Node.js API. You
will learn how to secure your endpoints by setting up a basic Express
server, hashing passwords with bcryptjs, generating JWTs
upon successful user login, and creating a middleware function to
authorize requests to protected routes.
Step 1: Install the Required Dependencies
To get started, you need to initialize a Node.js project and install the necessary packages. Run the following commands in your terminal:
npm init -y
npm install express jsonwebtoken bcryptjs dotenv- express: The web framework for building the API.
- jsonwebtoken: The library used to sign and verify tokens.
- bcryptjs: Used for hashing and comparing user passwords securely.
- dotenv: To manage environment variables securely (like your JWT secret).
Step 2: Configure Environment Variables
Create a file named .env in the root of your project to
store your secret key and port configuration.
PORT=3000
JWT_SECRET=your_super_secret_key_change_this_in_production
Step 3: Set Up the Express Server and Mock Database
Create an app.js file. For simplicity, this guide uses
an in-memory array to mock a user database.
require('dotenv').config();
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const users = []; // Mock database
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));Step 4: Implement User Registration
Before a user can log in, they need to register. You must hash their
password using bcryptjs before saving it to the
database.
app.post('/register', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ message: 'Username and password are required' });
}
// Hash the password
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = { username, password: hashedPassword };
users.push(newUser);
res.status(201).json({ message: 'User registered successfully' });
} catch (error) {
res.status(500).json({ message: 'Error registering user' });
}
});Step 5: Implement User Login and JWT Generation
When a user logs in with valid credentials, the API generates a JWT. This token contains a payload (like the user’s identifier) and is signed using the secret key.
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Verify password
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Generate JWT
const token = jwt.sign(
{ username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
});Step 6: Create the Authentication Middleware
To protect routes, create a middleware function that extracts the JWT
from the Authorization header, verifies its validity, and
attaches the decoded payload to the request object.
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Format: "Bearer TOKEN"
if (!token) {
return res.status(401).json({ message: 'Access denied. No token provided.' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ message: 'Invalid or expired token.' });
}
req.user = user;
next();
});
};Step 7: Protect Private Routes
Apply the authenticateToken middleware to any API
endpoint that requires authentication.
app.get('/dashboard', authenticateToken, (req, res) => {
res.json({ message: `Welcome to your private dashboard, ${req.user.username}!` });
});To test your implementation, first register a user at
/register, log in at /login to receive your
JWT, and then make a GET request to /dashboard by passing
the token in the headers as
Authorization: Bearer <your_token>.