Middleware de autenticación y autorización
Autenticación (¿quién eres?) y autorización (¿qué puedes hacer?) son dos pasos distintos — el primero identifica al usuario, el segundo decide si ese usuario específico puede hacer la acción que está pidiendo. En Express, ambos se implementan como middlewares en la cadena, antes del handler de la ruta.
Middleware de autenticación: poblar req.user
Sección titulada «Middleware de autenticación: poblar req.user»import type { NextFunction, Request, Response } from "express"import { verificarToken } from "../lib/jwt"
// Extender el tipo de Request para que TypeScript conozca req.userdeclare global { namespace Express { interface Request { user?: { id: string; rol: string } } }}
export function requireAuth(req: Request, res: Response, next: NextFunction) { const token = req.cookies?.token ?? req.headers.authorization?.replace("Bearer ", "")
if (!token) { return res.status(401).json({ error: "No autenticado" }) }
try { const payload = verificarToken(token) req.user = { id: payload.sub, rol: payload.rol } next() } catch { return res.status(401).json({ error: "Token inválido o expirado" }) }}app.get("/perfil", requireAuth, (req, res) => { res.json({ userId: req.user!.id }) // req.user existe porque requireAuth ya corrió})Aceptar el token desde una cookie (req.cookies.token) o un header Authorization: Bearer <token> cubre dos clientes habituales: navegador del mismo producto y cliente API. Consulta Cookies vs sesiones para evaluar seguridad y transporte.
Middleware de autorización: chequeo de rol
Sección titulada «Middleware de autorización: chequeo de rol»Una vez que req.user existe (gracias a requireAuth), un segundo middleware puede decidir si ese rol específico tiene permiso:
import type { NextFunction, Request, Response } from "express"
export function requireRole(...rolesPermitidos: string[]) { return (req: Request, res: Response, next: NextFunction) => { if (!req.user || !rolesPermitidos.includes(req.user.rol)) { return res.status(403).json({ error: "No tienes permiso para esto" }) } next() }}app.delete( "/usuarios/:id", requireAuth, // primero: ¿quién eres? requireRole("admin"), // segundo: ¿puedes hacer esto? handlerDeEliminarUsuario)requireRole es una fábrica de middleware (una función que devuelve un middleware) — permite parametrizar qué roles se aceptan por ruta, en vez de un middleware fijo para cada combinación posible.
401 vs 403: la diferencia importa
Sección titulada «401 vs 403: la diferencia importa»401 Unauthorized → "no sé quién eres" (falta o es inválido el token)403 Forbidden → "sé quién eres, pero no puedes hacer esto" (rol insuficiente)Confundirlos no rompe nada técnicamente, pero da información engañosa al cliente sobre qué está fallando — requireAuth siempre debería devolver 401, requireRole siempre 403.
Flujo de protección
Sección titulada «Flujo de protección»| Middleware | Responde |
|---|---|
requireAuth | ¿Hay un token válido? Puebla req.user |
requireRole(...roles) | ¿El req.user actual tiene uno de estos roles? |
| Orden en la ruta | Siempre requireAuth antes que requireRole — no se puede chequear rol sin saber antes quién es |
Autenticación no es autorización
Sección titulada «Autenticación no es autorización»requireRoledepende de quereq.userya exista — usarlo sinrequireAuthantes en la misma cadena es un bug (siempre entra alif (!req.user ...)y responde 403 a todo el mundo).- Este patrón (rol único por usuario,
req.user.rol === 'admin') alcanza para la mayoría de los casos; sistemas con permisos más finos (varios roles por usuario, permisos por recurso específico) necesitan un modelo más elaborado — ver Roles y permisos.