# RF-AUTH-003: Autenticación por Teléfono

**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 autenticarse mediante número de teléfono usando OTP (One-Time Password) enviado por SMS o WhatsApp, facilitando el acceso a usuarios que prefieren no usar email o redes sociales.

---

## Objetivo de Negocio

- Ampliar opciones de autenticación para mercado LATAM
- Reducir fricción para usuarios móviles
- WhatsApp tiene alta penetración en México y LATAM
- Proporcionar alternativa sin necesidad de recordar contraseña

---

## Requisitos Funcionales

### RF-AUTH-003.1: Envío de OTP

**DEBE:**
1. Aceptar número de teléfono con código de país
2. Validar formato E.164 (+52XXXXXXXXXX)
3. Permitir selección de canal (SMS o WhatsApp)
4. Generar OTP de 6 dígitos
5. Almacenar OTP con TTL de 5 minutos
6. Enviar OTP via Twilio
7. Limitar envíos por número (3 cada 15 min)

### RF-AUTH-003.2: Verificación de OTP

**DEBE:**
1. Aceptar número de teléfono y OTP
2. Validar OTP contra almacenado
3. Invalidar OTP después de uso
4. Máximo 3 intentos por OTP
5. Crear o actualizar usuario
6. Generar tokens JWT

### RF-AUTH-003.3: Vinculación de Teléfono

**DEBE:**
1. Permitir agregar teléfono a cuenta existente
2. Verificar teléfono con OTP
3. Marcar teléfono como verificado
4. Un teléfono solo puede estar en una cuenta

---

## Canales Soportados

| Canal | Proveedor | Costo* | Velocidad |
|-------|-----------|--------|-----------|
| SMS | Twilio | $0.05 USD | 5-30 seg |
| WhatsApp | Twilio | $0.005 USD | 1-5 seg |

*Costos aproximados por mensaje en México

---

## Formato de Número

### Validación

```typescript
// Formato E.164
const phoneRegex = /^\+[1-9]\d{1,14}$/;

// Ejemplos válidos:
// +525512345678 (México)
// +5491123456789 (Argentina)
// +573001234567 (Colombia)
```

### Normalización

```typescript
function normalizePhone(phone: string): string {
  // Remover espacios, guiones, paréntesis
  return phone.replace(/[\s\-\(\)]/g, '');
}
```

---

## Flujo de Autenticación

```
Usuario                    Frontend                   Backend                   Twilio
   │                          │                          │                        │
   │─── Ingresa teléfono ────▶│                          │                        │
   │    +525512345678         │                          │                        │
   │    Selecciona: WhatsApp  │                          │                        │
   │                          │                          │                        │
   │                          │─── POST /auth/phone/send─▶                        │
   │                          │    { phone, channel }    │                        │
   │                          │                          │                        │
   │                          │                          │─── Check rate limit ───│
   │                          │                          │                        │
   │                          │                          │─── Generate OTP ───────│
   │                          │                          │    (6 digits)          │
   │                          │                          │                        │
   │                          │                          │─── Store in Redis ─────│
   │                          │                          │    TTL: 5 min          │
   │                          │                          │                        │
   │                          │                          │─── Send via Twilio ───▶│
   │                          │                          │                        │
   │                          │                          │◀── Message sent ───────│
   │                          │                          │                        │
   │                          │◀── 200 OK ───────────────│                        │
   │                          │    { message, expires }  │                        │
   │                          │                          │                        │
   │◀── "Código enviado" ─────│                          │                        │
   │                          │                          │                        │
   │═══ Recibe WhatsApp ══════════════════════════════════════════════════════════│
   │    "Tu código: 123456"   │                          │                        │
   │                          │                          │                        │
   │─── Ingresa 123456 ──────▶│                          │                        │
   │                          │                          │                        │
   │                          │─── POST /auth/phone/verify                        │
   │                          │    { phone, code }       │                        │
   │                          │                          │                        │
   │                          │                          │─── Validate OTP ───────│
   │                          │                          │                        │
   │                          │                          │─── Find/Create user ───│
   │                          │                          │                        │
   │                          │                          │─── Generate JWT ───────│
   │                          │                          │                        │
   │                          │◀── 200 OK ───────────────│                        │
   │                          │    { tokens, user }      │                        │
   │                          │                          │                        │
   │◀── Dashboard ────────────│                          │                        │
```

---

## Generación de OTP

```typescript
function generateOTP(): string {
  // 6 dígitos numéricos
  return Math.floor(100000 + Math.random() * 900000).toString();
}
```

### Almacenamiento en Redis

