# RF-AUTH-002: Generacion y Validacion de JWT Tokens

## Identificacion

| Campo | Valor |
|-------|-------|
| **ID** | RF-AUTH-002 |
| **Modulo** | MGN-001 |
| **Nombre Modulo** | Auth - Autenticacion |
| **Prioridad** | P0 |
| **Complejidad** | Alta |
| **Estado** | Aprobado |
| **Autor** | System |
| **Fecha** | 2025-12-05 |

---

## Descripcion

El sistema debe implementar un mecanismo de tokens JWT (JSON Web Tokens) para autenticar y autorizar las peticiones de los usuarios. Los tokens deben contener la informacion necesaria para identificar al usuario y su tenant, permitiendo el acceso a los recursos del sistema de forma segura y stateless.

### Contexto de Negocio

Los tokens JWT permiten autenticacion stateless, lo cual es esencial para:
- Escalabilidad horizontal del backend
- APIs REST verdaderamente stateless
- Soporte para multiples clientes (web, mobile, integraciones)
- Aislamiento multi-tenant mediante tenant_id en el payload

---

## Criterios de Aceptacion

- [x] **CA-001:** El sistema debe generar access tokens con expiracion de 15 minutos
- [x] **CA-002:** El sistema debe generar refresh tokens con expiracion de 7 dias
- [x] **CA-003:** El payload del token debe incluir: userId, tenantId, email, roles
- [x] **CA-004:** Los tokens deben firmarse con algoritmo RS256 (asymmetric keys)
- [x] **CA-005:** El sistema debe validar la firma del token en cada request
- [x] **CA-006:** El sistema debe rechazar tokens expirados con error 401
- [x] **CA-007:** El sistema debe rechazar tokens con firma invalida con error 401
- [x] **CA-008:** El sistema debe extraer el usuario del token y adjuntarlo al request

### Ejemplos de Verificacion

```gherkin
Scenario: Validacion de token valido
  Given un usuario autenticado con un access token valido
  When el usuario hace una peticion a un endpoint protegido
  Then el sistema valida la firma del token
  And extrae el userId y tenantId del payload
  And permite el acceso al recurso

Scenario: Token expirado
  Given un usuario con un access token expirado
  When el usuario hace una peticion a un endpoint protegido
  Then el sistema responde con status 401
  And el mensaje es "Token expirado"
  And el header incluye WWW-Authenticate: Bearer error="token_expired"

Scenario: Token con firma invalida
  Given un token modificado o con firma incorrecta
  When se intenta acceder a un endpoint protegido
  Then el sistema responde con status 401
  And el mensaje es "Token invalido"
```

---

## Reglas de Negocio

| ID | Regla | Validacion |
|----|-------|------------|
| RN-001 | Access token expira en 15 minutos | JWT exp claim |
| RN-002 | Refresh token expira en 7 dias | JWT exp claim |
| RN-003 | Los tokens usan algoritmo RS256 | Asymmetric signing |
| RN-004 | El payload incluye jti (JWT ID) unico | UUID v4 |
| RN-005 | El issuer (iss) debe ser "erp-core" | JWT iss claim |
| RN-006 | El audience (aud) debe ser "erp-api" | JWT aud claim |
| RN-007 | El tenant_id es obligatorio (excepto superadmin) | Validacion en middleware |

### Estructura del JWT Payload

```json
{
  "sub": "user-uuid",           // Subject: User ID
  "tid": "tenant-uuid",         // Tenant ID
  "email": "user@example.com",  // Email del usuario
  "roles": ["admin", "user"],   // Roles del usuario
  "permissions": ["read", "write"], // Permisos directos
  "iat": 1701792000,            // Issued At
  "exp": 1701792900,            // Expiration (15 min)
  "iss": "erp-core",            // Issuer
  "aud": "erp-api",             // Audience
  "jti": "unique-token-id"      // JWT ID
}
```

---

## Impacto en Capas

### Database

| Elemento | Accion | Descripcion |
|----------|--------|-------------|
| Tabla | crear | `refresh_tokens` - almacenar refresh tokens |
| Tabla | crear | `revoked_tokens` - tokens revocados |
| Columna | - | `refresh_tokens.token_hash` (hash del token) |
| Columna | - | `refresh_tokens.expires_at` |
| Columna | - | `refresh_tokens.device_info` |
| Indice | crear | `idx_refresh_tokens_user` |
| Indice | crear | `idx_revoked_tokens_jti` |

