Concept
Authentication (Who are you?) vs Authorization (What can you do?)
- Authentication (AuthN): Validating credentials (like password, token, or session cookie) to verify the user is who they claim to be.
- Authorization (AuthZ): Verifying if the authenticated user has permissions to access a specific route or execute a write command.
Building JWT Authentication Middleware
In REST APIs, JWT tokens are sent in the HTTP Authorization header prefixed with Bearer . Express middleware verifies this token and attaches the user payload to the request object:
import jwt from 'jsonwebtoken';
export function authenticateJWT(req, res, next) {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.sendStatus(403); // Forbidden (invalid token)
}
req.user = user; // Attach user to request context
next();
});
Role-Based Access Control (RBAC)
Once authenticated, use secondary middleware guards to enforce user roles (like checking for admin status):
// Middleware factory for authorization
export function authorizeRoles(...allowedRoles) {
return (req, res, next) => {
if (!req.user) {
return res.sendStatus(401);
}
if (allowedRoles.includes(req.user.role)) {
next(); // Access allowed
} else {
res.sendStatus(403); // Forbidden (insufficient roles)
}
};
}
// Route guarded by authentication and role-based checks
app.delete('/users/:id', authenticateJWT,
Common Mistakes
1. Storing passwords in plain text or using weak hashing
Storing passwords directly in the database, or using outdated fast hash algorithms like MD5 or SHA256 without salts, makes databases vulnerable to rainbow table attacks. Always use slow-hashing algorithms like bcrypt or argon2.
2. Leaking authorization verification steps inside controllers
Do not manually parse JWT tokens inside every single route controller function. This duplicates code and leads to security bugs when developers forget to add validation blocks. Extract token parsing into a reusable middleware module.
Best Practices
- Use Slow Password Hashing: Hash passwords using bcrypt with a cost factor (e.g. 10 or 12 rounds) or argon2 before saving.
- Validate Tokens Securely: Read secrets directly from environment variables (
process.env.JWT_SECRET) rather than hardcoding them in files. - Set Token Expirations: Shorten JWT lifespans (e.g. 15 minutes) and issue secure, HTTP-only cookie-based refresh tokens for session renewals.
