---
id: "ET-AUTH-006"
title: "Frontend Specification for Auth Module"
type: "Specification"
status: "Done"
rf_parent: "RF-AUTH-003"
epic: "OQI-001"
version: "1.0"
created_date: "2026-01-25"
updated_date: "2026-01-25"
---

# ET-AUTH-006: Especificación Técnica - Frontend Auth

**Version:** 1.0.0
**Fecha:** 2026-01-25
**Estado:** ✅ Implementado
**Épica:** [OQI-001](../_MAP.md)

---

## Resumen

Esta especificación detalla la implementación frontend del módulo de autenticación de Trading Platform, incluyendo páginas, componentes reutilizables, servicios de API y flujos de usuario.

---

## Stack Tecnológico

| Componente | Tecnología | Versión |
|------------|-----------|---------|
| Framework | React | 18.2.0 |
| Build Tool | Vite | 6.2.0 |
| Router | React Router | 6.x |
| State Management | Zustand | 4.4.7 |
| Data Fetching | TanStack Query | 5.14.0 |
| Styling | CSS Modules + Tailwind | 3.x |
| Forms | React Hook Form | 7.x |
| Validación | Zod | 3.x |
| HTTP Client | Axios | 1.x |
| Auth Tokens | JWT | - |

---

## Arquitectura Frontend

```
apps/frontend/
├── src/
│   ├── pages/
│   │   ├── auth/
│   │   │   ├── Login.tsx
│   │   │   ├── Register.tsx
│   │   │   ├── ForgotPassword.tsx
│   │   │   ├── ResetPassword.tsx
│   │   │   ├── VerifyEmail.tsx
│   │   │   └── AuthCallback.tsx
│   ├── components/
│   │   ├── auth/
│   │   │   ├── SocialLoginButtons.tsx
│   │   │   ├── PhoneLoginForm.tsx
│   │   │   ├── TwoFactorForm.tsx
│   │   │   └── PasswordStrengthMeter.tsx
│   ├── services/
│   │   ├── api/
│   │   │   └── authService.ts
│   ├── stores/
│   │   └── authStore.ts
│   ├── hooks/
│   │   ├── useAuth.ts
│   │   ├── useAuthForm.ts
│   │   └── useSessionManager.ts
│   ├── types/
│   │   └── auth.ts
│   └── utils/
│       ├── tokenManager.ts
│       └── validators.ts
```

---

## Páginas de Autenticación

### 1. Login Page

**Ruta:** `/auth/login`

**Responsabilidades:**
- Autenticación con email/contraseña
- Redirección a OAuth (Google, GitHub, etc.)
- Recuperación de contraseña
- Manejo de 2FA si está activado
- Recordar usuario

**Flujo:**

```
1. Usuario ingresa email y contraseña
2. Validar formato de datos
3. Llamar a /api/v1/auth/login
4. Si 2FA está activado:
   → Mostrar formulario de verificación 2FA
   → Usuario ingresa código
   → Llamar a /api/v1/auth/2fa/verify
5. Guardar tokens en localStorage/sessionStorage
6. Actualizar auth store (Zustand)
7. Redirigir a /dashboard
```

**Props/State:**

```typescript
interface LoginPageProps {
  redirectTo?: string;
  onSuccess?: () => void;
}

interface LoginFormData {
  email: string;
  password: string;
  rememberMe: boolean;
}
```

**Manejo de Errores:**

| Código | Error | Acción |
|--------|-------|--------|
| INVALID_CREDENTIALS | Credenciales inválidas | Mostrar mensaje de error |
| EMAIL_NOT_VERIFIED | Email no verificado | Botón para reenviar verificación |
| ACCOUNT_LOCKED | Cuenta bloqueada | Mostrar tiempo de espera |
| ACCOUNT_SUSPENDED | Cuenta suspendida | Mostrar mensaje de contacto |
| RATE_LIMIT | Demasiados intentos | Desactivar botón por tiempo |

---

### 2. Register Page

**Ruta:** `/auth/register`

