# 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<UserSession>;

  // Validar sesión y actualizar actividad
  async validateSession(sessionId: string): Promise<UserSession | null>;

  // Renovar sesión
  async refreshSession(sessionId: string, newExpiresAt: Date): Promise<UserSession>;

  // 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<number>;

  // Obtener sesiones activas del usuario
  async getSessions(userId: string): Promise<UserSession[]>;
}
```

---

## 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