### Backend

| Elemento | Accion | Descripcion |
|----------|--------|-------------|
| Service | crear | `TokenService` |
| Method | crear | `generateAccessToken()` |
| Method | crear | `generateRefreshToken()` |
| Method | crear | `validateToken()` |
| Method | crear | `decodeToken()` |
| Guard | crear | `JwtAuthGuard` |
| Middleware | crear | `TokenMiddleware` |
| Config | crear | JWT keys (public/private) |
| Interface | crear | `JwtPayload` |
| Interface | crear | `TokenPair` |

### Frontend

| Elemento | Accion | Descripcion |
|----------|--------|-------------|
| Service | crear | `tokenService` |
| Method | - | `getAccessToken()` |
| Method | - | `setTokens()` |
| Method | - | `clearTokens()` |
| Method | - | `isTokenExpired()` |
| Interceptor | crear | Axios interceptor para adjuntar token |
| Storage | usar | localStorage/sessionStorage para tokens |

---

## Dependencias

### Depende de (Bloqueantes)

| ID | Requerimiento | Estado |
|----|---------------|--------|
| RF-AUTH-001 | Login | Genera los tokens |

### Dependencias Relacionadas

| ID | Requerimiento | Relacion |
|----|---------------|----------|
| RF-AUTH-003 | Refresh Token | Usa refresh token |
| RF-AUTH-004 | Logout | Revoca tokens |

---

## Especificaciones Tecnicas

### Generacion de Claves RS256

```bash
# Generar clave privada
openssl genrsa -out private.key 2048

# Extraer clave publica
openssl rsa -in private.key -pubout -out public.key
```

### Configuracion de Tokens

```typescript
// config/jwt.config.ts
export const jwtConfig = {
  accessToken: {
    algorithm: 'RS256',
    expiresIn: '15m',
    issuer: 'erp-core',
    audience: 'erp-api',
  },
  refreshToken: {
    algorithm: 'RS256',
    expiresIn: '7d',
    issuer: 'erp-core',
    audience: 'erp-api',
  },
};
```

### Ejemplo de Token Generado

```
Header:
{
  "alg": "RS256",
  "typ": "JWT"
}

Payload:
{
  "sub": "550e8400-e29b-41d4-a716-446655440000",
  "tid": "tenant-123",
  "email": "user@example.com",
  "roles": ["admin"],
  "iat": 1701792000,
  "exp": 1701792900,
  "iss": "erp-core",
  "aud": "erp-api",
  "jti": "abc123"
}
```

---

## Datos de Prueba

| Escenario | Token | Resultado |
|-----------|-------|-----------|
| Token valido | JWT firmado correctamente | 200, acceso permitido |
| Token expirado | exp < now | 401, "Token expirado" |
| Firma invalida | Token modificado | 401, "Token invalido" |
| Sin token | Header Authorization vacio | 401, "Token requerido" |
| Formato incorrecto | "Bearer abc123" (no JWT) | 401, "Token malformado" |

---

## Estimacion

| Capa | Story Points | Notas |
|------|--------------|-------|
| Database | 2 | Tablas refresh_tokens, revoked_tokens |
| Backend | 5 | TokenService, Guards, Middleware |
| Frontend | 2 | Token storage e interceptors |
| **Total** | **9** | |

---

## Notas Adicionales

- Las claves privadas deben almacenarse en variables de entorno o secrets manager
- Considerar rotacion de claves cada 90 dias
- Implementar JWK (JSON Web Key) endpoint para distribuir clave publica
- El refresh token NO debe almacenarse en localStorage (usar httpOnly cookie)
- Implementar token blacklist en Redis para logout inmediato

---

## Historial de Cambios

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

---

## Aprobaciones

| Rol | Nombre | Fecha | Firma |
|-----|--------|-------|-------|
| Analista | System | 2025-12-05 | [x] |
| Tech Lead | - | - | [ ] |
| Product Owner | - | - | [ ] |
