Node.js
Auth & JWT – JSON Web Token Authentication
Implement stateless authentication using jsonwebtoken and bcrypt: sign access tokens, verify them in middleware, handle refresh tokens, and secure login routes.
Install: Add
jsonwebtoken for token signing/verification and bcryptjs for password hashing.terminal
BASH
npm install jsonwebtoken bcryptjs
Generate Token: Sign a JWT with a payload, secret, and expiry. Store the secret in
.env — never hard-code it.src/utils/generateToken.js
JS
const jwt = require('jsonwebtoken');
const generateAccessToken = (user) => {
return jwt.sign(
{ id: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '15m' }
);
};
const generateRefreshToken = (user) => {
return jwt.sign(
{ id: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
};
module.exports = { generateAccessToken, generateRefreshToken };
Verify Middleware: Extract the Bearer token from the
Authorization header, verify it, and attach the decoded user to req.user.src/middleware/authMiddleware.js
JS
const jwt = require('jsonwebtoken');
const protect = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer <token>
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
const message = err.name === 'TokenExpiredError'
? 'Token expired'
: 'Invalid token';
return res.status(403).json({ message });
}
};
const authorize = (...roles) => (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ message: 'Forbidden: insufficient role' });
}
next();
};
module.exports = { protect, authorize };
Password Hashing: Use
bcryptjs to hash passwords before storing them, and bcrypt.compare() to validate on login.src/utils/password.js
JS
const bcrypt = require('bcryptjs');
const hashPassword = async (plainText) => {
const salt = await bcrypt.genSalt(12); // cost factor
return bcrypt.hash(plainText, salt);
};
const comparePassword = async (plainText, hashed) => {
return bcrypt.compare(plainText, hashed);
};
module.exports = { hashPassword, comparePassword };
Login Route: Validate credentials, issue an access token and a refresh token, and return them to the client.
src/controllers/authController.js
JS
const { findUserByEmail } = require('../models/User');
const { comparePassword } = require('../utils/password');
const { generateAccessToken, generateRefreshToken } = require('../utils/generateToken');
const login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await findUserByEmail(email);
if (!user) return res.status(401).json({ message: 'Invalid credentials' });
const valid = await comparePassword(password, user.password);
if (!valid) return res.status(401).json({ message: 'Invalid credentials' });
const accessToken = generateAccessToken(user);
const refreshToken = generateRefreshToken(user);
// Store refreshToken in DB or httpOnly cookie
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'Strict',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
res.json({ accessToken, user: { id: user.id, email: user.email } });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
module.exports = { login };
Refresh Token Pattern: Validate the refresh token from the cookie and issue a new access token without requiring re-login.
src/controllers/authController.js (refresh)
JS
const jwt = require('jsonwebtoken');
const { generateAccessToken } = require('../utils/generateToken');
const refresh = (req, res) => {
const token = req.cookies?.refreshToken;
if (!token) return res.status(401).json({ message: 'No refresh token' });
try {
const decoded = jwt.verify(token, process.env.JWT_REFRESH_SECRET);
const newAccess = generateAccessToken({ id: decoded.id });
res.json({ accessToken: newAccess });
} catch {
res.status(403).json({ message: 'Invalid or expired refresh token' });
}
};
module.exports = { refresh };