**Responsabilidades:**
- Registro con email/contraseña
- Validación de contraseña fuerte
- Aceptación de términos y condiciones
- Registro social (opcional)
- Envío de email de verificación

**Flujo:**

```
1. Usuario completa formulario:
   - Email
   - Contraseña
   - Confirmar contraseña
   - Nombre completo
   - Aceptar términos
2. Validar localmente (Zod)
3. Mostrar indicador de fortaleza de contraseña
4. Llamar a /api/v1/auth/register
5. Mostrar confirmación de email enviado
6. Redirigir a /auth/verify-email después de 3 segundos
```

**Props/State:**

```typescript
interface RegisterFormData {
  email: string;
  password: string;
  confirmPassword: string;
  firstName: string;
  lastName: string;
  acceptTerms: boolean;
  acceptNewsletter?: boolean;
}

interface PasswordRequirements {
  minLength: boolean;
  hasUppercase: boolean;
  hasLowercase: boolean;
  hasNumber: boolean;
  hasSpecialChar: boolean;
}
```

**Validaciones:**

- Email válido y único
- Contraseña: mínimo 12 caracteres, 1 mayúscula, 1 minúscula, 1 número, 1 carácter especial
- Contraseñas coinciden
- Edad mínima 18 años (si aplica)
- Términos aceptados

---

### 3. Forgot Password Page

**Ruta:** `/auth/forgot-password`

**Responsabilidades:**
- Solicitar recuperación de contraseña
- Validar email
- Enviar link de recuperación
- Mostrar confirmación

**Flujo:**

```
1. Usuario ingresa email
2. Validar formato
3. Llamar a /api/v1/auth/forgot-password
4. Mostrar mensaje de confirmación
   "Si la cuenta existe, recibirás instrucciones por email"
5. Mostrar campo para ingresar código alternativo
6. Opción de volver a login
```

**Seguridad:**
- No revelar si email existe
- Rate limiting en lado cliente
- Email enviado con validación de seguridad

---

### 4. Reset Password Page

**Ruta:** `/auth/reset-password?token=xxx`

**Responsabilidades:**
- Validar token de recuperación
- Permitir cambio de contraseña
- Verificar nueva contraseña

**Flujo:**

```
1. Extraer token del URL
2. Validar que token es válido en cliente
3. Usuario ingresa nueva contraseña
4. Mostrar indicador de fortaleza
5. Validar confirmación
6. Llamar a /api/v1/auth/reset-password
7. Mostrar confirmación
8. Redirigir a /auth/login después de 3 segundos
```

**Manejo de Errores:**

| Error | Acción |
|-------|--------|
| Token inválido | Mostrar formulario para solicitar nuevo |
| Token expirado | Botón para reenviar |

---

### 5. Verify Email Page

**Ruta:** `/auth/verify-email`

**Responsabilidades:**
- Mostrar instrucciones de verificación
- Permitir ingreso manual de código
- Resenviar email de verificación
- Validar código de verificación

**Flujo:**

```
1. Mostrar mensaje: "Revisa tu email para verificar tu cuenta"
2. Campo para ingresar código de 6 dígitos
3. Link para reenviar email (con rate limiting)
4. Contador de tiempo restante para expiración
5. Llamar a /api/v1/auth/verify-email con token
6. Mostrar confirmación
7. Botón "Continuar a login"
```

**Validaciones:**
- Código de 6 dígitos
- No expirado
- Formato correcto

---

### 6. Auth Callback Page

**Ruta:** `/auth/callback/:provider?code=xxx&state=xxx`

**Responsabilidades:**
- Procesar callback de OAuth
- Intercambiar código por tokens
- Manejar errores de OAuth
- Redirigir según resultado

**Flujo:**

```
1. Página muestra loading spinner
2. Extraer code y state del URL
3. Validar state para prevenir CSRF
4. Llamar a /api/v1/auth/oauth/{provider}
5. Si éxito:
   → Guardar tokens
   → Redirigir a redirectTo o /dashboard
6. Si error:
   → Mostrar mensaje de error
   → Botón para volver a intentar
   → Botón para ir a login
```

