Skip to main content
Practice Problems

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 access

JWT Structure

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJBbGljZSJ9.signature Header Payload Signature

Setup

bash
npm install jsonwebtoken bcryptjs

Login 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:

  1. Login route → verify credentials → sign JWT
  2. Auth middleware → verify JWT → attach user to req
  3. Protected routes → use authenticate middleware
  4. 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems