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>
563 lines
17 KiB
Markdown
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*
|