# 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 }` | `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 `` y `` --- ### 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 `` --- ### 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 `` | 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 fetchSessions: () => Promise revokeSession: (sessionId: string) => Promise revokeAllSessions: () => Promise 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 `` 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 `` - 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 } ``` **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*