Middleware de autenticación y verificación de sesión
Overview
Este archivo define tres middlewares de seguridad para el backend de auth. Su función principal es validar tokens de acceso, limitar el acceso por rol y bloquear acciones cuando el correo aún no está verificado.
El archivo actúa como una frontera de autorización reutilizable. requireAuth construye req.auth a partir del JWT y de la consulta al modelo User. requireSystemRole y requireVerifiedEmail se apoyan en ese contexto ya autenticado. El repositorio muestra este middleware como pieza transversal de rutas protegidas, y también como apoyo para flujos de autenticación y cuenta
[!NOTE]
req.authsolo existe después de una validación exitosa. Las rutas posteriores deben tratarlo como un dato derivado del middleware, no como una entrada confiable por sí sola
How it works
El flujo central empieza con un encabezado Authorization. Si el valor no usa Bearer, el middleware rechaza la petición. Si el token existe, lo verifica con verifyAccessToken, confirma que pertenece a un user, y carga datos mínimos desde User para validar la sesión. Si todo encaja, adjunta la identidad en req.auth y continúa con next().
flowchart TB
Request[Peticion entrante] --> AuthMW[requireAuth]
AuthMW --> Header[Leer authorization]
Header --> Bearer{Bearer valido}
Bearer -- No --> Missing[ApiError 401 token no proporcionado]
Bearer -- Si --> Token[Extraer token]
Token --> Verify[verifyAccessToken]
Verify --> Subject{SubjectType user}
Subject -- No --> TypeError[ApiError 401 tipo no autorizado]
Subject -- Si --> UserLookup[User findById select lean]
UserLookup --> Active{Cuenta activa y version valida}
Active -- No --> SessionError[ApiError 401 sesion invalida]
Active -- Si --> AuthCtx[req auth]
AuthCtx --> Next[next]
AuthMW -. cualquier fallo .-> Catch[ApiError 401 token invalido o expirado]
El middleware no responde con res. Solo decide si la petición avanza o termina en next(error). Ese patrón permite combinarlo con otros guards, como el control por rol y la verificación de correo.
- Lee
req.headers.authorization. - Verifica que empiece con
Bearer. - Extrae el token con
replaceytrim. - Llama a
verifyAccessToken(token). - Comprueba que
payload.subjectTypeseauser. - Busca el usuario con
User.findById(payload.sub). - Valida que la cuenta esté activa.
- Compara
payload.tokenVersionconuser.tokenVersion. - Construye
req.auth. - Llama a
next().
Key components
{
"title": "requireAuth",
"content": "[badge:public] Middleware de Express que valida un JWT de acceso, confirma la sesi\u00f3n del usuario y deja `req.auth` listo para el siguiente handler.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.middleware.js",
"badges": [
"public"
]
}
{
"title": "requireSystemRole",
"content": "[badge:public] Factory de middleware que restringe el acceso seg\u00fan `req.auth.systemRole` y devuelve 403 si el rol no coincide.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.middleware.js",
"badges": [
"public"
]
}
{
"title": "requireVerifiedEmail",
"content": "[badge:public] Middleware que bloquea peticiones sin `emailVerifiedAt`, lanza un recordatorio en segundo plano y responde con 403 `EMAIL_NOT_VERIFIED`.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.middleware.js",
"badges": [
"public"
]
}
requireSystemRole devuelve una función interna llamada systemRoleGuard. Esa función usa allowedRoles.includes(req.auth.systemRole) para decidir el acceso. requireVerifiedEmail revisa req.auth.emailVerifiedAt y, si falta, dispara maybeSendVerificationReminder sin esperar su resultado.
Data & fields
El archivo trabaja con tres grupos de datos. El primero llega desde la petición. El segundo proviene del JWT verificado. El tercero se construye con datos del usuario cargado desde la base.
[
{
"name": "req.headers.authorization",
"type": "string",
"required": true,
"description": "Encabezado HTTP que debe comenzar con `Bearer `."
},
{
"name": "payload",
"type": "object",
"required": true,
"description": "Payload devuelto por `verifyAccessToken`.",
"children": [
{
"name": "sub",
"type": "string",
"required": true,
"description": "Identificador del usuario autenticado."
},
{
"name": "subjectType",
"type": "string",
"required": true,
"description": "Debe ser `user` para que el middleware acepte el token."
},
{
"name": "tokenVersion",
"type": "number",
"description": "Versión del token usada para invalidar sesiones antiguas."
}
]
},
{
"name": "req.auth",
"type": "object",
"description": "Contexto autenticado agregado por `requireAuth` cuando la validación termina bien.",
"children": [
{
"name": "userId",
"type": "string",
"required": true,
"description": "Valor tomado de `payload.sub`."
},
{
"name": "email",
"type": "string",
"required": true,
"description": "Correo del usuario cargado desde `User`."
},
{
"name": "subjectType",
"type": "string",
"required": true,
"description": "Valor fijo `user`."
},
{
"name": "systemRole",
"type": "string",
"required": true,
"description": "Rol global del usuario."
},
{
"name": "accountRole",
"type": "string",
"required": true,
"description": "Rol funcional de la cuenta."
},
{
"name": "tokenVersion",
"type": "number",
"required": true,
"description": "Versión efectiva del token confirmada contra el usuario."
},
{
"name": "emailVerifiedAt",
"type": "string or null",
"description": "Fecha de verificación del correo, o null si no existe."
}
]
},
{
"name": "allowedRoles",
"type": "string[]",
"description": "Lista variable de roles permitidos para `requireSystemRole`."
}
]
Campos clave en la validación
authorizationcontrola si la petición entra en la ruta protegida.subjectTypeevita usar tokens de otro tipo como si fueran de usuario.subidentifica al usuario a cargar.tokenVersionpermite invalidar sesiones antiguas.systemRoledecide el acceso enrequireSystemRole.emailVerifiedAtdecide el acceso enrequireVerifiedEmail.
Behaviors
requireAuth— Lee el token desdeAuthorization, verifica el JWT y carga el usuario asociado. Si todo coincide, asignareq.authy llama anext().requireAuth— Rechaza tokens ausentes, vacíos, con tipo incorrecto, con sesión inválida o con verificación fallida.requireSystemRole(...allowedRoles)— Devuelve un middleware que responde con 403 sireq.auth.systemRoleno está en la lista permitida.requireVerifiedEmail— Permite seguir solo cuandoreq.auth.emailVerifiedAtexiste.requireVerifiedEmail— Si falta el correo verificado, envía un recordatorio best-effort conmaybeSendVerificationReminder(req.auth.userId)y luego termina con 403.
Workflows
requireAuthvalida la presencia del encabezado.requireAuthextrae y verifica el JWT.requireAuthcomprueba que el token pertenezca a unuser.requireAuthconsultaUserpara validar cuenta y versión.requireAuthconstruyereq.auth.requireSystemRolecompara el rol actual conallowedRoles.requireVerifiedEmailbloquea escrituras cuando el correo no está verificado.
Gotchas & edge cases
[!WARNING]
requireAuthnormaliza cualquier fallo comoApiError(401, 'Token inválido o expirado'). Eso incluye ausencia de token, formato inválido, token no autorizado y cualquier error capturado en eltry.
[!CAUTION]
requireSystemRoledepende de quereq.authexista y tengasystemRole. SinrequireAuthantes, siempre termina en 403.
[!NOTE]
requireVerifiedEmailno espera el envío del recordatorio. Usavoid maybeSendVerificationReminder(...)para dispararlo en segundo plano.
[!IMPORTANT] El comentario del archivo ubica
requireVerifiedEmailen escrituras de Listings y enPOST /api/conversations/:id/refinements. También aclara que no se usa en lecturas, incluidoMatch, ni en el resto de conversaciones, comentarios, likes, contact events, leads o billing
[!WARNING] La comparación de
tokenVersioninvalida sesiones antiguas. Si el valor del token y el valor persistido no coinciden, la petición termina enSesion invalida.
Related
services/api/src/utils/jwt.js— aportaverifyAccessToken, que valida el JWT que consumerequireAuthservices/api/src/utils/apiError.js— aportaApiError, usado para normalizar respuestas de autenticación y autorizaciónservices/api/src/models/User.js—requireAuthusafindById,selectyleanpara recuperar el estado de la cuenta.services/api/src/modules/auth/auth.service.js— aportamaybeSendVerificationReminder, usado porrequireVerifiedEmailservices/api/src/modules/auth/auth.routes.js— aplicarequireAuthen rutas autenticadas comoGET /me,PATCH /meyPATCH /change-passwordservices/api/test/user-auth.test.js— confirma que un token consubjectTypedistinto deuseres rechazado con 401
[!NOTE] En el contexto del repositorio, este middleware aparece como una pieza reutilizable de seguridad para varias rutas protegidas, no solo para auth. El material recuperado lo vincula con módulos de acceso transversal