trading-platform/orchestration/analisis/OQI-001-ANALISIS-COMPONENTES.md
Adrian Flores Cortes 76b0ced338 [TASK-002] docs: Auditoria comprehensiva frontend trading-platform
Analisis exhaustivo CAPVED de 9 epics (OQI-001 a OQI-009) con:
- 48 documentos generados (~19,000 lineas)
- 122+ componentes analizados
- 113 endpoints API mapeados
- 30 gaps criticos identificados
- Roadmap de implementacion (2,457h esfuerzo)
- 9 subagentes en paralelo (2.5-3h vs 20h)

Hallazgos principales:
- 38% completitud promedio
- 10 gaps bloqueantes (P0)
- OQI-009 (MT4) 0% funcional
- OQI-005 (Pagos) PCI-DSS non-compliant
- Test coverage <10%

Entregables:
- EXECUTIVE-SUMMARY.md (reporte ejecutivo)
- 02-ANALISIS.md (consolidado 9 epics)
- 48 docs tecnicos por epic (componentes, APIs, gaps)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 12:57:14 -06:00

563 lines
17 KiB
Markdown

# OQI-001: Análisis de Componentes - Fundamentos-Auth
**Módulo:** OQI-001 (fundamentos-auth)
**Ruta:** `apps/frontend/src/modules/auth/`
**Progreso:** 70%
**Fecha:** 2026-01-25
**Status:** ANÁLISIS COMPLETO
---
## COMPONENTES - TABLA CONSOLIDADA
| Componente | Función | Props | Hooks | Estados | Eventos |
|---|---|---|---|---|---|
| **Login.tsx** | Página principal de inicio de sesión con métodos email/teléfono | `none` | `useState`, `useNavigate` | `loginMethod`, `showPassword`, `isLoading`, `error`, `formData`, `requiresTwoFactor` | `handleEmailLogin()` |
| **Register.tsx** | Página de registro de nueva cuenta con validación de contraseña | `none` | `useState`, `useNavigate` | `showPassword`, `isLoading`, `error`, `success`, `formData` | `handleRegister()`, `PasswordCheck()` componente |
| **ForgotPassword.tsx** | Página para solicitar recuperación de contraseña | `none` | `useState` | `email`, `isLoading`, `submitted`, `error` | `handleSubmit()` |
| **VerifyEmail.tsx** | Página de verificación de email con token | `none` | `useState`, `useEffect`, `useSearchParams` | `status`, `error`, `token` (query param) | `verifyEmail()` - llamada automática |
| **ResetPassword.tsx** | Página para establecer nueva contraseña con token | `none` | `useState`, `useEffect`, `useSearchParams`, `useNavigate` | `showPassword`, `isLoading`, `status`, `error`, `formData`, `token` (query param) | `handleSubmit()`, `PasswordCheck()` componente |
| **AuthCallback.tsx** | Página callback para OAuth y manejo de tokens | `none` | `useState`, `useEffect`, `useNavigate`, `useSearchParams` | `status`, `error`, `accessToken`, `refreshToken`, `isNewUser`, `returnUrl` (query params) | `getErrorMessage()`, redirección automática |
| **SecuritySettings.tsx** | Página principal de configuración de seguridad con tabs | `none` | `useState`, `useNavigate` | `activeTab` | Cambio de tabs, navegación |
| **SocialLoginButtons** | Componente de botones OAuth (Google, Facebook, X, Apple, GitHub) | `mode?: 'login' \| 'register'` | `useState` | `isLoading` (por provider) | `handleSocialLogin()` |
| **PhoneLoginForm** | Componente de formulario de login por teléfono (WhatsApp/SMS) | `none` | `useState`, `useNavigate` | `step`, `channel`, `isLoading`, `error`, `formData`, `otpExpiresAt` | `handleSendOTP()`, `handleVerifyOTP()`, `handleResendOTP()` |
| **SessionsList** | Componente que lista sesiones activas del usuario | `none` | `useState`, `useEffect` (custom Zustand store) | `sessions`, `loading`, `error`, `revoking` (Set), `showRevokeAllConfirm` | `fetchSessions()`, `revokeSession()`, `revokeAllSessions()`, `handleRevokeAll()` |
| **DeviceCard** | Componente que renderiza una tarjeta individual de sesión/dispositivo | `{ session: ActiveSession, isRevoking: boolean, onRevoke: (sessionId) => Promise<void> }` | `useState` | `showConfirm` | `handleRevoke()`, toggle confirmación |
---
## PÁGINAS (7 Total)
### 1. Login.tsx
**Ubicación:** `/pages/Login.tsx`
**Líneas:** 1-230
**Función Principal:**
Página de inicio de sesión con soporte para email/contraseña, teléfono (SMS/WhatsApp), y OAuth social.
**Props:** Ninguno (componente página)
**Hooks Utilizados:**
- `useState()` - Gestión de estado local (loginMethod, showPassword, isLoading, error, formData, requiresTwoFactor)
- `useNavigate()` - Navegación programática tras login exitoso
**Estados Principales:**
```typescript
loginMethod: 'email' | 'phone'
showPassword: boolean
isLoading: boolean
error: string | null
formData: {
email: string
password: string
rememberMe: boolean
totpCode: string
}
requiresTwoFactor: boolean
```
**Eventos/Métodos:**
- `handleEmailLogin()` - POST /api/v1/auth/login
- Soporta 2FA (TOTP)
- Guarda tokens en localStorage
- Redirige a /dashboard
**Integración:** Utiliza `<SocialLoginButtons />` y `<PhoneLoginForm />`
---
### 2. Register.tsx
**Ubicación:** `/pages/Register.tsx`
**Líneas:** 1-258
**Función Principal:**
Registro de nuevos usuarios con validación robusta de contraseña.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - Estado del formulario, loading, error, success
- `useNavigate()` - Redirección tras registro exitoso
**Estados Principales:**
```typescript
showPassword: boolean
isLoading: boolean
error: string | null
success: boolean
formData: {
firstName: string
lastName: string
email: string
password: string
confirmPassword: string
acceptTerms: boolean
}
// Password validation
passwordChecks: {
length: boolean (8 caracteres)
uppercase: boolean (/[A-Z]/)
lowercase: boolean (/[a-z]/)
number: boolean (/[0-9]/)
special: boolean (/[!@#$%^&*(),.?":{}|<>]/)
match: boolean (password === confirmPassword)
}
```
**Eventos/Métodos:**
- `handleRegister()` - POST /api/v1/auth/register
- `PasswordCheck()` - Componente interno para mostrar requerimientos
**Validaciones:**
- 5 requerimientos de contraseña (8+, mayúscula, minúscula, número, especial)
- Confirmación de contraseña
- Aceptación de términos
**Integración:** Utiliza `<SocialLoginButtons mode="register" />`
---
### 3. ForgotPassword.tsx
**Ubicación:** `/pages/ForgotPassword.tsx`
**Líneas:** 1-119
**Función Principal:**
Solicitud de recuperación de contraseña por email.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - Email, isLoading, submitted, error
- `useNavigate()` - Navegación
**Estados Principales:**
```typescript
email: string
isLoading: boolean
submitted: boolean
error: string | null
```
**Eventos/Métodos:**
- `handleSubmit()` - POST /api/v1/auth/forgot-password
- Mensaje genérico (privacidad): "Si existe cuenta... recibirás enlace"
---
### 4. VerifyEmail.tsx
**Ubicación:** `/pages/VerifyEmail.tsx`
**Líneas:** 1-98
**Función Principal:**
Verificar email del usuario mediante token de verificación.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - Status (loading|success|error|no-token), error
- `useEffect()` - Llamada automática a verifyEmail() si token existe
- `useSearchParams()` - Obtención del token (?token=xxx)
**Estados Principales:**
```typescript
status: 'loading' | 'success' | 'error' | 'no-token'
error: string | null
token: string | null (query param)
```
**Eventos/Métodos:**
- `verifyEmail(token)` - POST /api/v1/auth/verify-email
- Ejecutado automáticamente en useEffect
**Flujos UI:**
1. **loading:** Spinner "Verificando email..."
2. **success:** CheckCircle icon + "Email Verificado" + link a Login
3. **no-token:** Mail icon + instrucción para hacer clic en email
4. **error:** XCircle icon + mensaje de error + botones de intento
---
### 5. ResetPassword.tsx
**Ubicación:** `/pages/ResetPassword.tsx`
**Líneas:** 1-210
**Función Principal:**
Establecer nueva contraseña con token de recuperación.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - Contraseñas, loading, status, error, formData
- `useEffect()` - Validación de token en mount
- `useNavigate()` - Redirección tras éxito
- `useSearchParams()` - Obtención de token
**Estados Principales:**
```typescript
showPassword: boolean
isLoading: boolean
status: 'form' | 'success' | 'error' | 'no-token'
error: string | null
formData: {
password: string
confirmPassword: string
}
token: string | null
```
**Eventos/Métodos:**
- `handleSubmit()` - POST /api/v1/auth/reset-password
- Validación igual a Register (5 requerimientos)
**Flujos UI:**
1. **form:** Formulario de contraseña nueva
2. **success:** CheckCircle + redirige a Login
3. **error:** Error message + opción de reintentar
4. **no-token:** Token inválido/expirado
---
### 6. AuthCallback.tsx
**Ubicación:** `/pages/AuthCallback.tsx`
**Líneas:** 1-96
**Función Principal:**
Callback para OAuth y manejo de tokens post-autenticación.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - Status, error
- `useEffect()` - Procesamiento automático de query params
- `useNavigate()` - Redirección final
- `useSearchParams()` - Obtención de tokens y parámetros
**Query Params Esperados:**
```
?accessToken=xxx
&refreshToken=xxx
&isNewUser=true|false
&error=error_code
&returnUrl=/dashboard (default)
```
**Errores Soportados:**
- `invalid_state` - Sesión expirada
- `oauth_failed` - Conexión fallida con proveedor
- `oauth_error` - Error genérico de autenticación
- `missing_code_verifier` - Error de configuración
- `invalid_provider` - Proveedor no válido
**Flujos:**
1. **loading:** Spinner "Completando autenticación..."
2. **success:** CheckCircle + redirección a onboarding (nuevo) o returnUrl
3. **error:** XCircle + mensaje + botón "Volver a Iniciar Sesión"
---
### 7. SecuritySettings.tsx
**Ubicación:** `/pages/SecuritySettings.tsx`
**Líneas:** 1-274
**Función Principal:**
Panel principal de configuración de seguridad con 3 tabs: sesiones, contraseña, 2FA.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - activeTab
- `useNavigate()` - Navegación hacia atrás
- Custom SVG icons (BackIcon, ShieldIcon, KeyIcon, LockIcon, DevicesIcon)
**States Principales:**
```typescript
activeTab: 'sessions' | 'password' | 'two-factor'
```
**Tabs Contenidos:**
| Tab | Contenido | Features |
|-----|-----------|----------|
| **sessions** | Renderiza `<SessionsList />` | Listado y revocación de sesiones |
| **password** | Formulario de cambio de contraseña | 3 inputs (actual, nueva, confirmar) - SIN IMPLEMENTAR |
| **two-factor** | Opciones 2FA | Authenticator App y SMS - SIN IMPLEMENTAR (botones Setup) |
**Observaciones:**
- Tab password: Formulario básico sin handler
- Tab two-factor: UI completa pero botones "Setup" sin funcionalidad
- Integración con SessionsList component
---
## COMPONENTES (4 Total)
### 1. SocialLoginButtons.tsx
**Ubicación:** `/components/SocialLoginButtons.tsx`
**Líneas:** 1-129
**Función Principal:**
Botones OAuth para login/registro social.
**Props:**
```typescript
interface SocialLoginButtonsProps {
mode?: 'login' | 'register' // Default: 'login'
}
```
**Hooks Utilizados:**
- `useState()` - isLoading por provider
**Estados:**
```typescript
isLoading: string | null // Provider ID siendo procesado
```
**Providers Soportados (5):**
1. **Google** - bg-white, botón grande
2. **Facebook** - bg-[#1877F2], botón grande
3. **X (Twitter)** - bg-black, ícono pequeño
4. **Apple** - bg-black, ícono pequeño
5. **GitHub** - bg-[#24292e], ícono pequeño
**Eventos:**
- `handleSocialLogin(provider)` - GET /api/v1/auth/{provider}?returnUrl=...
- Redirige a data.data.authUrl (URL de OAuth del proveedor)
**Estructura UI:**
```
[Google | Facebook] (grid 2 cols)
[X | Apple | GitHub] (flex horizontal, iconos)
```
---
### 2. PhoneLoginForm.tsx
**Ubicación:** `/components/PhoneLoginForm.tsx`
**Líneas:** 1-264
**Función Principal:**
Formulario de login por teléfono con OTP (SMS/WhatsApp).
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - step, channel, isLoading, error, formData, otpExpiresAt
- `useNavigate()` - Redirección tras verificación
**Estados:**
```typescript
step: 'phone' | 'otp'
channel: 'whatsapp' | 'sms'
isLoading: boolean
error: string | null
formData: {
countryCode: string // Default: '52' (Mexico)
phoneNumber: string
otpCode: string
}
otpExpiresAt: Date | null
```
**Códigos de País Soportados (9):**
| País | Código | Flag |
|------|--------|------|
| México | 52 | MX |
| USA | 1 | US |
| Colombia | 57 | CO |
| Argentina | 54 | AR |
| Chile | 56 | CL |
| Perú | 51 | PE |
| Ecuador | 593 | EC |
| Venezuela | 58 | VE |
| España | 34 | ES |
**Flujo de Dos Pasos:**
**PASO 1: Phone**
- Seleccionar canal (WhatsApp/SMS)
- Seleccionar código de país
- Ingresar número
- POST /api/v1/auth/phone/send-otp
**PASO 2: OTP**
- Mostrar número enmascarado (+52 55 1234 5678)
- Input de 6 dígitos
- POST /api/v1/auth/phone/verify-otp
- Almacena tokens en localStorage
- Redirige a /dashboard
**Validaciones:**
- Número mínimo 8 dígitos
- OTP exactamente 6 dígitos
**Eventos:**
- `handleSendOTP()` - Envía OTP
- `handleVerifyOTP()` - Verifica código
- `handleResendOTP()` - Reenvía código
---
### 3. SessionsList.tsx
**Ubicación:** `/components/SessionsList.tsx`
**Líneas:** 1-196
**Función Principal:**
Listado de sesiones activas con opciones de revocación.
**Props:** Ninguno
**Hooks Utilizados:**
- `useState()` - showRevokeAllConfirm, revokeAllLoading
- `useEffect()` - fetchSessions en mount
- Custom Zustand store: `useSessionsStore()`
**Zustand Store Actions:**
```typescript
const {
sessions: ActiveSession[]
loading: boolean
error: string | null
revoking: Set<string>
fetchSessions: () => Promise<void>
revokeSession: (sessionId: string) => Promise<void>
revokeAllSessions: () => Promise<void>
clearError: () => void
} = useSessionsStore()
```
**Estados Locales:**
```typescript
showRevokeAllConfirm: boolean
revokeAllLoading: boolean
```
**Lógica Principal:**
- Ordena sesiones: actual primero, luego por lastActiveAt DESC
- Muestra contador: "N active sessions across your devices"
- Botón "Sign Out All Devices" si hay sesiones distintas a la actual
- Renderiza `<DeviceCard />` para cada sesión
**UI Estados:**
1. **loading:** Spinner "Loading sessions..."
2. **no sessions:** "No active sessions found" (vacío)
3. **with sessions:**
- Cada sesión es un `<DeviceCard />`
- Security tip: "If you see unrecognized device..."
4. **error:** Mensaje de error con botón cerrar
**Evento:**
- `handleRevokeAll()` - POST /api/v1/auth/logout-all
- Requiere confirmación
- Limpia localStorage y redirige a /login
---
### 4. DeviceCard.tsx
**Ubicación:** `/components/DeviceCard.tsx`
**Líneas:** 1-194
**Función Principal:**
Tarjeta individual de sesión/dispositivo con información y botón de revocación.
**Props:**
```typescript
interface DeviceCardProps {
session: ActiveSession
isRevoking: boolean
onRevoke: (sessionId: string) => Promise<void>
}
```
**Hooks Utilizados:**
- `useState()` - showConfirm
- Custom functions: `authService.parseUserAgent()`, `authService.formatRelativeTime()`
**Estados:**
```typescript
showConfirm: boolean
```
**Información Mostrada:**
| Campo | Origen | Formato |
|-------|--------|---------|
| Browser + OS | userAgent (parsed) | "Chrome on Windows 10/11" |
| Device Type | userAgent (parsed) | Desktop/Mobile/Tablet icon |
| IP Address | session.ipAddress | "10.0.0.1" o "Unknown IP" |
| Last Active | session.lastActiveAt | Relative time: "2 mins ago" |
| Session Created | session.createdAt | "1/25/2026, 3:30:45 PM" |
| Current Status | session.isCurrent | Badge verde "Current" |
**Device Type Icons (4):**
- **Desktop** - Monitor icon
- **Mobile** - Phone icon
- **Tablet** - Tablet icon
- **Unknown** - Question mark icon
**User Agent Parsing:**
```typescript
parseUserAgent(userAgent): {
type: 'desktop' | 'mobile' | 'tablet' | 'unknown'
os: string ('Windows 10/11', 'macOS', 'iOS', 'Android', etc)
browser: string ('Chrome', 'Safari', 'Firefox', 'Edge', etc)
}
```
**Styling:**
- **Sesión actual:** Fondo verde emergente, borde verde
- **Otras sesiones:** Fondo gris-oscuro, borde gris
**Botón Revocación:**
- **Sesión actual:** "Log Out" (gris)
- **Otras sesiones:** "Revoke" (rojo)
- Toggle de confirmación: "Confirm" / "Cancel"
**Evento:**
- `handleRevoke()` - Llama a onRevoke(sessionId)
- Para sesión actual: Logout + redirección a /login
---
## RESUMEN ESTADÍSTICO
| Métrica | Cantidad |
|---------|----------|
| **Páginas** | 7 |
| **Componentes** | 4 |
| **Total de archivos** | 11 |
| **Líneas de código (total)** | ~2,200 |
| **Hooks React utilizados** | useState, useEffect, useNavigate, useSearchParams |
| **State Management** | Zustand (SessionsList) + localStorage + useState |
| **APIs consumidas** | 14+ endpoints |
| **Validaciones** | Contraseña robusta, Email, Teléfono, Tokens |
| **Social Providers** | 5 (Google, Facebook, X, Apple, GitHub) |
| **Métodos 2FA** | TOTP (Login), SMS (PhoneLoginForm), Authenticator (TODO) |
| **Dispositivos soportados** | Desktop, Mobile, Tablet |
| **Idioma** | Español (es) |
---
## REFERENCIAS INTERNAS
- **Auth Service:** `/services/auth.service.ts` - API client y utilidades
- **Sessions Store:** `/stores/sessionsStore.ts` - Estado global de sesiones (Zustand)
- **Auth Types:** `/types/auth.types.ts` - Definiciones TypeScript
---
## NOTAS IMPORTANTES
1. **SocialLoginButtons:** Soporta modo login y registro
2. **PhoneLoginForm:** Integrado solo en Login (no en Register)
3. **DeviceCard:** Utiliza funciones helper de authService para parsing
4. **SecuritySettings:** Tabs 2FA y Password sin implementación completa
5. **Validación de Contraseña:** Idéntica en Register y ResetPassword
6. **Flujo 2FA:** Solo en Login (TOTP), SecuritySettings es placeholder
7. **Locales:** Mensajes en español, variables de environment para URLs
---
*Análisis completado: 2026-01-25*
*Módulo OQI-001 (fundamentos-auth) - 70% implementación*