```typescript
// Key pattern
const key = `otp:${phone}`;

// Value structure
{
  "code": "123456",
  "attempts": 0,
  "channel": "whatsapp",
  "createdAt": "2025-12-05T10:00:00Z"
}

// TTL: 5 minutos
await redis.setEx(key, 300, JSON.stringify(otpData));
```

---

## Mensajes de OTP

### SMS

```
OrbiQuant: Tu código de verificación es 123456. Expira en 5 minutos.
```

### WhatsApp

```
🔐 *OrbiQuant IA*

Tu código de verificación es: *123456*

Este código expira en 5 minutos.
No compartas este código con nadie.
```

---

## Rate Limiting

### Por Número de Teléfono

| Período | Límite | Acción |
|---------|--------|--------|
| 15 min | 3 envíos | Bloqueo temporal |
| 1 hora | 5 envíos | Bloqueo 1 hora |
| 24 horas | 10 envíos | Bloqueo 24 horas |

### Por IP

| Período | Límite | Acción |
|---------|--------|--------|
| 15 min | 10 envíos | Bloqueo temporal |
| 1 hora | 20 envíos | Bloqueo 1 hora |

---

## Manejo de Errores

| Error | Código | Mensaje Usuario |
|-------|--------|-----------------|
| Teléfono inválido | 400 | Formato de teléfono inválido |
| Rate limit | 429 | Demasiados intentos, espera X minutos |
| OTP inválido | 401 | Código incorrecto |
| OTP expirado | 401 | Código expirado, solicita uno nuevo |
| Twilio error | 502 | Error al enviar mensaje, intenta de nuevo |
| Canal no soportado | 400 | Canal de envío no soportado |

---

## Integración Twilio

### Configuración

```env
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_PHONE_NUMBER=+15555555555
TWILIO_WHATSAPP_NUMBER=+14155238886
```

### Envío SMS

```typescript
await twilioClient.messages.create({
  body: `OrbiQuant: Tu código es ${otp}. Expira en 5 min.`,
  from: process.env.TWILIO_PHONE_NUMBER,
  to: phone,
});
```

### Envío WhatsApp

```typescript
await twilioClient.messages.create({
  body: `🔐 *OrbiQuant IA*\n\nTu código es: *${otp}*\n\nExpira en 5 minutos.`,
  from: `whatsapp:${process.env.TWILIO_WHATSAPP_NUMBER}`,
  to: `whatsapp:${phone}`,
});
```

---

## Reglas de Negocio

### RN-001: Teléfono Existente

Si el teléfono ya está registrado:
1. Enviar OTP al número
2. Al verificar, crear sesión con cuenta existente
3. No crear cuenta nueva

### RN-002: Teléfono Nuevo

Si el teléfono no está registrado:
1. Crear cuenta nueva con teléfono como identificador
2. Solicitar email posteriormente (opcional)
3. Asignar rol `investor` por defecto

### RN-003: Sin Email

Cuentas creadas solo con teléfono:
1. Pueden operar normalmente
2. Se les solicita email para ciertas funciones (inversión real)
3. Pueden agregar email después

---

## Consideraciones de Seguridad

### Prevención de Abuso

1. **Rate limiting agresivo** por número e IP
2. **CAPTCHA** después de 2 intentos fallidos
3. **Bloqueo progresivo** de números sospechosos
4. **Monitoreo** de patrones de abuso

### Protección de Datos

1. **No almacenar** OTPs en logs
2. **Encriptar** números de teléfono en BD
3. **Audit log** de todos los envíos
4. **Eliminar** OTPs después de verificación

---

## Criterios de Aceptación

- [ ] Usuario puede solicitar OTP por SMS
- [ ] Usuario puede solicitar OTP por WhatsApp
- [ ] OTP se recibe en menos de 30 segundos
- [ ] OTP expira después de 5 minutos
- [ ] Usuario puede verificar OTP correctamente
- [ ] Usuario bloqueado después de 3 intentos fallidos
- [ ] Rate limiting funciona correctamente
- [ ] Usuario existente puede hacer login con teléfono
- [ ] Usuario nuevo puede registrarse con teléfono
- [ ] Teléfono se puede vincular a cuenta existente

---

## Especificación Técnica Relacionada

- [ET-AUTH-004: API Endpoints](../especificaciones/ET-AUTH-004-api.md)

## Historias de Usuario Relacionadas

- [US-AUTH-008: Login con SMS](../historias-usuario/US-AUTH-008-phone-sms.md)
- [US-AUTH-009: Login con WhatsApp](../historias-usuario/US-AUTH-009-phone-whatsapp.md)
