Authentication is a critical part of nearly every web application. One of the most popular methods today is using JWT (JSON Web Tokens) — a compact, secure way to verify user identity without storing session data on the server.
We’ll walk through how to implement JWT-based authentication in a Node.js + Express.js app.
Step 1: Set Up Your Express Project
First, create Node.js project using the below commands:
mkdir jwt-auth-demo && cd jwt-auth-demo
npm init -y
npm install express jsonwebtoken dotenv
You may also want to install nodemon for automatic server reloads during development.
Step 2: Create Basic Server Structure
Create a file named index.js file and also create a basic server:
// index.js
import express from 'express';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Make sure to create a .env file:
PORT=3000
JWT_SECRET=mySuperSecretKey
Step 3: Generate JWT on Login
Let’s assume a basic login system. You’ll typically verify user credentials from a database. For simplicity, we’ll hard-code a dummy user.
// authController.js
import jwt from 'jsonwebtoken';
export const login = (req, res) => {
const { email, password } = req.body;
// Mock user check — in real apps, validate against DB
if (email === 'test@example.com' && password === 'password123') {
const token = jwt.sign(
{ userId: '12345', email },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
return res.json({ success: true, token });
} else {
return res.status(401).json({ success: false, message: 'Invalid credentials' });
}
};
Now set up a route for login:
// routes.js
import express from 'express';
import { login } from './authController.js';
const router = express.Router();
router.post('/login', login);
export default router;
Update index.js to use the route:
import routes from './routes.js';
app.use('/api', routes);
Step 4: Create JWT Middleware to Protect Routes
This middleware will verify the token and attach user info to the request:
// middleware/userAuth.js
import jwt from 'jsonwebtoken';
const userAuth = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ success: false, message: 'Unauthorized' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(403).json({ success: false, message: 'Invalid or expired token' });
}
};
export default userAuth;
Step 5: Protect Routes Using the Middleware
Add a protected route that only logged-in users can access:
// routes.js
import userAuth from './middleware/userAuth.js';
router.post('/testing', userAuth, (req, res) => {
res.json({ success: true, message: `This is a testing Route.` });
});
Step 6: Test the API
- Login by sending a POST request to /api/login with valid credentials:
{
"email": "test@example.com",
"password": "password123"
}
- Copy the returned token.
- Access protected route /api/testing by adding the token in the request header:
Authorization: Bearer
If the token is valid, you’ll get a valid response.
Summary
JWT authentication in Express.js is a powerful and scalable way to protect your APIs. By storing user information securely in the token and verifying it on each request, you eliminate the need for traditional server-side session handling.



