Autenticación y Autorización del API Gateway
Autenticación y Autorización del API Gateway
Descripción General
El API Gateway implementa un sistema de autenticación integral basado en JWT que valida las identidades de usuario y aplica control de acceso basado en roles (RBAC) en todos los Endpoints protegidos. Las operaciones de autenticación se delegan al microservicio MS_AUTH, que maneja la validación de credenciales, generación de tokens y gestión de usuarios.
Componentes Clave:
- JWT Tokens: Autenticación sin estado usando JSON Web Tokens
- JwtAuthGuard: Valida tokens y adjunta datos de usuario a las solicitudes
- RolesGuard: Aplica control de acceso basado en roles
- TokenValidationService: Se comunica con MS_AUTH para validación de token
- OTP System: Flujos de verificación de email y restablecimiento de contraseña
Ciclo de Vida del Token:
- Generación: MS_AUTH genera JWT al iniciar sesión exitosamente
- Validación: Gateway valida el token en cada solicitud protegida
- Actualización: Actualmente no implementado (ver sección Brechas)
- Revocación: Manejado por MS_AUTH (invalidación de token al cerrar sesión)
Flujo de Autenticación
Flujo de Login
sequenceDiagram
participant Client
participant Gateway as API Gateway
participant Controller as AuthController
participant Handler as LoginCommandHandler
participant MS_AUTH
Client->>Gateway: POST /api/auth/login<br/>{ email, password }
Gateway->>Controller: login()
Controller->>Handler: Dispatch LoginCommand
Handler->>MS_AUTH: Send 'auth.login' message<br/>{ email, password }
MS_AUTH->>MS_AUTH: Validate credentials
MS_AUTH->>MS_AUTH: Check email verification status
MS_AUTH->>MS_AUTH: Generate JWT token
MS_AUTH-->>Handler: { token, user: { id, email, name, role } }
Handler-->>Controller: Return result
Controller-->>Gateway: LoginResponseDto
Gateway-->>Client: 200 OK<br/>{ token, user }
Ejemplo de Código: Solicitud de Login
# Login requestcurl -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "securePassword123" }'
# Success response (wrapped by ResponseInterceptor){ "statusCode": 200, "message": "Login successful", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/auth/login", "method": "POST", "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJyb2xlIjoiQ0xJRU5UIiwiaWF0IjoxNjQyMjQ0NDAwLCJleHAiOjE2NDIzMzA4MDB9.signature", "user": { "id": "user-123", "email": "user@example.com", "name": "John Doe", "role": "CLIENT", "isEmailVerified": true } }, "traceId": "550e8400-e29b-41d4-a716-446655440000"}
# Note: The actual response structure depends on what the handler returns.# If the handler returns an object with 'success' or 'statusCode', the interceptor passes it through.
# Use token in subsequent requestscurl http://localhost:3000/api/orders \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Archivo de Referencia: algesta-api-gateway-nestjs/src/infrastructure/controllers/auth.controller.ts
Proceso de Validación de Token
Flujo de Validación
sequenceDiagram
participant Client
participant Gateway as API Gateway
participant Guard as JwtAuthGuard
participant TokenService as TokenValidationService
participant MS_AUTH
participant Controller
Client->>Gateway: GET /api/orders<br/>Authorization: Bearer <token>
Gateway->>Guard: canActivate(context)
Guard->>Guard: Extract token from header
Guard->>TokenService: execute(token)
TokenService->>MS_AUTH: Send 'auth.validateToken' message
MS_AUTH->>MS_AUTH: Verify JWT signature
MS_AUTH->>MS_AUTH: Check expiration
MS_AUTH->>MS_AUTH: Check user status
MS_AUTH->>MS_AUTH: Check email verification
alt Token Valid
MS_AUTH-->>TokenService: { valid: true, user: {...} }
TokenService-->>Guard: User data
Guard->>Gateway: Attach user to request.user
Gateway->>Controller: Route to handler
Controller-->>Client: 200 OK + data
else Token Invalid/Expired
MS_AUTH-->>TokenService: { valid: false, error: "..." }
TokenService-->>Guard: Throw UnauthorizedException
Guard-->>Client: 401 Unauthorized
else Email Not Verified
MS_AUTH-->>TokenService: { emailVerified: false }
TokenService-->>Guard: Throw ForbiddenException
Guard-->>Client: 403 Forbidden
end
Implementación de JwtAuthGuard
El guard realiza los siguientes pasos:
- Extraer Token: Lee el header
Authorization: Bearer <token> - Validar Formato: Asegura que el token esté presente y correctamente formateado
- Llamar TokenValidationService: Delega la validación al servicio
- Verificar Respuesta: Valida el estado del token y datos de usuario
- Adjuntar Usuario: Agrega el objeto de usuario a
request.user - Permitir/Rechazar: Retorna true (permitir) o lanza excepción (rechazar)
Escenarios de Error:
| Escenario | Exception | Código de Estado | Mensaje |
|---|---|---|---|
| Token faltante | UnauthorizedException | 401 | ”No token provided” |
| Token inválido | UnauthorizedException | 401 | ”Invalid token” |
| Token expirado | UnauthorizedException | 401 | ”Token expired” |
| Email no verificado | ForbiddenException | 403 | ”Email not verified” |
| Usuario inactivo | UnauthorizedException | 401 | ”User account inActivo” |
Nota sobre Detección de Errores: El JwtAuthGuard usa coincidencia de cadenas en validationResult.reason de MS_AUTH para distinguir entre estos casos. Los mensajes de error exactos y las condiciones están acoplados a lo que MS_AUTH retorna (ej. verificando si reason incluye “not verified”, “expired”, o “not found”). Esta dependencia semántica significa que cambios a los mensajes de error de MS_AUTH pueden requerir actualizaciones correspondientes en la lógica del guard.
Ejemplo de Código: Usando Guards
@Controller("orders")@ApiTags("Orders")export class OrdersManagementController { @Get() @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: "Get all orders" }) async getAllOrders(@CurrentUser() user: AuthenticatedUser) { // user object is available here // Contains: userId, email, name, role, isEmailVerified, etc. return this.commandBus.execute(new GetAllOrdersQuery(user)); }}Archivos de Referencia:
algesta-api-gateway-nestjs/src/shared/guards/jwt-auth.guard.tsalgesta-api-gateway-nestjs/src/shared/services/token-validation.service.ts
Control de Acceso Basado en Roles (RBAC)
Roles Soportados
El sistema soporta cinco roles de usuario, cada uno con permisos específicos:
| Rol | Descripción | Nivel de Acceso | Usuarios Típicos |
|---|---|---|---|
| ADMIN | Acceso completo al sistema | Todos los Endpoints y operaciones | Administradores del sistema |
| AGENT | Gestión y curación de órdenes | CRUD de órdenes, asignación de proveedores, publicación subastas | Agentes de servicio al cliente |
| PROVIDER | Entrega de servicios | Participación en subastas, reportes de trabajo, cumplimiento | Proveedores de servicio/contratistas |
| CLIENT | Solicitudes de servicio | Creación de órdenes, aprobaciones, calificación de proveedores | Clientes finales |
| EMPLOYEE | Operaciones de la empresa | Funcionalidades específicas de empresa, gestión de activos | Empleados de la empresa |
Implementación de RolesGuard
El RolesGuard aplica control de acceso basado en roles mediante:
- Lectura de Roles Requeridos: Extrae roles de los metadatos del decorador
@Roles - Extracción de Rol de Usuario: Obtiene
user.roledel token JWT (adjuntado por JwtAuthGuard) - Comparación de Roles: Verifica si el usuario tiene alguno de los roles requeridos
- Permitir/Rechazar: Retorna true si está autorizado, lanza ForbiddenException de lo contrario
Flujo del Guard:
sequenceDiagram
participant Request
participant JwtAuthGuard
participant RolesGuard
participant Controller
Request->>JwtAuthGuard: canActivate()
JwtAuthGuard->>Request: Attach user to request
Request->>RolesGuard: canActivate()
RolesGuard->>RolesGuard: Get @Roles metadata
RolesGuard->>RolesGuard: Check user.role in required roles
alt Role Authorized
RolesGuard->>Controller: Allow access
else Role Not Authorized
RolesGuard-->>Request: 403 Forbidden
end
Ejemplos de Endpoints con Restricciones de Rol
| Endpoint | Método | Roles Requeridos | Propósito |
|---|---|---|---|
/api/orders | PATCH | ADMIN, AGENT | Actualizar detalles de orden |
/api/orders/publish/:orderId | POST | ADMIN, AGENT | Publicar orden al mercado |
/api/provider/auctions | GET | PROVIDER | Listar subastas disponibles |
/api/provider/auction-response | POST | PROVIDER | Enviar oferta de subasta |
/api/client/submit-approval | POST | CLIENT | Aprobar cotización/trabajo |
/api/auction/publish | POST | ADMIN, AGENT | Crear nueva subasta |
/api/technician/assign-order | POST | ADMIN, AGENT | Asignar orden a técnico |
Ejemplo de Código: Guards Combinados
@Controller("orders")export class OrdersManagementController { @Patch(":orderId") @UseGuards(JwtAuthGuard, RolesGuard) @Roles(UserRole.ADMIN, UserRole.AGENT) @ApiBearerAuth() @ApiOperation({ summary: "Update order (ADMIN/AGENT only)" }) async updateOrder( @CurrentUser() user: AuthenticatedUser, @Param("orderId") orderId: string, @Body() updateDto: UpdateOrderDto ) { // Only ADMIN or AGENT can reach this point return this.commandBus.execute(new UpdateOrderCommand(orderId, updateDto)); }}Probando Acceso Basado en Roles:
# CLIENT trying to access ADMIN-only endpoint (should fail)curl -X PATCH http://localhost:3000/api/orders/order-123 \ -H "Authorization: Bearer <client-token>" \ -H "Content-Type: application/json" \ -d '{"status":"PUBLISHED"}'
# Response (BaseResponseDto structure from HttpExceptionFilter):{ "success": false, "message": "Forbidden resource", "data": null, "error": { "statusCode": 403, "error": "Forbidden", "message": "Forbidden resource", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/orders/order-123", "method": "PATCH", "traceId": "uuid-here" }}
# ADMIN trying same endpoint (should succeed)curl -X PATCH http://localhost:3000/api/orders/order-123 \ -H "Authorization: Bearer <admin-token>" \ -H "Content-Type: application/json" \ -d '{"status":"PUBLISHED"}'
# Response:{ "statusCode": 200, "message": "Order updated successfully", "data": { ... }, "traceId": "uuid-here"}Archivos de Referencia:
algesta-api-gateway-nestjs/src/shared/guards/roles.guard.tsalgesta-api-gateway-nestjs/src/shared/decorators/roles.decorator.tsalgesta-api-gateway-nestjs/src/shared/enums/user-role.enum.ts
Flujos de Registro de Usuario
Flujo de Registro Web
sequenceDiagram
participant Client
participant Gateway
participant MS_AUTH
participant EmailService
Client->>Gateway: POST /api/auth/register-web<br/>{ email, name, password, ... }
Gateway->>MS_AUTH: Send 'auth.register' message
MS_AUTH->>MS_AUTH: Validate email uniqueness
MS_AUTH->>MS_AUTH: Hash password
MS_AUTH->>MS_AUTH: Create user (isEmailVerified: false)
MS_AUTH->>EmailService: Send verification email
MS_AUTH-->>Gateway: { userId, email, message }
Gateway-->>Client: 201 Created<br/>"Check email for verification"
Note over Client,EmailService: User receives email with verification link
Client->>Gateway: POST /api/auth/otp/validate/:email/:code
Gateway->>MS_AUTH: Validate OTP code
MS_AUTH->>MS_AUTH: Update isEmailVerified: true
MS_AUTH-->>Gateway: { verified: true }
Gateway-->>Client: 200 OK<br/>"Email verified successfully"
Endpoint: POST /api/auth/register-web
Cuerpo de la Solicitud:
{ "email": "newuser@example.com", "name": "Jane Smith", "identification": "1234567890", "phone": "+573001234567", "password": "SecurePass123!", "isEmployee": false}Respuesta:
{ "statusCode": 201, "message": "User registered successfully. Please verify your email.", "data": { "userId": "user-456", "email": "newuser@example.com" }, "traceId": "uuid-here"}Archivo de Referencia: algesta-api-gateway-nestjs/src/infrastructure/controllers/auth.controller.ts
Flujo de Verificación OTP
1. Enviar OTP:
POST /api/auth/otp/send/:email
# Examplecurl -X POST http://localhost:3000/api/auth/otp/send/user@example.com
# Response{ "statusCode": 200, "message": "OTP sent to email", "data": { "email": "user@example.com", "expiresIn": "10 minutes" }}2. Validar OTP:
POST /api/auth/otp/validate/:email/:code
# Examplecurl -X POST http://localhost:3000/api/auth/otp/validate/user@example.com/123456
# Success Response{ "statusCode": 200, "message": "Email verified successfully", "data": { "verified": true }}
# Error Response (invalid code) - BaseResponseDto structure{ "success": false, "message": "Invalid or expired OTP code", "data": null, "error": { "statusCode": 400, "error": "Bad Request", "message": "Invalid or expired OTP code", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/auth/otp/validate/user@example.com/123456", "method": "POST", "traceId": "uuid-here" }}Características del OTP:
- Formato: Código numérico de 6 dígitos
- Validez: Tiempo limitado (configurable en MS_AUTH, típicamente 10 minutos)
- Uso único: Código invalidado después de validación exitosa
- Entrega: Email vía servicio de notificación de MS_AUTH
Archivo de Referencia: algesta-api-gateway-nestjs/src/infrastructure/controllers/auth.otp.controller.ts
Flujo de Registro de Empleado
Los empleados se registran con asociación de empresa:
POST /api/employee/register
# Request{ "email": "employee@company.com", "name": "Employee Name", "identification": "9876543210", "phone": "+573009876543", "password": "EmpPass123!", "companyNit": "900123456-7"}
# Process:# 1. Validate company NIT exists# 2. Create user account with EMPLOYEE role# 3. Associate employee with company# 4. Send verification email
# Response{ "statusCode": 201, "message": "Employee registered successfully", "data": { "userId": "emp-789", "companyId": "company-123", "email": "employee@company.com" }}Archivo de Referencia: algesta-api-gateway-nestjs/src/infrastructure/controllers/employee.controller.ts
Flujo de Restablecimiento de Contraseña
Proceso Completo de Restablecimiento de Contraseña
sequenceDiagram
participant User
participant Gateway
participant MS_AUTH
participant EmailService
User->>Gateway: POST /api/auth/forgot-password/:email
Gateway->>MS_AUTH: Request password reset
MS_AUTH->>MS_AUTH: Generate OTP (6 digits)
MS_AUTH->>EmailService: Send OTP email
MS_AUTH-->>Gateway: { message: "OTP sent" }
Gateway-->>User: 200 OK
Note over User,EmailService: User receives OTP email
User->>Gateway: POST /api/auth/validate-forgot-password-otp/:email/:code
Gateway->>MS_AUTH: Validate OTP
MS_AUTH->>MS_AUTH: Verify OTP is valid and not expired
MS_AUTH->>MS_AUTH: Generate reset token (2-hour validity)
MS_AUTH-->>Gateway: { resetToken: "token-abc123" }
Gateway-->>User: 200 OK + resetToken
User->>Gateway: POST /api/auth/reset-password<br/>{ resetToken, newPassword }
Gateway->>MS_AUTH: Reset password
MS_AUTH->>MS_AUTH: Validate reset token
MS_AUTH->>MS_AUTH: Hash new password
MS_AUTH->>MS_AUTH: Update user password
MS_AUTH->>MS_AUTH: Invalidate reset token
MS_AUTH-->>Gateway: { message: "Password reset successful" }
Gateway-->>User: 200 OK
Implementación Paso a Paso
Paso 1: Solicitar Restablecimiento de Contraseña
POST /api/auth/forgot-password/:email
# Examplecurl -X POST http://localhost:3000/api/auth/forgot-password/user@example.com
# Response{ "statusCode": 200, "message": "Password reset OTP sent to email", "data": { "email": "user@example.com" }}Paso 2: Validar OTP y Obtener Token de Restablecimiento
POST /api/auth/validate-forgot-password-otp/:email/:code
# Examplecurl -X POST http://localhost:3000/api/auth/validate-forgot-password-otp/user@example.com/123456
# Success Response{ "statusCode": 200, "message": "OTP validated successfully", "data": { "resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJwdXJwb3NlIjoicGFzc3dvcmRSZXNldCIsImlhdCI6MTY0MjI0NDQwMCwiZXhwIjoxNjQyMjUxNjAwfQ.signature", "expiresIn": "2 hours" }}Paso 3: Restablecer Contraseña con Token
POST /api/auth/reset-password
# Requestcurl -X POST http://localhost:3000/api/auth/reset-password \ -H "Content-Type: application/json" \ -d '{ "resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "newPassword": "NewSecurePass123!" }'
# Success Response{ "statusCode": 200, "message": "Password reset successfully", "data": { "email": "user@example.com" }}
# Error Response (invalid/expired token) - BaseResponseDto structure{ "success": false, "message": "Invalid or expired reset token", "data": null, "error": { "statusCode": 400, "error": "Bad Request", "message": "Invalid or expired reset token", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/auth/reset-password", "method": "POST", "traceId": "uuid-here" }}Consideraciones de Seguridad
Seguridad del OTP:
- Validez limitada en tiempo (típicamente 10 minutos)
- Uso único (invalidado después de validación exitosa)
- Limitación de tasa en generación de OTP (actualmente no implementado - ver Brechas)
Seguridad del Token de Restablecimiento:
- Ventana de expiración corta (2 horas)
- Uso único (invalidado inmediatamente después del restablecimiento de contraseña)
- No puede ser reutilizado incluso si no ha expirado
- Contiene email y propósito en el payload del JWT
Validación de Contraseña:
- Aplicada por MS_AUTH durante el restablecimiento de contraseña
- Longitud mínima, requisitos de complejidad
- Verificación de contraseña anterior (actualmente no implementado - ver Brechas)
Archivo de Referencia: algesta-api-gateway-nestjs/src/infrastructure/controllers/auth.controller.ts
Extracción de Usuario Actual
Decorador @CurrentUser()
El decorador @CurrentUser() proporciona acceso seguro a los datos del usuario autenticado dentro de los métodos del controlador.
Ejemplo de Uso:
@Controller("orders")export class OrdersController { @Get() @UseGuards(JwtAuthGuard) async getMyOrders(@CurrentUser() user: AuthenticatedUser) { // user object is automatically extracted from request.user console.log(user.userId); // "user-123" console.log(user.email); // "user@example.com" console.log(user.role); // UserRole.CLIENT console.log(user.name); // "John Doe"
return this.orderService.findByUserId(user.userId); }
@Post() @UseGuards(JwtAuthGuard, RolesGuard) @Roles(UserRole.ADMIN, UserRole.AGENT) async createOrder( @CurrentUser() user: AuthenticatedUser, @Body() createDto: CreateOrderDto ) { // Only ADMIN or AGENT can reach this point return this.orderService.create(createDto, user.userId); }}Interfaz AuthenticatedUser
El objeto de usuario adjuntado a las solicitudes contiene:
interface AuthenticatedUser { userId: string; // Unique user identifier (guaranteed) email: string; // User email address (guaranteed) name?: string; // Full name (optional - depends on MS_AUTH response) identification?: string; // Government ID number (optional - depends on MS_AUTH response) role?: UserRole; // User role (optional - depends on MS_AUTH response) isEmailVerified?: boolean; // Email verification status (optional - depends on MS_AUTH response) mfaEnabled?: boolean; // Multi-factor authentication status (optional - depends on MS_AUTH response) token?: string; // Original JWT token (optional - for logout operations) phone?: string; // Phone number (optional) companyId?: string; // Company ID (optional - for employees)}Detalles de los Campos:
| Campo | Tipo | Garantizado | Descripción |
|---|---|---|---|
| userId | string | Sí | Identificador único de MS_AUTH; presente confiablemente según lógica actual del guard |
| string | Sí | Dirección de email del usuario; presente confiablemente según lógica actual del guard | |
| name | string | No* | Nombre completo; presencia depende de la respuesta de MS_AUTH |
| identification | string | No* | Número de ID gubernamental o pasaporte; presencia depende de la respuesta de MS_AUTH |
| role | UserRole | No* | Uno de: ADMIN, AGENT, PROVIDER, CLIENT, EMPLOYEE; presencia depende de la respuesta de MS_AUTH |
| isEmailVerified | boolean | No* | Si el email está verificado; presencia depende de la respuesta de MS_AUTH |
| mfaEnabled | boolean | No* | Si MFA está habilitado (aún no aplicado); presencia depende de la respuesta de MS_AUTH |
| token | string | No* | JWT original para operaciones de logout; presencia depende de la respuesta de MS_AUTH |
| phone | string | No | Número de teléfono con código de país; campo opcional |
| companyId | string | No | Asociación con empresa (para rol EMPLOYEE); campo opcional |
Nota Importante: Solo userId y email están garantizados de estar presentes por la implementación actual de JwtAuthGuard. Todos los demás campos dependen de lo que MS_AUTH retorna durante la validación del token. El código que depende de campos como name, identification, mfaEnabled, o companyId debe manejar su potencial ausencia con gracia (ej. user.name || 'Unknown').
Archivos de Referencia:
algesta-api-gateway-nestjs/src/shared/decorators/current-user.decorator.tsalgesta-api-gateway-nestjs/src/shared/guards/jwt-auth.guard.ts
Mejores Prácticas de Seguridad
Almacenamiento de Token (Lado del Cliente)
Enfoques Recomendados:
-
HttpOnly Cookies (Más Seguro)
- Almacenado en cookie httpOnly, inaccesible a JavaScript
- Protege contra ataques XSS
- Requiere protección CSRF
- Actualmente no implementado - API usa tokens Bearer
-
localStorage (Implementación Actual)
- Fácil de implementar
- Vulnerable a ataques XSS
- Requiere prevención cuidadosa de XSS
- Usado por la mayoría de clientes SPA
-
sessionStorage (Alternativa)
- Se limpia cuando se cierra la pestaña
- Más seguro que localStorage para datos sensibles
- Mismas vulnerabilidades XSS
Transmisión de Token
Siempre usar HTTPS en producción:
// Development (HTTP allowed)const apiUrl = "http://localhost:3000/api";
// Production (HTTPS required)const apiUrl = "https://api.algesta.com/api";Siempre incluir el token en el header Authorization:
// Correctfetch("/api/orders", { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", },});
// Incorrect (never send token in URL or body)fetch(`/api/orders?token=${token}`); // NEVER DO THISAplicación de Verificación de Email
Algunos Endpoints requieren email verificado:
@Post('sensitive-action')@UseGuards(JwtAuthGuard, EmailVerifiedGuard)async sensitiveAction(@CurrentUser() user: AuthenticatedUser) { // Solo usuarios con isEmailVerified: true pueden acceder}Brecha Actual: La verificación de email no se aplica consistentemente en todos los Endpoints sensibles.
Estrategia de Expiración de Token
Implementación Actual:
- Los tokens JWT tienen tiempo de expiración (configurado en MS_AUTH)
- Sin mecanismo automático de actualización
- Los usuarios deben volver a iniciar sesión cuando el token expira
Estrategia Recomendada (ver sección Brechas):
- Access tokens de corta duración (15-30 minutos)
- Refresh tokens de larga duración (7-30 días)
- Actualización automática de token antes de la expiración
- Ventana de sesión deslizante
Limitación de Tasa en Endpoints de Autenticación
Brecha Actual: Sin limitación de tasa implementada.
Límites Recomendados:
- Login: 5 intentos por 15 minutos por IP/email
- Restablecimiento de contraseña: 3 intentos por hora por email
- Generación OTP: 3 intentos por hora por email
- Registro: 10 intentos por hora por IP
Referencia de Endpoints de Autenticación
Lista Completa de Endpoints
| Método | Ruta | Descripción | Auth Requerido | Roles | Cuerpo de Solicitud | Respuesta |
|---|---|---|---|---|---|---|
| POST | /api/auth/login | Autenticar usuario | No | - | { email, password } | { token, user } |
| POST | /api/auth/register-web | Registrar usuario web | No | - | { email, name, identification, phone, password, isEmployee } | { userId, email, message } |
| POST | /api/auth/forgot-password/:email | Solicitar restablecimiento de contraseña | No | - | - | { message } |
| POST | /api/auth/validate-forgot-password-otp/:email/:code | Validar OTP de restablecimiento | No | - | - | { resetToken } |
| POST | /api/auth/reset-password | Restablecer contraseña con token | No | - | { resetToken, newPassword } | { message } |
| POST | /api/auth/otp/send/:email | Enviar OTP de verificación | No | - | - | { message } |
| POST | /api/auth/otp/validate/:email/:code | Validar OTP de verificación | No | - | - | { verified: true } |
Documentación Detallada de Endpoints
POST /api/auth/login
- Propósito: Autenticar usuario con email y contraseña
- Estado de Éxito: 200
- Estado de Error: 401 (Credenciales inválidas), 400 (Error de validación)
- Ejemplo: Ver sección Flujo de Login
POST /api/auth/register-web
- Propósito: Registrar nuevo usuario web
- Estado de Éxito: 201
- Estado de Error: 409 (Usuario existe), 400 (Error de validación)
- Ejemplo: Ver sección Flujo de Registro Web
POST /api/auth/forgot-password/:email
- Propósito: Iniciar proceso de restablecimiento de contraseña
- Estado de Éxito: 200
- Estado de Error: 404 (Usuario no encontrado)
- Ejemplo: Ver sección Flujo de Restablecimiento de Contraseña
POST /api/auth/validate-forgot-password-otp/:email/:code
- Propósito: Validar OTP y recibir token de restablecimiento
- Estado de Éxito: 200
- Estado de Error: 400 (OTP inválido/expirado)
- Ejemplo: Ver sección Flujo de Restablecimiento de Contraseña
POST /api/auth/reset-password
- Propósito: Restablecer contraseña usando token de restablecimiento
- Estado de Éxito: 200
- Estado de Error: 400 (Token inválido, contraseña débil)
- Ejemplo: Ver sección Flujo de Restablecimiento de Contraseña
Probando Autenticación
Flujo de Prueba Completo
1. Registrar Nuevo Usuario
curl -X POST http://localhost:3000/api/auth/register-web \ -H "Content-Type: application/json" \ -d '{ "email": "test@example.com", "name": "Test User", "identification": "1234567890", "phone": "+573001234567", "password": "TestPass123!", "isEmployee": false }'2. Verificar Email con OTP
# Send OTPcurl -X POST http://localhost:3000/api/auth/otp/send/test@example.com
# Validate OTP (check email for code)curl -X POST http://localhost:3000/api/auth/otp/validate/test@example.com/1234563. Iniciar Sesión
curl -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "test@example.com", "password": "TestPass123!" }'
# Save the token from responseexport TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."4. Access Protected Endpoint
curl http://localhost:3000/api/orders \ -H "Authorization: Bearer $TOKEN"5. Test Role-Based Access (Should Fail)
# CLIENT trying to access ADMIN endpointcurl -X PATCH http://localhost:3000/api/orders/order-123 \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"status":"PUBLISHED"}'
# Expected: 403 Forbidden6. Test Password Reset
# Request resetcurl -X POST http://localhost:3000/api/auth/forgot-password/test@example.com
# Validate OTP (check email)curl -X POST http://localhost:3000/api/auth/validate-forgot-password-otp/test@example.com/123456
# Reset password with tokencurl -X POST http://localhost:3000/api/auth/reset-password \ -H "Content-Type: application/json" \ -d '{ "resetToken": "token-from-previous-step", "newPassword": "NewPass123!" }'Error Response Examples
Invalid Credentials:
{ "statusCode": 401, "message": "Invalid email or password", "error": "Unauthorized", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/auth/login", "traceId": "uuid-here"}Token Expired:
{ "statusCode": 401, "message": "Token has expired", "error": "Unauthorized", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/orders", "traceId": "uuid-here"}Insufficient Permissions:
{ "statusCode": 403, "message": "Forbidden resource", "error": "Forbidden", "timestamp": "2025-01-15T10:30:00Z", "path": "/api/orders/order-123", "traceId": "uuid-here"}Integration with Microservicios
Gateway → MS_AUTH Communication
All authentication Operaciones are delegated to the MS_AUTH Microservicio:
Message Patterns:
auth.login- User loginauth.register- User registrationauth.validateToken- Token validationauth.forgotPassword- Password reset requestauth.validateOtp- OTP validationauth.resetPassword- Password reset execution
Example Communication:
// In TokenValidationServiceasync execute(token: string): Promise<AuthenticatedUser> { try { const result = await this.msAuthClient .send('auth.validateToken', { token }) .toPromise();
if (!result.valid) { throw new UnauthorizedException('Invalid token'); }
if (!result.user.isEmailVerified) { throw new ForbiddenException('Email not verified'); }
return result.user; } catch (error) { // Handle MS_AUTH errors throw new UnauthorizedException('Token validation failed'); }}Error Handling and Propagation
MS_AUTH Error → Gateway Error Mapping:
| MS_AUTH Error | Gateway Exception | Estado Code |
|---|---|---|
| User not found | UnauthorizedException | 401 |
| Invalid password | UnauthorizedException | 401 |
| Token expired | UnauthorizedException | 401 |
| Email not verified | ForbiddenException | 403 |
| User inActivo | UnauthorizedException | 401 |
| Validation error | BadRequestException | 400 |
| Service unavailable | ServiceUnavailableException | 503 |
Circuit Breaker Integration:
- Authentication calls wrapped in circuit breaker
- Fast-fail when MS_AUTH is unavailable
- Fallback: Return cached user data for token validation (not currently implemented)
For more details on resilience patterns, see API Gateway Resilience.
For inter-service communication details, see Inter-Service Communication.
Gaps and Recommendations
Current Gaps
-
Refresh Token Mechanism
- Estado: Not implemented
- Impact: Users must re-login when JWT expires
- Recommendation: Implement refresh token flow with sliding sessions
- Implementación:
POST /api/auth/refreshBody: { refreshToken: "..." }Response: { accessToken: "...", refreshToken: "..." }
-
Multi-Factor Authentication (MFA)
- Estado: Partially implemented (mfaEnabled flag exists but not enforced)
- Impact: Reduced security for sensitive Operaciones
- Recommendation: Completo MFA Implementación with TOTP/SMS
- Prioridad: High for ADMIN and AGENT roles
-
Session Management
- Estado: Not implemented
- Impact: No centralized session tracking or revocation
- Recommendation: Implement session store (Redis) for token tracking
- Benefits: Enable logout from all devices, track Activo sessions
-
Rate Limiting on Auth Endpoints
- Estado: Not implemented
- Impact: Vulnerable to brute force attacks
- Recommendation: Implement @nestjs/throttler
- Suggested Limits:
- Login: 5 attempts per 15 minutes
- Password reset: 3 attempts per hour
- OTP generation: 3 attempts per hour
-
Password History
- Estado: Not implemented
- Impact: Users can reuse old passwords
- Recommendation: Store hash of last 5 passwords, prevent reuse
-
Account Lockout
- Estado: Not implemented
- Impact: Unlimited login attempts possible
- Recommendation: Lock account after N failed attempts, require admin unlock or time-based unlock
-
Audit Logging for Auth Events
- Estado: Basic logging exists, no structured audit trail
- Impact: Difficult to track suspicious authentication activity
- Recommendation: Implement dedicated auth audit log with:
- Login attempts (success/failure)
- Password changes
- Role changes
- Token generation/validation
-
OAuth2/OIDC Support
- Estado: Not implemented
- Impact: Cannot integrate with third-party identity providers
- Recommendation: Add OAuth2 support for Google, Microsoft, etc.
- Use Case: Enterprise SSO integration
-
Email Verification Enforcement
- Estado: Inconsistent across Endpoints
- Impact: Unverified users can access some protected resources
- Recommendation: Create EmailVerifiedGuard and apply consistently
-
Token Blacklisting
- Estado: Not implemented
- Impact: Tokens remain valid until expiration even after logout
- Recommendation: Implement Redis-based token blacklist
- Use Case: Immediate logout, security incidents
Security Recommendations
High Prioridad:
- Implement rate limiting on all auth Endpoints
- Add refresh token mechanism
- Enforce email verification consistently
- Implement account lockout after failed attempts
- Add comprehensive audit logging
Medium Prioridad: 6. Completo MFA Implementación for admin roles 7. Implement session management with Redis 8. Add password history tracking 9. Implement token blacklisting for logout
Low Prioridad: 10. Add OAuth2/OIDC support for third-party auth 11. Implement device tracking and management 12. Add IP-based geolocation and suspicious login detection
Pruebas Recommendations
Automated Pruebas Needed:
- Unit Pruebas for guards and validation service
- Integration Pruebas for Completo auth flows
- Security Pruebas for:
- Token tampering
- Expired token handling
- Role elevation attempts
- Brute force attack simulation
Manual Security Pruebas:
- Penetration Pruebas of auth Endpoints
- Token lifetime and expiration verification
- Role-based access control verification
- Password reset flow security
Referencias Cruzadas
Related Documentoation
- Main Gateway Documentoation: API Gateway Arquitectura
- API Reference: API Gateway API Reference
- Resilience Patterns: API Gateway Resilience
- Inter-Service Communication: Inter-Service Communication
- Backend Microservicios: Backend Microservicios Descripción General
Referenced Files
algesta-api-gateway-nestjs/src/shared/guards/jwt-auth.guard.tsalgesta-api-gateway-nestjs/src/shared/guards/roles.guard.tsalgesta-api-gateway-nestjs/src/shared/services/token-validation.service.tsalgesta-api-gateway-nestjs/src/shared/decorators/roles.decorator.tsalgesta-api-gateway-nestjs/src/shared/decorators/current-user.decorator.tsalgesta-api-gateway-nestjs/src/infrastructure/controllers/auth.controller.tsalgesta-api-gateway-nestjs/src/infrastructure/controllers/auth.otp.controller.ts
Resumen
The API Gateway implements a robust JWT-based authentication system with role-based access control, delegating all auth Operaciones to the MS_AUTH Microservicio. The system provides comprehensive user registration, email verification, and password reset flows.
Key Strengths:
- Clean separation of concerns with guards and services
- Type-safe user extraction with @CurrentUser() decorator
- Flexible role-based access control
- Comprehensive OTP-based verification flows
- Integration with MS_AUTH for centralized user management
Prioridad Improvements:
- Implement refresh token mechanism
- Add rate limiting to prevent brute force attacks
- Completo MFA Implementación for sensitive roles
- Implement session management and token blacklisting
- Enforce email verification consistently
For Completo API Endpoint Documentoation, see API Gateway API Reference.