ML Engine Updates: - Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records - Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence) - Backtest results: +176.71R profit with aggressive_filter strategy Documentation Consolidation: - Created docs/99-analisis/_MAP.md index with 13 new analysis documents - Consolidated inventories: removed duplicates from orchestration/inventarios/ - Updated ML_INVENTORY.yml with BTCUSD metrics and training results - Added execution reports: FASE11-BTCUSD, correction issues, alignment validation Architecture & Integration: - Updated all module documentation with NEXUS v3.4 frontmatter - Fixed _MAP.md indexes across all folders - Updated orchestration plans and traces Files: 229 changed, 5064 insertions(+), 1872 deletions(-) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
395 lines
16 KiB
Markdown
395 lines
16 KiB
Markdown
---
|
|
id: "RF-AUTH-004"
|
|
title: "Two-Factor Authentication (2FA)"
|
|
type: "Requirement"
|
|
status: "Done"
|
|
priority: "Alta"
|
|
module: "auth"
|
|
epic: "OQI-001"
|
|
version: "1.0"
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# RF-AUTH-004: Two-Factor Authentication (2FA)
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Estado:** ✅ Implementado
|
|
**Prioridad:** P1 (Alta)
|
|
**Épica:** [OQI-001](../_MAP.md)
|
|
|
|
---
|
|
|
|
## Descripción
|
|
|
|
El sistema debe permitir a los usuarios habilitar autenticación de dos factores (2FA) usando TOTP (Time-based One-Time Password), compatible con aplicaciones como Google Authenticator, Authy, y 1Password.
|
|
|
|
---
|
|
|
|
## Objetivo de Negocio
|
|
|
|
- Aumentar seguridad de cuentas con fondos
|
|
- Cumplir con estándares de seguridad financiera
|
|
- Reducir riesgo de acceso no autorizado
|
|
- Generar confianza en la plataforma
|
|
|
|
---
|
|
|
|
## Requisitos Funcionales
|
|
|
|
### RF-AUTH-004.1: Configuración de 2FA
|
|
|
|
**DEBE:**
|
|
1. Generar secreto TOTP único por usuario
|
|
2. Generar código QR para escanear
|
|
3. Mostrar secreto manual como alternativa
|
|
4. Requerir verificación antes de activar
|
|
5. Generar backup codes (10 códigos)
|
|
|
|
### RF-AUTH-004.2: Verificación 2FA
|
|
|
|
**DEBE:**
|
|
1. Solicitar código 2FA después de login exitoso
|
|
2. Validar código TOTP (ventana de ±1)
|
|
3. Aceptar backup codes como alternativa
|
|
4. Invalidar backup code después de uso
|
|
5. Bloquear después de 5 intentos fallidos
|
|
|
|
### RF-AUTH-004.3: Desactivación de 2FA
|
|
|
|
**DEBE:**
|
|
1. Requerir código 2FA válido para desactivar
|
|
2. Alternativa: backup code para desactivar
|
|
3. Invalidar todos los backup codes
|
|
4. Notificar por email al desactivar
|
|
|
|
### RF-AUTH-004.4: Recuperación de 2FA
|
|
|
|
**DEBE:**
|
|
1. Permitir uso de backup codes si pierde dispositivo
|
|
2. Proceso de recuperación con verificación de identidad
|
|
3. Notificar intentos de recuperación sospechosos
|
|
|
|
---
|
|
|
|
## Especificación TOTP
|
|
|
|
### Parámetros
|
|
|
|
| Parámetro | Valor |
|
|
|-----------|-------|
|
|
| Algoritmo | SHA1 |
|
|
| Dígitos | 6 |
|
|
| Período | 30 segundos |
|
|
| Ventana | ±1 período (tolerancia) |
|
|
|
|
### Generación de Secreto
|
|
|
|
```typescript
|
|
import speakeasy from 'speakeasy';
|
|
|
|
const secret = speakeasy.generateSecret({
|
|
length: 20,
|
|
name: 'Trading Platform',
|
|
issuer: 'Trading Platform',
|
|
});
|
|
|
|
// secret.base32 → almacenar encriptado
|
|
// secret.otpauth_url → para QR code
|
|
```
|
|
|
|
### Verificación de Código
|
|
|
|
```typescript
|
|
const verified = speakeasy.totp.verify({
|
|
secret: userSecret,
|
|
encoding: 'base32',
|
|
token: userCode,
|
|
window: 1, // ±30 segundos
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Flujo de Configuración
|
|
|
|
```
|
|
Usuario Frontend Backend DB
|
|
│ │ │ │
|
|
│─── "Activar 2FA" ───────▶│ │ │
|
|
│ │ │ │
|
|
│ │─── POST /auth/2fa/setup ▶│ │
|
|
│ │ │ │
|
|
│ │ │─── Generate secret ────│
|
|
│ │ │─── Generate QR code ───│
|
|
│ │ │─── Store temp secret ──│
|
|
│ │ │ │
|
|
│ │◀── 200 OK ───────────────│ │
|
|
│ │ { qrCode, secret } │ │
|
|
│ │ │ │
|
|
│◀── Muestra QR code ──────│ │ │
|
|
│ │ │ │
|
|
│─── Escanea con App ──────│ │ │
|
|
│ │ │ │
|
|
│─── Ingresa código ──────▶│ │ │
|
|
│ "123456" │ │ │
|
|
│ │ │ │
|
|
│ │─── POST /auth/2fa/enable▶│ │
|
|
│ │ { code: "123456" } │ │
|
|
│ │ │ │
|
|
│ │ │─── Verify TOTP ────────│
|
|
│ │ │ │
|
|
│ │ │─── Generate backup ────│
|
|
│ │ │ codes (10) │
|
|
│ │ │ │
|
|
│ │ │─── UPDATE user ───────▶│
|
|
│ │ │ 2fa_enabled = true │
|
|
│ │ │ 2fa_secret = xxx │
|
|
│ │ │ │
|
|
│ │◀── 200 OK ───────────────│ │
|
|
│ │ { backupCodes } │ │
|
|
│ │ │ │
|
|
│◀── Muestra backup codes ─│ │ │
|
|
│ "Guarda estos códigos"│ │ │
|
|
```
|
|
|
|
---
|
|
|
|
## Flujo de Login con 2FA
|
|
|
|
```
|
|
Usuario Frontend Backend DB
|
|
│ │ │ │
|
|
│─── Login email/pass ────▶│ │ │
|
|
│ │ │ │
|
|
│ │─── POST /auth/login ────▶│ │
|
|
│ │ │ │
|
|
│ │ │─── Validate creds ─────│
|
|
│ │ │─── Check 2FA enabled ──│
|
|
│ │ │ │
|
|
│ │◀── 200 OK ───────────────│ │
|
|
│ │ { requires2FA: true, │ │
|
|
│ │ tempToken } │ │
|
|
│ │ │ │
|
|
│◀── Muestra input 2FA ────│ │ │
|
|
│ │ │ │
|
|
│─── Ingresa código ──────▶│ │ │
|
|
│ "123456" │ │ │
|
|
│ │ │ │
|
|
│ │─── POST /auth/2fa/verify▶│ │
|
|
│ │ { tempToken, code } │ │
|
|
│ │ │ │
|
|
│ │ │─── Verify tempToken ───│
|
|
│ │ │─── Verify TOTP ────────│
|
|
│ │ │─── Generate JWT ───────│
|
|
│ │ │─── Create session ────▶│
|
|
│ │ │ │
|
|
│ │◀── 200 OK ───────────────│ │
|
|
│ │ { tokens, user } │ │
|
|
│ │ │ │
|
|
│◀── Dashboard ────────────│ │ │
|
|
```
|
|
|
|
---
|
|
|
|
## Backup Codes
|
|
|
|
### Generación
|
|
|
|
```typescript
|
|
function generateBackupCodes(count: number = 10): string[] {
|
|
const codes: string[] = [];
|
|
for (let i = 0; i < count; i++) {
|
|
// 8 caracteres alfanuméricos en mayúsculas
|
|
const code = crypto.randomBytes(4).toString('hex').toUpperCase();
|
|
codes.push(code);
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
// Ejemplo: ["A1B2C3D4", "E5F6G7H8", ...]
|
|
```
|
|
|
|
### Almacenamiento
|
|
|
|
```typescript
|
|
// Hashear cada código antes de almacenar
|
|
const hashedCodes = backupCodes.map(code =>
|
|
bcrypt.hashSync(code, 10)
|
|
);
|
|
|
|
// Almacenar en DB
|
|
await db.query(`
|
|
UPDATE users
|
|
SET backup_codes = $1
|
|
WHERE id = $2
|
|
`, [JSON.stringify(hashedCodes), userId]);
|
|
```
|
|
|
|
### Uso de Backup Code
|
|
|
|
1. Usuario ingresa backup code en lugar de TOTP
|
|
2. Sistema busca en lista de códigos hasheados
|
|
3. Si coincide, invalida ese código (elimina de lista)
|
|
4. Permite acceso
|
|
5. Notifica por email que se usó backup code
|
|
|
|
---
|
|
|
|
## Interfaz de Usuario
|
|
|
|
### Pantalla de Configuración
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Configurar 2FA │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 1. Escanea este código QR con tu aplicación │
|
|
│ │
|
|
│ ┌─────────────────┐ │
|
|
│ │ ▄▄▄▄▄ ▄▄▄▄▄ │ │
|
|
│ │ █ █ █ █ │ Google Authenticator │
|
|
│ │ █████ █████ │ Microsoft Authenticator │
|
|
│ │ █ █ █ █ │ Authy │
|
|
│ │ ▀▀▀▀▀ ▀▀▀▀▀ │ 1Password │
|
|
│ └─────────────────┘ │
|
|
│ │
|
|
│ 2. O ingresa este código manualmente: │
|
|
│ JBSWY3DPEHPK3PXP │
|
|
│ │
|
|
│ 3. Ingresa el código de 6 dígitos: │
|
|
│ ┌──────────────────────────┐ │
|
|
│ │ _ _ _ _ _ _ │ │
|
|
│ └──────────────────────────┘ │
|
|
│ │
|
|
│ [Cancelar] [Verificar y Activar] │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Pantalla de Backup Codes
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Códigos de Respaldo │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ⚠️ Guarda estos códigos en un lugar seguro. │
|
|
│ Cada código solo puede usarse una vez. │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ A1B2C3D4 E5F6G7H8 I9J0K1L2 M3N4O5P6 │ │
|
|
│ │ Q7R8S9T0 U1V2W3X4 Y5Z6A7B8 C9D0E1F2 │ │
|
|
│ │ G3H4I5J6 K7L8M9N0 │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ [Descargar como archivo] [Copiar al portapapeles] │
|
|
│ │
|
|
│ ☑ He guardado mis códigos de respaldo │
|
|
│ │
|
|
│ [Continuar] │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Manejo de Errores
|
|
|
|
| Error | Código | Mensaje Usuario |
|
|
|-------|--------|-----------------|
|
|
| Código inválido | 401 | Código incorrecto, intenta de nuevo |
|
|
| Código expirado | 401 | Código expirado, usa el actual |
|
|
| Muchos intentos | 429 | Demasiados intentos, espera X minutos |
|
|
| 2FA no configurado | 400 | 2FA no está configurado |
|
|
| Backup code inválido | 401 | Código de respaldo inválido |
|
|
| Sin backup codes | 400 | No tienes códigos de respaldo |
|
|
|
|
---
|
|
|
|
## Notificaciones por Email
|
|
|
|
### 2FA Activado
|
|
|
|
```
|
|
Asunto: 2FA activado en tu cuenta Trading Platform
|
|
|
|
Hola {{firstName}},
|
|
|
|
La autenticación de dos factores ha sido activada en tu cuenta.
|
|
|
|
Fecha: {{date}}
|
|
Dispositivo: {{device}}
|
|
IP: {{ip}}
|
|
|
|
Si no realizaste esta acción, contacta soporte inmediatamente.
|
|
|
|
Saludos,
|
|
Equipo Trading Platform
|
|
```
|
|
|
|
### Backup Code Usado
|
|
|
|
```
|
|
Asunto: ⚠️ Código de respaldo usado en tu cuenta
|
|
|
|
Hola {{firstName}},
|
|
|
|
Se usó un código de respaldo para acceder a tu cuenta.
|
|
|
|
Fecha: {{date}}
|
|
IP: {{ip}}
|
|
|
|
Códigos restantes: {{remainingCodes}}
|
|
|
|
Si no fuiste tú, cambia tu contraseña y contacta soporte.
|
|
|
|
Saludos,
|
|
Equipo Trading Platform
|
|
```
|
|
|
|
---
|
|
|
|
## Seguridad
|
|
|
|
### Almacenamiento del Secreto
|
|
|
|
1. **Encriptar** secreto TOTP antes de almacenar
|
|
2. Usar **AES-256-GCM** para encriptación
|
|
3. Clave de encriptación en variables de entorno
|
|
4. **No** loguear secretos TOTP
|
|
|
|
### Protección contra Replay
|
|
|
|
1. **Tracking** de códigos usados (últimos 60 segundos)
|
|
2. **Rechazar** códigos ya usados en la misma ventana
|
|
3. **Limpiar** tracking después de cada ventana
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
- [ ] Usuario puede generar secreto TOTP
|
|
- [ ] Código QR se muestra correctamente
|
|
- [ ] Código manual se puede copiar
|
|
- [ ] Usuario puede verificar y activar 2FA
|
|
- [ ] Se generan 10 backup codes
|
|
- [ ] Login requiere 2FA si está activado
|
|
- [ ] Códigos TOTP válidos permiten acceso
|
|
- [ ] Backup codes funcionan como alternativa
|
|
- [ ] Backup codes se invalidan después de uso
|
|
- [ ] Usuario puede desactivar 2FA
|
|
- [ ] Notificaciones se envían correctamente
|
|
|
|
---
|
|
|
|
## Especificación Técnica Relacionada
|
|
|
|
- [ET-AUTH-005: Seguridad](../especificaciones/ET-AUTH-005-security.md)
|
|
|
|
## Historias de Usuario Relacionadas
|
|
|
|
- [US-AUTH-010: Configurar 2FA](../historias-usuario/US-AUTH-010-2fa-setup.md)
|