Authorizing means answering "can you do this?"
Authentication says who you are; authorization decides what you are allowed to do. They are two distinct steps, and they map to two status codes:
- 401 Unauthorized: I don't know who you are (the token is missing or invalid).
- 403 Forbidden: I know who you are, but you don't have permission for this.
A very common mistake is returning 401 when 403 is what's needed. If the user is already authenticated and simply lacks the permission, it's 403.
Middleware that requires a valid token
The first middleware reads the Authorization header. If it's missing —or if the
token isn't valid because the signature doesn't match— it stops with 401. If
it's valid, it stores the user in req.user so the next steps know who it is.
function requireAuth(req, res, next) {
const header = req.headers.authorization;
// verifyToken returns the payload, or null if the signature doesn't match.
const user = header ? verifyToken(header, SECRET) : null;
if (!user) {
res.status(401).json({ error: "Unauthorized" });
return; // stops: doesn't call next()
}
req.user = user;
next();
}
In a real case, the header is "Bearer <token>": the scheme (Bearer) is split
from the token before verifying it. For the exercises we'll send the token
directly in the header to focus on the logic.
Role middleware
Role-based authorization is solved with a middleware factory: a function
that receives the required role and returns the concrete middleware. This way
you can write requireRole("admin") on any route.
function requireRole(role) {
return function (req, res, next) {
if (!req.user || req.user.role !== role) {
res.status(403).json({ error: "Forbidden" });
return;
}
next();
};
}
They are chained in order: first requireAuth (so that req.user exists), then
requireRole("admin"):
app.delete("/users/:id", requireAuth, requireRole("admin"), deleteUser);
Examples
Chaining authentication and role
function requireRole(role) {
return function (req, res, next) {
if (!req.user || req.user.role !== role) {
res.status(403).json({ error: "Forbidden" });
return;
}
next();
};
}
const onlyAdmin = requireRole("admin");
const req = { user: { role: "reader" } };
const res = {
status(c) { this.code = c; return this; },
json(b) { console.log(this.code, JSON.stringify(b)); },
};
onlyAdmin(req, res, () => console.log("access granted"));