# Gestión de Sesiones **Versión:** 1.0.0 **Origen:** projects/gamilit **Estado:** Producción **Última actualización:** 2025-12-08 --- ## Descripción Sistema de gestión de sesiones de usuario con: - Múltiples sesiones concurrentes (máx 5) - Tracking de dispositivo, IP, ubicación - Renovación automática con refresh tokens - Revocación individual y masiva - Limpieza automática de sesiones expiradas --- ## Características | Característica | Descripción | |----------------|-------------| | Multi-sesión | Hasta 5 sesiones concurrentes por usuario | | Auto-limpieza | Sesiones más antiguas eliminadas automáticamente | | Device Tracking | IP, User-Agent, dispositivo, navegador, OS | | Geo-location | País y ciudad (si disponible) | | Refresh Tokens | Hasheados con SHA256 | | Revocación | Individual o masiva | | Multi-tenant | Soporte para múltiples tenants | --- ## Stack Tecnológico ```yaml backend: framework: NestJS orm: TypeORM crypto: Node.js crypto (SHA256) database: engine: PostgreSQL schemas: - auth_management (sesiones) ``` --- ## Dependencias NPM ```json { "typeorm": "^0.3.x", "@nestjs/typeorm": "^10.x" } ``` Nota: crypto es nativo de Node.js, no requiere instalación. --- ## Tabla Requerida ```sql CREATE TABLE auth_management.user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, tenant_id UUID, session_token TEXT NOT NULL UNIQUE, refresh_token TEXT, -- Hasheado con SHA256 user_agent TEXT, ip_address INET, device_type VARCHAR(50), -- desktop, mobile, tablet, unknown browser VARCHAR(100), os VARCHAR(100), country VARCHAR(100), city VARCHAR(100), created_at TIMESTAMPTZ DEFAULT NOW(), last_activity_at TIMESTAMPTZ DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL, is_active BOOLEAN DEFAULT TRUE, revoked_at TIMESTAMPTZ, metadata JSONB DEFAULT '{}' ); -- Índices CREATE INDEX idx_user_sessions_user_id ON auth_management.user_sessions(user_id); CREATE INDEX idx_user_sessions_tenant_id ON auth_management.user_sessions(tenant_id); CREATE INDEX idx_user_sessions_session_token ON auth_management.user_sessions(session_token); CREATE INDEX idx_user_sessions_expires_at ON auth_management.user_sessions(expires_at); ``` --- ## Estructura del Módulo ``` session-management/ ├── services/ │ └── session-management.service.ts ├── entities/ │ └── user-session.entity.ts ├── dto/ │ ├── create-user-session.dto.ts │ ├── update-user-session.dto.ts │ └── user-session-response.dto.ts └── __tests__/ └── session-management.service.spec.ts ``` --- ## API del Servicio ```typescript class SessionManagementService { // Crear nueva sesión async createSession(dto: CreateUserSessionDto): Promise; // Validar sesión y actualizar actividad async validateSession(sessionId: string): Promise; // Renovar sesión async refreshSession(sessionId: string, newExpiresAt: Date): Promise; // Revocar sesión específica async revokeSession(sessionId: string, userId: string): Promise<{ message: string }>; // Revocar todas excepto la actual async revokeAllSessions(userId: string, currentSessionId: string): Promise<{ message: string; count: number }>; // Limpiar sesiones expiradas (cron) async cleanExpiredSessions(): Promise; // Obtener sesiones activas del usuario async getSessions(userId: string): Promise; } ``` --- ## Uso Rápido ### 1. Crear sesión al login ```typescript import { SessionManagementService } from '@/modules/auth/services'; // En AuthService.login() const session = await this.sessionManagementService.createSession({ user_id: user.id, tenant_id: profile.tenant_id, session_token: crypto.randomBytes(32).toString('hex'), refresh_token: refreshToken, // Será hasheado internamente ip_address: req.ip, user_agent: req.headers['user-agent'], device_type: this.detectDeviceType(userAgent), browser: this.detectBrowser(userAgent), os: this.detectOS(userAgent), expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 días }); ``` ### 2. Obtener sesiones activas ```typescript // En UsersController @Get('sessions') @UseGuards(JwtAuthGuard) async getSessions(@Request() req) { return this.sessionManagementService.getSessions(req.user.id); } ``` ### 3. Revocar sesión ```typescript // En UsersController @Delete('sessions/:id') @UseGuards(JwtAuthGuard) async revokeSession(@Param('id') sessionId: string, @Request() req) { return this.sessionManagementService.revokeSession(sessionId, req.user.id); } ``` ### 4. Cerrar todas las sesiones ```typescript // En UsersController @Post('sessions/revoke-all') @UseGuards(JwtAuthGuard) async revokeAllSessions(@Request() req, @Body() body: { currentSessionId: string }) { return this.sessionManagementService.revokeAllSessions( req.user.id, body.currentSessionId ); } ``` ### 5. Cron job para limpieza ```typescript import { Cron, CronExpression } from '@nestjs/schedule'; @Cron(CronExpression.EVERY_HOUR) async cleanExpiredSessions() { const count = await this.sessionManagementService.cleanExpiredSessions(); this.logger.log(`Limpiadas ${count} sesiones expiradas`); } ``` --- ## Comportamientos Clave ### Límite de Sesiones ```typescript MAX_SESSIONS_PER_USER = 5; // Al crear la 6ta sesión: // 1. Se eliminan sesiones expiradas // 2. Si aún hay 5+, se elimina la más antigua // 3. Se crea la nueva sesión ``` ### Hasheo de Refresh Tokens ```typescript // El refresh token se hashea antes de guardar en BD const hashedToken = crypto.createHash('sha256') .update(refreshToken) .digest('hex'); ``` ### Validación de Propiedad ```typescript // Solo el dueño puede revocar sus sesiones await this.sessionRepository.findOne({ where: { id: sessionId, user_id: userId }, // Validación de ownership }); ``` --- ## Flujo de Sesiones ``` LOGIN │ ├─► Crear sesión con tokens │ ├─► Limpiar expiradas │ ├─► Si >5, eliminar antigua │ └─► Guardar nueva sesión │ REFRESH │ ├─► Validar refresh token ├─► Buscar sesión por hash └─► Actualizar expiración │ LOGOUT │ └─► Marcar sesión como inactiva └─► Establecer revoked_at ``` --- ## Seguridad - Refresh tokens nunca se almacenan en texto plano - Validación de ownership en revocación - Soft delete con `is_active = false` y `revoked_at` - Limpieza periódica de datos obsoletos - No se serializa `refresh_token` en respuestas --- ## Adaptaciones Necesarias 1. **Límite de sesiones**: Ajustar `MAX_SESSIONS_PER_USER` según necesidades 2. **Tiempo de expiración**: Configurar según política de seguridad 3. **Geo-location**: Implementar si se requiere país/ciudad 4. **Multi-tenant**: Omitir `tenant_id` si no aplica 5. **Cron schedule**: Ajustar frecuencia de limpieza --- ## Referencias - Código completo: `projects/gamilit/apps/backend/src/modules/auth/services/session-management.service.ts` - Entity: `projects/gamilit/apps/backend/src/modules/auth/entities/user-session.entity.ts` --- **Mantenido por:** Sistema NEXUS **Proyecto origen:** Gamilit Platform