Write an Express middleware that checks a JWT and protects a route.
Read the Bearer token from the Authorization header, verify it with jwt.verify, attach the claims to req.user and call next. On a missing or invalid token, respond 401 without calling next.
Explain it simply
Auth middleware is the gate guard in front of a protected route. Each request must show a valid pass. If it does, the guard waves it through with next(). If not, the guard sends it back with 401 and the route never runs.
Example
const jwt = require("jsonwebtoken");
function requireAuth(req, res, next) {
const header = req.get("Authorization") || "";
const [scheme, token] = header.split(" ");
if (scheme !== "Bearer" || !token) {
return res.status(401).json({ error: "Missing token" });
}
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (err) {
const msg = err.name === "TokenExpiredError" ? "Token expired" : "Invalid token";
res.status(401).json({ error: msg });
}
}
function requireRole(role) {
return (req, res, next) =>
req.user?.role === role ? next() : res.status(403).json({ error: "Forbidden" });
}
app.get("/me", requireAuth, (req, res) => res.json({ id: req.user.sub }));
app.delete("/students/:id", requireAuth, requireRole("admin"), deleteStudent);Line by line
- The header looks like
Authorization: Bearer <token>, so we split on the space. jwt.verifythrows if the signature is wrong or the token is expired, so it sits in atry.- Setting
req.userlets later handlers know who is calling. requireRoleis a function returning a middleware, which is how you pass options to middleware. It answers 403, since the user is known but not allowed.
Complexity
Time O(1) per request (one signature check); no extra memory.
What to say in the interview
"I parse the Bearer header, verify the token inside a try/catch, set req.user, and call next. Failures give 401, and a separate role middleware gives 403 for authenticated users without permission."
Common mistakes
- Calling
next()after sending a 401, which runs the route anyway. - Using
jwt.decode, which does not check the signature.