How to implement JWT authentication in Express.js?
JWT Authentication in Express.js
JWT (JSON Web Token) is a compact, self-contained token used for stateless authentication. It eliminates the need for server-side sessions.
How JWT Works
1. User logs in (POST /auth/login)
2. Server verifies credentials → generates JWT
3. Client stores JWT (localStorage / httpOnly cookie)
4. Client sends JWT in Authorization header for every request
5. Server verifies JWT → grants accessJWT Structure
eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJBbGljZSJ9.signature
Header Payload SignatureSetup
bash
npm install jsonwebtoken bcryptjsLogin Route (Issue Token)
js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const JWT_SECRET = process.env.JWT_SECRET; // store in env!
const JWT_EXPIRES_IN = '7d';
app.post('/auth/login', async (req, res, next) => {
try {
const { email, password } = req.body;
// 1. Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 2. Verify password
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 3. Sign JWT
const token = jwt.sign(
{ id: user.id, email: user.email, role: user.role },
JWT_SECRET,
{ expiresIn: JWT_EXPIRES_IN }
);
res.json({ token, expiresIn: JWT_EXPIRES_IN });
} catch (err) {
next(err);
}
});Auth Middleware (Verify Token)
js
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded; // { id, email, role, iat, exp }
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(401).json({ error: 'Invalid token' });
}
}
// Role-based authorization
function authorize(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}Protected Routes
js
// Any authenticated user
app.get('/profile', authenticate, (req, res) => {
res.json({ user: req.user });
});
// Only admins
app.delete('/users/:id', authenticate, authorize('admin'), async (req, res) => {
await User.deleteById(req.params.id);
res.status(204).send();
});Registration with Password Hashing
js
app.post('/auth/register', async (req, res, next) => {
try {
const { email, password } = req.body;
const exists = await User.findOne({ email });
if (exists) return res.status(409).json({ error: 'Email already taken' });
const passwordHash = await bcrypt.hash(password, 12); // 12 salt rounds
const user = await User.create({ email, passwordHash });
res.status(201).json({ id: user.id, email: user.email });
} catch (err) {
next(err);
}
});Refresh Tokens Pattern
js
// Access token: short-lived (15min)
const accessToken = jwt.sign(payload, JWT_SECRET, { expiresIn: '15m' });
// Refresh token: long-lived (7d), stored in httpOnly cookie
const refreshToken = jwt.sign(payload, REFRESH_SECRET, { expiresIn: '7d' });
res.cookie('refreshToken', refreshToken, {
httpOnly: true, // not accessible via JS
secure: true, // HTTPS only
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
});Summary
JWT authentication in Express involves:
- Login route → verify credentials → sign JWT
- Auth middleware → verify JWT → attach user to
req - Protected routes → use
authenticatemiddleware - Authorization → check roles after authentication
Always store the JWT secret in environment variables and use short expiry times with refresh tokens in production.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.