**Providers Soportados:**
- Google
- GitHub
- Apple (iOS)
- Microsoft

---

## Componentes Reutilizables

### SocialLoginButtons

**Ubicación:** `src/components/auth/SocialLoginButtons.tsx`

**Props:**

```typescript
interface SocialLoginButtonsProps {
  onSuccess?: (user: AuthUser) => void;
  onError?: (error: Error) => void;
  className?: string;
  showLabels?: boolean;
  providers?: ('google' | 'github' | 'apple' | 'microsoft')[];
}
```

**Funcionalidades:**

```
1. Botones para cada proveedor OAuth
2. Iconos proporcionados por providers
3. Hover y estados activos
4. Loading state durante autenticación
5. Manejo de errores con mensajes claros
6. Responsivo en mobile
```

**Rendering:**

```jsx
<SocialLoginButtons
  providers={['google', 'github']}
  showLabels={true}
  onSuccess={handleSuccess}
/>
```

**Estilos:**
- Google: Azul (#4285F4)
- GitHub: Negro (#333)
- Apple: Negro (#000)
- Microsoft: Azul (#00A4EF)
- Ancho: 100% en mobile, auto en desktop
- Altura: 48px
- Spacing entre botones: 12px

---

### PhoneLoginForm

**Ubicación:** `src/components/auth/PhoneLoginForm.tsx`

**Props:**

```typescript
interface PhoneLoginFormProps {
  onSuccess?: (user: AuthUser, tokens: TokenResponse) => void;
  onError?: (error: Error) => void;
  defaultCountry?: string;
  disabled?: boolean;
}

interface PhoneLoginFormState {
  step: 'phone' | 'otp';
  phone: string;
  code: string;
  channel: 'sms' | 'whatsapp';
  expiresAt: Date;
  loading: boolean;
  error?: string;
}
```

**Flujo de Pasos:**

**Paso 1: Ingreso de Teléfono**

```
1. Selector de país con bandera
2. Campo de teléfono internacional
3. Selector de canal (SMS/WhatsApp)
4. Validar formato con libphonenumber-js
5. Botón "Enviar código"
```

**Paso 2: Verificación OTP**

```
1. Mostrar último 4 dígitos del teléfono
2. Campo de 6 dígitos (auto-focus después de cada)
3. Countdown timer (5 minutos)
4. Link "Reenviar código" (después de 30s)
5. Link "Cambiar número"
6. Botón "Verificar código"
```

**Validaciones:**

```typescript
const phoneRegex = /^\+[1-9]\d{1,14}$/;
const otpRegex = /^\d{6}$/;
```

---

### TwoFactorForm

**Ubicación:** `src/components/auth/TwoFactorForm.tsx`

**Props:**

```typescript
interface TwoFactorFormProps {
  tempToken: string;
  methods: ('totp' | 'backup_code' | 'sms')[];
  onSuccess: (tokens: TokenResponse) => void;
  onError: (error: Error) => void;
}

interface TwoFactorFormState {
  method: 'totp' | 'backup_code' | 'sms';
  code: string;
  remainingAttempts: number;
  loading: boolean;
}
```

**Métodos Soportados:**

1. **TOTP (Authenticator)**
   - Campo de 6 dígitos
   - Auto-focus y validación en tiempo real
   - Contador de tiempo (30s por código)

2. **Backup Codes**
   - Campo de 8 caracteres (sin guiones)
   - Validación de formato
   - Mensaje de "código usado"

3. **SMS**
   - Reenviador de OTP a teléfono
   - Campo de 6 dígitos
   - Countdown timer

---

### PasswordStrengthMeter

**Ubicación:** `src/components/auth/PasswordStrengthMeter.tsx`

**Props:**

```typescript
interface PasswordStrengthMeterProps {
  password: string;
  showRequirements?: boolean;
  minLength?: number;
  className?: string;
}

interface PasswordRequirements {
  minLength: boolean;      // >= 12 caracteres
  hasUppercase: boolean;   // >= 1 mayúscula
  hasLowercase: boolean;   // >= 1 minúscula
  hasNumber: boolean;      // >= 1 número
  hasSpecialChar: boolean; // >= 1 carácter especial
}
```

**Rendering:**

```
Barra de fortaleza:
┌──────────────────────┐
│ █████░░░░░░░░░░░░░░│ Muy débil
└──────────────────────┘

Requisitos:
✓ Mínimo 12 caracteres
✗ Una mayúscula
✓ Una minúscula
✓ Un número
✗ Carácter especial
```

**Colores:**
- Rojo: Muy débil
- Naranja: Débil
- Amarillo: Medio
- Verde claro: Fuerte
- Verde oscuro: Muy fuerte

---

## Servicios de API

### authService.ts

**Ubicación:** `src/services/api/authService.ts`

**Métodos:**

```typescript
class AuthService {
  // Login
  login(email: string, password: string): Promise<LoginResponse>

  // Registro
  register(data: RegisterRequest): Promise<RegisterResponse>

  // Logout
  logout(): Promise<void>

  // Renovar tokens
  refreshTokens(refreshToken: string): Promise<TokenResponse>

  // Obtener usuario actual
  getCurrentUser(): Promise<UserData>

  // OAuth
  getOAuthUrl(provider: string, redirectTo?: string): Promise<{ authUrl: string; state: string }>
  oauth(provider: string, code: string, state: string): Promise<LoginResponse>
  unlinkOAuth(provider: string): Promise<void>

  // Teléfono
  sendPhoneOTP(phone: string, channel: 'sms' | 'whatsapp'): Promise<{ expiresAt: Date }>
  verifyPhoneOTP(phone: string, code: string): Promise<LoginResponse>

  // 2FA
  setupTwoFactor(): Promise<{ secret: string; qrCode: string; otpauthUrl: string }>
  enableTwoFactor(code: string): Promise<{ backupCodes: string[] }>
  verifyTwoFactor(tempToken: string, code: string): Promise<LoginResponse>
  disableTwoFactor(code: string): Promise<void>

  // Contraseña
  forgotPassword(email: string): Promise<void>
  resetPassword(token: string, password: string): Promise<void>

  // Email
  verifyEmail(token: string): Promise<void>
  resendVerificationEmail(email: string): Promise<void>

  // Sesiones
  getSessions(): Promise<Session[]>
  revokeSession(sessionId: string): Promise<void>
  revokeAllOtherSessions(): Promise<void>
}

export const authService = new AuthService();
```

**Configuración Base:**

```typescript
const apiClient = axios.create({
  baseURL: process.env.VITE_API_URL || 'http://localhost:3080/api/v1',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Interceptor para agregar token
apiClient.interceptors.request.use((config) => {
  const token = tokenManager.getAccessToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Interceptor para renovar token expirado
apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      const refreshToken = tokenManager.getRefreshToken();
      if (refreshToken) {
        // Intentar renovar
        const response = await refreshTokens(refreshToken);
        // Reintentar solicitud original
      } else {
        // Redirigir a login
      }
    }
    return Promise.reject(error);
  }
);
```

---

## Estado Global (Zustand)

### authStore.ts

**Ubicación:** `src/stores/authStore.ts`

```typescript
interface AuthState {
  // Estado
  user: AuthUser | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  error: Error | null;
  tokens: TokenResponse | null;

  // Métodos
  setUser: (user: AuthUser | null) => void;
  setTokens: (tokens: TokenResponse | null) => void;
  login: (email: string, password: string) => Promise<void>;
  register: (data: RegisterRequest) => Promise<void>;
  logout: () => Promise<void>;
  refreshUser: () => Promise<void>;
  clearError: () => void;

  // 2FA
  setRequires2FA: (requires: boolean) => void;
  setTempToken: (token: string) => void;
}

export const useAuthStore = create<AuthState>((set, get) => ({
  user: null,
  isAuthenticated: false,
  isLoading: false,
  error: null,
  tokens: null,

  setUser: (user) => set({ user, isAuthenticated: !!user }),

  setTokens: (tokens) => {
    set({ tokens });
    if (tokens) {
      tokenManager.setTokens(tokens);
    } else {
      tokenManager.clear();
    }
  },

  login: async (email, password) => {
    // Implementación
  },

  logout: async () => {
    set({ user: null, tokens: null, isAuthenticated: false });
    tokenManager.clear();
  },

  // ... otros métodos
}));
```

---

## Hooks Personalizados

### useAuth()

```typescript
function useAuth() {
  const store = useAuthStore();

  return {
    user: store.user,
    isAuthenticated: store.isAuthenticated,
    isLoading: store.isLoading,
    login: store.login,
    logout: store.logout,
    register: store.register,
  };
}

// Uso:
function MyComponent() {
  const { user, isAuthenticated, logout } = useAuth();

  if (!isAuthenticated) return null;

  return <div>Bienvenido {user?.firstName}</div>;
}
```

### useAuthForm()

```typescript
function useAuthForm(formType: 'login' | 'register' | 'resetPassword') {
  const form = useForm<LoginFormData | RegisterFormData>({
    resolver: zodResolver(getSchema(formType)),
    mode: 'onBlur',
  });

  const onSubmit = async (data) => {
    // Validar y enviar
  };

  return {
    form,
    isSubmitting: form.formState.isSubmitting,
    errors: form.formState.errors,
    onSubmit: form.handleSubmit(onSubmit),
  };
}
```

### useSessionManager()

```typescript
function useSessionManager() {
  const [sessions, setSessions] = useState<Session[]>([]);
  const [loading, setLoading] = useState(false);

  const loadSessions = async () => {
    setLoading(true);
    const data = await authService.getSessions();
    setSessions(data);
    setLoading(false);
  };

  const revokeSession = async (id: string) => {
    await authService.revokeSession(id);
    await loadSessions();
  };

  useEffect(() => {
    loadSessions();
  }, []);

  return { sessions, loading, revokeSession };
}
```

---

## Flujos de Usuario

### Flujo: Registrarse con Email

```
1. Usuario accede a /auth/register
2. Completa formulario:
   - Email
   - Contraseña (con validación en tiempo real)
   - Nombre
   - Acepta términos
3. Validación local (Zod)
4. POST /api/v1/auth/register
5. Respuesta: Usuario con status "pending_verification"
6. Redirigir a /auth/verify-email?email=xxx
7. Usuario ingresa código del email
8. POST /api/v1/auth/verify-email
9. Email verificado
10. Redirigir a /auth/login con mensaje "Cuenta verificada"
```

### Flujo: Iniciar Sesión con Email/Contraseña

```
1. Usuario accede a /auth/login
2. Ingresa email y contraseña
3. POST /api/v1/auth/login
4. Respuesta de servidor:
   a) Sin 2FA: tokens (accessToken, refreshToken)
   b) Con 2FA: tempToken y métodos disponibles
5. Si sin 2FA:
   - Guardar tokens en store
   - Redirigir a /dashboard
6. Si con 2FA:
   - Mostrar formulario de verificación
   - Usuario ingresa código
   - POST /api/v1/auth/2fa/verify
   - Guardar tokens
   - Redirigir a /dashboard
```

### Flujo: Iniciar Sesión con OAuth (Google)

```
1. Usuario haz clic en "Iniciar sesión con Google"
2. GET /api/v1/auth/oauth/google/url?redirectTo=/dashboard
3. Respuesta: authUrl + state
4. Redirigir a Google
5. Usuario autentica y otorga permisos
6. Google redirige a /auth/callback/google?code=xxx&state=xxx
7. POST /api/v1/auth/oauth/google { code, state }
8. Respuesta: tokens
9. Guardar en store
10. Redirigir a /dashboard
```

### Flujo: Cambiar Contraseña Olvidada

```
1. Usuario accede a /auth/forgot-password
2. Ingresa email
3. POST /api/v1/auth/forgot-password
4. Mostrar mensaje: "Si la cuenta existe..."
5. Usuario recibe email con link
6. Link dirigido a /auth/reset-password?token=xxx
7. Usuario ingresa nueva contraseña
8. POST /api/v1/auth/reset-password { token, password }
9. Mostrar confirmación
10. Redirigir a /auth/login
```

### Flujo: Activar 2FA (TOTP)

```
1. Usuario va a Settings > Seguridad
2. Haz clic en "Activar autenticación de dos factores"
3. POST /api/v1/auth/2fa/setup
4. Respuesta: QR code + secret
5. Usuario escanea con Google Authenticator/Authy
6. Usuario ingresa código de 6 dígitos
7. POST /api/v1/auth/2fa/enable { code }
8. Respuesta: Backup codes
9. Usuario descargar/copia backup codes
10. Mostrar confirmación: "2FA activado"
```

### Flujo: Iniciar Sesión con Teléfono

```
1. Usuario accede a /auth/phone-login
2. Selecciona país y ingresa teléfono
3. Elige canal: SMS o WhatsApp
4. POST /api/v1/auth/phone/send { phone, channel }
5. Mostrar "Código enviado a +52 555 1234..."
6. Usuario ingresa código de 6 dígitos
7. POST /api/v1/auth/phone/verify { phone, code }
8. Si usuario nuevo: Crear account automáticamente
9. Respuesta: tokens
10. Redirigir a /dashboard
```

---

## Seguridad

### Protección CSRF

```typescript
// Usar state token en OAuth
const state = crypto.randomUUID();
sessionStorage.setItem('oauth_state', state);

// Validar en callback
const urlState = new URLSearchParams(location.search).get('state');
if (urlState !== sessionStorage.getItem('oauth_state')) {
  throw new Error('Invalid state token');
}
```

### Manejo de Tokens

```typescript
class TokenManager {
  setTokens(tokens: TokenResponse) {
    sessionStorage.setItem('accessToken', tokens.accessToken);
    localStorage.setItem('refreshToken', tokens.refreshToken);
  }

  getAccessToken(): string | null {
    return sessionStorage.getItem('accessToken');
  }

  getRefreshToken(): string | null {
    return localStorage.getItem('refreshToken');
  }

  clear() {
    sessionStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
  }

  isTokenExpired(token: string): boolean {
    const decoded = jwtDecode(token);
    return decoded.exp * 1000 < Date.now();
  }
}
```

### Protección XSS

- Usar React (que escapa automáticamente)
- Sanitizar HTML si es necesario: `DOMPurify`
- Content Security Policy headers

### Validación de Inputs

```typescript
const schemas = {
  login: z.object({
    email: z.string().email('Email inválido'),
    password: z.string().min(1, 'Requerido'),
  }),
  register: z.object({
    email: z.string().email('Email inválido'),
    password: z.string()
      .min(12, 'Mínimo 12 caracteres')
      .regex(/[A-Z]/, 'Debe contener mayúscula')
      .regex(/[a-z]/, 'Debe contener minúscula')
      .regex(/[0-9]/, 'Debe contener número')
      .regex(/[^a-zA-Z0-9]/, 'Debe contener carácter especial'),
    confirmPassword: z.string(),
  }).refine((data) => data.password === data.confirmPassword, {
    message: 'Las contraseñas no coinciden',
    path: ['confirmPassword'],
  }),
};
```

---

## Rutas Protegidas

```typescript
function ProtectedRoute({ children }: { children: ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) return <LoadingSpinner />;
  if (!isAuthenticated) return <Navigate to="/auth/login" />;

  return children;
}

// Uso:
<Routes>
  <Route path="/auth/login" element={<LoginPage />} />
  <Route path="/dashboard" element={
    <ProtectedRoute>
      <DashboardPage />
    </ProtectedRoute>
  } />
</Routes>
```

---

## Testing

### Unit Tests

```typescript
describe('authService.login', () => {
  it('debe retornar tokens al login exitoso', async () => {
    const result = await authService.login('user@example.com', 'password');
    expect(result.data.tokens).toBeDefined();
    expect(result.data.user).toBeDefined();
  });

  it('debe lanzar error con credenciales inválidas', async () => {
    await expect(
      authService.login('user@example.com', 'wrong')
    ).rejects.toThrow('INVALID_CREDENTIALS');
  });
});
```

### Integration Tests

```typescript
describe('Login Flow', () => {
  it('debe completar flujo de login y redirigir', async () => {
    render(<LoginPage />);

    await userEvent.type(screen.getByLabelText('Email'), 'user@example.com');
    await userEvent.type(screen.getByLabelText('Password'), 'SecurePass123!');
    await userEvent.click(screen.getByText('Iniciar sesión'));

    await waitFor(() => {
      expect(window.location.pathname).toBe('/dashboard');
    });
  });
});
```

---

## Tipos TypeScript

**Ubicación:** `src/types/auth.ts`

```typescript
export interface AuthUser {
  id: string;
  email: string;
  firstName: string;
  lastName: string;
  phone?: string;
  role: 'investor' | 'admin' | 'support';
  status: 'active' | 'pending_verification' | 'suspended';
  emailVerified: boolean;
  phoneVerified: boolean;
  twoFactorEnabled: boolean;
  createdAt: Date;
  updatedAt: Date;
  profile?: {
    displayName: string;
    avatarUrl?: string;
    preferredLanguage: string;
    timezone: string;
  };
  oauthProviders: string[];
}

export interface TokenResponse {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
  tokenType: 'Bearer';
}

export interface LoginResponse {
  success: boolean;
  data: {
    user: AuthUser;
    tokens?: TokenResponse;
    requires2FA?: boolean;
    tempToken?: string;
    methods?: string[];
  };
}

export interface Session {
  id: string;
  device: {
    type: 'desktop' | 'mobile' | 'tablet';
    browser: string;
    browserVersion?: string;
    os: string;
    osVersion: string;
  };
  location: {
    city: string;
    country: string;
    countryCode: string;
  };
  ipAddress: string;
  lastActivity: Date;
  createdAt: Date;
  isCurrent: boolean;
}
```

---

## Checklist de Implementación

- [ ] Páginas de autenticación creadas
- [ ] Componentes reutilizables implementados
- [ ] Servicios de API configurados
- [ ] Estado Zustand inicializado
- [ ] Hooks personalizados desarrollados
- [ ] Rutas protegidas implementadas
- [ ] Validaciones con Zod aplicadas
- [ ] Manejo de errores completado
- [ ] Tests unitarios escritos
- [ ] Tests de integración completados
- [ ] Security review realizado
- [ ] Documentación de componentes lista
- [ ] CHANGELOG actualizado

---

## Performance

### Code Splitting

```typescript
const LoginPage = lazy(() => import('./pages/auth/LoginPage'));
const RegisterPage = lazy(() => import('./pages/auth/RegisterPage'));

<Suspense fallback={<LoadingSpinner />}>
  <Routes>
    <Route path="/auth/login" element={<LoginPage />} />
  </Routes>
</Suspense>
```

### Memoización

```typescript
const SocialLoginButtons = memo(function SocialLoginButtons(props) {
  // Componente
});
```

### Request Caching

```typescript
const { data: user } = useQuery({
  queryKey: ['auth', 'me'],
  queryFn: () => authService.getCurrentUser(),
  staleTime: 5 * 60 * 1000, // 5 minutos
});
```

---

## Referencias

- [ET-AUTH-001: OAuth Specification](./ET-AUTH-001-oauth.md)
- [ET-AUTH-002: JWT Specification](./ET-AUTH-002-jwt.md)
- [ET-AUTH-004: API Endpoints](./ET-AUTH-004-api.md)
- [ET-AUTH-005: Security](./ET-AUTH-005-security.md)
- [React Documentation](https://react.dev)
- [React Router Documentation](https://reactrouter.com)
- [Zustand Documentation](https://github.com/pmndrs/zustand)
- [React Hook Form Documentation](https://react-hook-form.com)
- [Zod Validation Library](https://zod.dev)
