# US-MGN001-003: Renovacion Automatica de Tokens

## Identificacion

| Campo | Valor |
|-------|-------|
| **ID** | US-MGN001-003 |
| **Modulo** | MGN-001 Auth |
| **Sprint** | Sprint 1 |
| **Prioridad** | P0 - Critica |
| **Story Points** | 8 |
| **Estado** | Ready |
| **Autor** | System |
| **Fecha** | 2025-12-05 |

---

## Historia de Usuario

**Como** usuario autenticado del sistema ERP
**Quiero** que mi sesion se renueve automaticamente mientras estoy usando el sistema
**Para** no tener que re-autenticarme constantemente y tener una experiencia de usuario fluida

---

## Descripcion

El sistema debe renovar automaticamente los tokens de acceso antes de que expiren, utilizando el refresh token. Esto permite mantener sesiones de larga duracion (hasta 7 dias) mientras los access tokens mantienen una vida corta (15 minutos) por seguridad.

### Contexto

- Access tokens expiran en 15 minutos por seguridad
- Los usuarios no deben ser interrumpidos mientras trabajan
- El proceso debe ser transparente para el usuario

---

## Criterios de Aceptacion

### Escenario 1: Refresh automatico exitoso

```gherkin
Given un usuario autenticado trabajando en el sistema
  And su access token expira en menos de 1 minuto
  And su refresh token es valido
When el frontend detecta que el token esta por expirar
Then el frontend envia automaticamente POST /api/v1/auth/refresh
  And recibe nuevos access y refresh tokens
  And actualiza los tokens en memoria
  And la cookie httpOnly es actualizada
  And el usuario NO es interrumpido
```

### Escenario 2: Refresh con token valido (manual)

```gherkin
Given un usuario con refresh token valido
When hace POST /api/v1/auth/refresh
Then el sistema valida el refresh token
  And genera un nuevo par de tokens
  And el refresh token anterior es marcado como usado
  And el nuevo refresh token pertenece a la misma familia
  And responde con status 200
```

### Escenario 3: Refresh token expirado

```gherkin
Given un usuario con refresh token expirado (>7 dias)
When intenta renovar tokens
Then el sistema responde con status 401
  And el mensaje es "Refresh token expirado"
  And el usuario es redirigido al login
```

### Escenario 4: Deteccion de token replay

```gherkin
Given un refresh token que ya fue usado para renovar
  And el sistema genero un nuevo token despues de usarlo
When alguien intenta usar el refresh token viejo
Then el sistema detecta el reuso
  And invalida TODA la familia de tokens
  And responde con status 401
  And el mensaje es "Sesion comprometida. Por favor inicia sesion."
  And todas las sesiones del usuario son cerradas
```

### Escenario 5: Refresh sin token

```gherkin
Given un request sin refresh token
When intenta renovar tokens
Then el sistema responde con status 400
  And el mensaje es "Refresh token requerido"
```

### Escenario 6: Multiples requests simultaneos

```gherkin
Given un usuario con token por expirar
  And el frontend hace 3 requests simultaneos de refresh
When los requests llegan al servidor
Then solo el primero obtiene nuevos tokens
  And los siguientes reciben los nuevos tokens (idempotente)
  OR los siguientes reciben error y deben reintentar con el nuevo token
```

---

## Mockup / Wireframe

```
El proceso es INVISIBLE para el usuario. No hay UI directa.

┌──────────────────────────────────────────────────────────────────┐
│ Indicador de sesion (opcional en header)                         │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│  [🔒 Sesion activa]  ← Verde: sesion renovada                    │
│                                                                   │
│  [⚠️ Renovando...]   ← Amarillo: refresh en progreso             │
│                                                                   │
│  [❌ Sesion expirada] ← Rojo: necesita login                     │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘

Flujo de sesion expirada:
┌──────────────────────────────────────────────────────────────────┐
│                     SESION EXPIRADA                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│   Tu sesion ha expirado. Por favor inicia sesion nuevamente.    │
│                                                                   │
│   [  Ir a Login  ]                                               │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘
```

---

## Notas Tecnicas

### API

