javascript 108 lines · 3 tabs

JWT Authentication Middleware in Express That Populates req.user

Shared by codesnips Jul 2026
3 tabs
const jwt = require('jsonwebtoken');

const SECRET = process.env.JWT_SECRET;
const ALGORITHM = 'HS256';
const ACCESS_TTL = '15m';

class AuthError extends Error {
  constructor(message, status = 401) {
    super(message);
    this.name = 'AuthError';
    this.status = status;
  }
}

function signAccessToken(user) {
  return jwt.sign(
    { role: user.role },
    SECRET,
    { subject: String(user.id), algorithm: ALGORITHM, expiresIn: ACCESS_TTL }
  );
}

function verifyAccessToken(token) {
  try {
    return jwt.verify(token, SECRET, { algorithms: [ALGORITHM] });
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      throw new AuthError('Access token expired', 401);
    }
    throw new AuthError('Invalid access token', 401);
  }
}

module.exports = { signAccessToken, verifyAccessToken, AuthError };
3 files · javascript Explain with highlit

This snippet shows how a stateless authentication layer is wired into an Express application using JSON Web Tokens. Rather than looking up a session on every request, the server trusts a signed token supplied by the client, verifies its signature, and attaches the decoded identity to req.user so downstream handlers can act on it without repeating the verification logic.

The token service centralises everything related to signing and verifying. signAccessToken embeds a sub claim plus a role, sets a short expiry, and pins the algorithm to HS256 so a caller cannot downgrade to none. verifyAccessToken mirrors those constraints by passing algorithms explicitly to jwt.verify, which is a common pitfall: without it, a forged token declaring alg: none could pass. Errors from the jsonwebtoken library are normalised into an AuthError carrying an HTTP status, so the middleware never has to branch on library-specific error classes.

In authenticate middleware, the requireAuth function extracts the bearer token from the Authorization header, rejecting anything that does not match the Bearer <token> shape before touching the crypto. On success it constructs a lean req.user object containing only the id and role — deliberately not the full decoded payload — which keeps handlers from depending on incidental claims. The companion requireRole is a higher-order guard: it returns a middleware closure so routes can declare requireRole('admin') inline, and it assumes requireAuth already ran, returning 401 versus 403 to distinguish "not logged in" from "logged in but not allowed".

The routes tab ties it together. Public routes like POST /login issue a token, while router.use(requireAuth) applies authentication to every route declared after it, so /me and the admin route are protected without per-route repetition. This ordering matters: middleware placement is how Express scopes protection.

The trade-off of this stateless design is that tokens cannot be revoked before they expire, which is why the expiry is kept short and why a refresh-token flow usually accompanies it. It suits APIs where horizontal scaling makes server-side sessions awkward.


Related snips

Share this code

Here's the card — post it anywhere.

JWT Authentication Middleware in Express That Populates req.user — share card
Link copied