Auth Middleware Pattern
On this page
Middleware as a Security Boundary
Authentication must happen before business logic. The middleware should validate credentials, construct a trusted principal, attach it to the request, and enforce failure behavior consistently.
Attach a Principal, Not Raw Claims
Do not spread token claims across the app. Create a normalized user context (id, roles, permissions, tenant) and attach it to the request in one place.
Express Middleware Example
import type { Request, Response, NextFunction } from 'express';
type Principal = { userId: string; roles: string[] };
export function requireAuth(req: Request, res: Response, next: NextFunction) {
const auth = req.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
return res.status(401).json({ error: { code: 'UNAUTHORIZED', message: 'Missing token' } });
}
const token = auth.slice('Bearer '.length);
// verifyToken should validate signature + exp and return trusted claims
const claims = verifyToken(token);
(req as any).principal = { userId: claims.sub, roles: claims.roles ?? [] } satisfies Principal;
next();
}
Fail Closed
If verification fails, return 401/403 and stop processing. Do not continue with partial identity. Always log authentication failures with requestId and coarse reason (avoid leaking detail).
Production Details
- Validate token expiration and issuer/audience
- Enforce clock skew tolerance intentionally
- Protect against token replay where applicable
- Prefer constant-time comparisons for secrets