```typescript
// Request
POST /api/v1/auth/refresh
Cookie: refresh_token=eyJhbGciOiJSUzI1NiIs...

// O en body (fallback)
{
  "refreshToken": "eyJhbGciOiJSUzI1NiIs..."
}

// Response 200
{
  "accessToken": "eyJhbGciOiJSUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 900
}
// Set-Cookie: refresh_token=nuevo...; HttpOnly; Secure; SameSite=Strict
```

### Logica de Refresh en Frontend

```typescript
// Token refresh interceptor (pseudo-code)
class TokenRefreshService {
  private refreshing = false;
  private refreshPromise: Promise<void> | null = null;

  async checkAndRefresh(): Promise<void> {
    const accessToken = this.getAccessToken();
    const expiresAt = this.decodeExpiration(accessToken);
    const now = Date.now();
    const oneMinute = 60 * 1000;

    // Renovar si expira en menos de 1 minuto
    if (expiresAt - now < oneMinute) {
      await this.refresh();
    }
  }

  async refresh(): Promise<void> {
    // Evitar multiples refreshes simultaneos
    if (this.refreshing) {
      return this.refreshPromise;
    }

    this.refreshing = true;
    this.refreshPromise = this.doRefresh();

    try {
      await this.refreshPromise;
    } finally {
      this.refreshing = false;
      this.refreshPromise = null;
    }
  }

  private async doRefresh(): Promise<void> {
    try {
      const response = await api.post('/auth/refresh');
      this.setTokens(response.data);
    } catch (error) {
      // Refresh failed, redirect to login
      this.clearTokens();
      router.push('/login');
      throw error;
    }
  }
}
```

### Rotacion de Tokens (Token Family)

```
Login exitoso:
  └── RT1 (family: ABC) ← Activo

Refresh 1:
  ├── RT1 (family: ABC) → isUsed: true, replacedBy: RT2
  └── RT2 (family: ABC) ← Activo

Refresh 2:
  ├── RT1 (family: ABC) → isUsed: true, replacedBy: RT2
  ├── RT2 (family: ABC) → isUsed: true, replacedBy: RT3
  └── RT3 (family: ABC) ← Activo

Token Replay (RT1 reutilizado):
  ├── RT1 (family: ABC) → ALERTA! ya esta usado
  ├── RT2 (family: ABC) → REVOCADO por seguridad
  └── RT3 (family: ABC) → REVOCADO por seguridad
```

---

## Definicion de Done

- [ ] Endpoint POST /api/v1/auth/refresh implementado
- [ ] Rotacion de tokens funcionando
- [ ] Deteccion de token replay
- [ ] Revocacion de familia en caso de reuso
- [ ] Cookie actualizada en cada refresh
- [ ] Frontend: interceptor de refresh automatico
- [ ] Frontend: manejo de sesion expirada
- [ ] Tests unitarios (>80% coverage)
- [ ] Tests e2e pasando
- [ ] Code review aprobado

---

## Dependencias

### Requiere

| Item | Descripcion |
|------|-------------|
| US-MGN001-001 | Login (obtener tokens iniciales) |
| Tabla refresh_tokens | Con campos family_id, is_used, replaced_by |
| Redis | Para rate limiting |

### Bloquea

| Item | Descripcion |
|------|-------------|
| Todas las features | Dependen de sesion activa |

---

## Estimacion

| Tarea | Horas |
|-------|-------|
| Backend: refreshTokens() | 4h |
| Backend: Token rotation logic | 3h |
| Backend: Token replay detection | 2h |
| Backend: Tests | 3h |
| Frontend: Refresh interceptor | 3h |
| Frontend: Token storage | 2h |
| Frontend: Tests | 2h |
| **Total** | **19h** |

---

## Referencias

- [RF-AUTH-003](../../01-requerimientos/RF-auth/RF-AUTH-003.md) - Requerimiento funcional
- [RF-AUTH-002](../../01-requerimientos/RF-auth/RF-AUTH-002.md) - Estructura de tokens
- [ET-auth-backend](../../02-modelado/especificaciones-tecnicas/ET-auth-backend.md) - Spec tecnica

---

## Historial

| Version | Fecha | Autor | Cambios |
|---------|-------|-------|---------|
| 1.0 | 2025-12-05 | System | Creacion inicial |
