---
id: "ET-AUTH-004"
title: "API Endpoints for Auth"
type: "Specification"
status: "Done"
rf_parent: "RF-AUTH-002"
epic: "OQI-001"
version: "1.0"
created_date: "2025-12-05"
updated_date: "2026-01-04"
---

# ET-AUTH-004: Especificación Técnica - API Endpoints

**Version:** 1.0.0
**Fecha:** 2025-12-05
**Estado:** ✅ Implementado
**Épica:** [OQI-001](../_MAP.md)

---

## Resumen

Esta especificación detalla todos los endpoints de la API de autenticación de Trading Platform.

---

## Base URL

```
Production: https://api.trading.com/api/v1
Development: http://localhost:3000/api/v1
```

---

## Endpoints Overview

| Método | Endpoint | Descripción | Auth |
|--------|----------|-------------|------|
| POST | `/auth/register` | Registro con email | ❌ |
| POST | `/auth/login` | Login con email/password | ❌ |
| POST | `/auth/logout` | Cerrar sesión | ✅ |
| POST | `/auth/refresh` | Renovar tokens | ❌ |
| GET | `/auth/me` | Usuario actual | ✅ |
| GET | `/auth/oauth/:provider/url` | URL de OAuth | ❌ |
| POST | `/auth/oauth/:provider` | Callback OAuth | ❌ |
| DELETE | `/auth/oauth/:provider` | Desvincular OAuth | ✅ |
| POST | `/auth/phone/send` | Enviar OTP | ❌ |
| POST | `/auth/phone/verify` | Verificar OTP | ❌ |
| POST | `/auth/2fa/setup` | Configurar 2FA | ✅ |
| POST | `/auth/2fa/enable` | Activar 2FA | ✅ |
| POST | `/auth/2fa/verify` | Verificar 2FA | ❌ |
| POST | `/auth/2fa/disable` | Desactivar 2FA | ✅ |
| POST | `/auth/forgot-password` | Solicitar reset | ❌ |
| POST | `/auth/reset-password` | Cambiar password | ❌ |
| POST | `/auth/verify-email` | Verificar email | ❌ |
| GET | `/auth/sessions` | Listar sesiones | ✅ |
| DELETE | `/auth/sessions/:id` | Revocar sesión | ✅ |
| DELETE | `/auth/sessions` | Revocar todas | ✅ |

---

## Detalle de Endpoints

### POST /auth/register

Registro de nuevo usuario con email y contraseña.

**Request:**

```http
POST /api/v1/auth/register
Content-Type: application/json

{
  "email": "usuario@example.com",
  "password": "SecurePass123!",
  "firstName": "Juan",
  "lastName": "Pérez",
  "acceptTerms": true
}
```

**Response 201:**

```json
{
  "success": true,
  "message": "Registro exitoso. Revisa tu email para verificar tu cuenta.",
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "usuario@example.com",
      "firstName": "Juan",
      "lastName": "Pérez",
      "role": "investor",
      "status": "pending_verification",
      "emailVerified": false
    }
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 400 | VALIDATION_ERROR | Datos inválidos |
| 409 | EMAIL_EXISTS | Email ya registrado |
| 429 | RATE_LIMIT | Demasiadas solicitudes |

---

### POST /auth/login

Inicio de sesión con email y contraseña.

**Request:**

```http
POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "usuario@example.com",
  "password": "SecurePass123!"
}
```

**Response 200 (sin 2FA):**

```json
{
  "success": true,
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "usuario@example.com",
      "firstName": "Juan",
      "lastName": "Pérez",
      "role": "investor",
      "status": "active"
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresIn": 900,
      "tokenType": "Bearer"
    }
  }
}
```

**Response 200 (con 2FA):**

```json
{
  "success": true,
  "data": {
    "requires2FA": true,
    "tempToken": "temp_token_for_2fa_verification",
    "methods": ["totp", "backup_code"]
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 401 | INVALID_CREDENTIALS | Email o contraseña incorrectos |
| 403 | EMAIL_NOT_VERIFIED | Email no verificado |
| 403 | ACCOUNT_LOCKED | Cuenta bloqueada temporalmente |
| 403 | ACCOUNT_SUSPENDED | Cuenta suspendida |

---

### POST /auth/logout

Cerrar sesión actual.

**Request:**

```http
POST /api/v1/auth/logout
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Sesión cerrada exitosamente"
}
```

---

### POST /auth/refresh

Renovar access token usando refresh token.

**Request:**

```http
POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refreshToken": "eyJhbGciOiJSUzI1NiIs..."
}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "tokens": {
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresIn": 900,
      "tokenType": "Bearer"
    }
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 401 | INVALID_TOKEN | Token inválido o expirado |
| 401 | TOKEN_REVOKED | Token revocado |

---

### GET /auth/me

Obtener información del usuario actual.

**Request:**

```http
GET /api/v1/auth/me
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "usuario@example.com",
      "phone": "+525512345678",
      "firstName": "Juan",
      "lastName": "Pérez",
      "role": "investor",
      "status": "active",
      "emailVerified": true,
      "phoneVerified": true,
      "twoFactorEnabled": false,
      "createdAt": "2025-01-15T10:00:00Z",
      "profile": {
        "displayName": "JuanPerez",
        "avatarUrl": "https://...",
        "preferredLanguage": "es",
        "timezone": "America/Mexico_City"
      },
      "oauthProviders": ["google", "github"]
    }
  }
}
```

---

### GET /auth/oauth/:provider/url

Obtener URL de autorización OAuth.

**Request:**

```http
GET /api/v1/auth/oauth/google/url?redirectTo=/dashboard
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "authUrl": "https://accounts.google.com/o/oauth2/v2/auth?...",
    "state": "random_state_token"
  }
}
```

---

### POST /auth/oauth/:provider

Procesar callback de OAuth.

**Request:**

```http
POST /api/v1/auth/oauth/google
Content-Type: application/json

{
  "code": "authorization_code_from_provider",
  "state": "state_token_from_url"
}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "usuario@gmail.com",
      "firstName": "Juan",
      "lastName": "Pérez",
      "isNewUser": false
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresIn": 900,
      "tokenType": "Bearer"
    },
    "redirectTo": "/dashboard"
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 400 | INVALID_STATE | State token inválido |
| 400 | CODE_EXPIRED | Código de autorización expirado |
| 502 | PROVIDER_ERROR | Error con el proveedor OAuth |

---

### DELETE /auth/oauth/:provider

Desvincular proveedor OAuth.

**Request:**

```http
DELETE /api/v1/auth/oauth/github
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "message": "GitHub desvinculado exitosamente"
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 400 | LAST_AUTH_METHOD | No puedes eliminar tu único método de auth |
| 404 | PROVIDER_NOT_LINKED | Proveedor no vinculado |

---

### POST /auth/phone/send

Enviar OTP por SMS o WhatsApp.

**Request:**

```http
POST /api/v1/auth/phone/send
Content-Type: application/json

{
  "phone": "+525512345678",
  "channel": "whatsapp"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Código enviado a tu WhatsApp",
  "data": {
    "expiresAt": "2025-12-05T10:05:00Z",
    "retryAfter": 60
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 400 | INVALID_PHONE | Número de teléfono inválido |
| 429 | RATE_LIMIT | Demasiadas solicitudes |
| 502 | SMS_ERROR | Error al enviar mensaje |

---

### POST /auth/phone/verify

Verificar OTP y autenticar.

**Request:**

```http
POST /api/v1/auth/phone/verify
Content-Type: application/json

{
  "phone": "+525512345678",
  "code": "123456"
}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "phone": "+525512345678",
      "firstName": "Usuario",
      "lastName": "Nuevo",
      "isNewUser": true
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresIn": 900,
      "tokenType": "Bearer"
    }
  }
}
```

**Errors:**

| Code | Error | Descripción |
|------|-------|-------------|
| 401 | INVALID_CODE | Código incorrecto |
| 401 | CODE_EXPIRED | Código expirado |
| 429 | TOO_MANY_ATTEMPTS | Demasiados intentos |

---

### POST /auth/2fa/setup

Generar secreto TOTP para configuración.

**Request:**

```http
POST /api/v1/auth/2fa/setup
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "secret": "JBSWY3DPEHPK3PXP",
    "qrCode": "data:image/png;base64,iVBORw0KGgo...",
    "otpauthUrl": "otpauth://totp/Trading Platform:usuario@example.com?..."
  }
}
```

---

### POST /auth/2fa/enable

Activar 2FA después de verificar código.

**Request:**

```http
POST /api/v1/auth/2fa/enable
Authorization: Bearer {accessToken}
Content-Type: application/json

{
  "code": "123456"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "2FA activado exitosamente",
  "data": {
    "backupCodes": [
      "A1B2C3D4",
      "E5F6G7H8",
      "I9J0K1L2",
      "M3N4O5P6",
      "Q7R8S9T0",
      "U1V2W3X4",
      "Y5Z6A7B8",
      "C9D0E1F2",
      "G3H4I5J6",
      "K7L8M9N0"
    ]
  }
}
```

---

### POST /auth/2fa/verify

Verificar código 2FA durante login.

**Request:**

```http
POST /api/v1/auth/2fa/verify
Content-Type: application/json

{
  "tempToken": "temp_token_from_login",
  "code": "123456"
}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "user": { ... },
    "tokens": {
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresIn": 900,
      "tokenType": "Bearer"
    }
  }
}
```

---

### POST /auth/2fa/disable

Desactivar 2FA.

**Request:**

```http
POST /api/v1/auth/2fa/disable
Authorization: Bearer {accessToken}
Content-Type: application/json

{
  "code": "123456"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "2FA desactivado exitosamente"
}
```

---

### POST /auth/forgot-password

Solicitar recuperación de contraseña.

**Request:**

```http
POST /api/v1/auth/forgot-password
Content-Type: application/json

{
  "email": "usuario@example.com"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Si el email existe, recibirás instrucciones para recuperar tu contraseña"
}
```

---

### POST /auth/reset-password

Cambiar contraseña con token de recuperación.

**Request:**

```http
POST /api/v1/auth/reset-password
Content-Type: application/json

{
  "token": "reset_token_from_email",
  "password": "NewSecurePass123!"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Contraseña actualizada exitosamente"
}
```

---

### POST /auth/verify-email

Verificar email con token.

**Request:**

```http
POST /api/v1/auth/verify-email
Content-Type: application/json

{
  "token": "verification_token_from_email"
}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Email verificado exitosamente"
}
```

---

### GET /auth/sessions

Listar sesiones activas.

**Request:**

```http
GET /api/v1/auth/sessions
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "data": {
    "sessions": [
      {
        "id": "session-uuid-1",
        "device": {
          "type": "desktop",
          "browser": "Chrome",
          "browserVersion": "120.0.0",
          "os": "Windows",
          "osVersion": "11"
        },
        "location": {
          "city": "Ciudad de México",
          "country": "México",
          "countryCode": "MX"
        },
        "ipAddress": "189.xxx.xxx.xxx",
        "lastActivity": "2025-12-05T10:00:00Z",
        "createdAt": "2025-12-01T08:00:00Z",
        "isCurrent": true
      },
      {
        "id": "session-uuid-2",
        "device": {
          "type": "mobile",
          "browser": "Safari",
          "os": "iOS",
          "osVersion": "17.1"
        },
        "location": {
          "city": "Guadalajara",
          "country": "México"
        },
        "ipAddress": "187.xxx.xxx.xxx",
        "lastActivity": "2025-12-04T15:30:00Z",
        "createdAt": "2025-11-28T10:00:00Z",
        "isCurrent": false
      }
    ],
    "totalCount": 2
  }
}
```

---

### DELETE /auth/sessions/:id

Revocar sesión específica.

**Request:**

```http
DELETE /api/v1/auth/sessions/session-uuid-2
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Sesión cerrada exitosamente"
}
```

---

### DELETE /auth/sessions

Revocar todas las demás sesiones.

**Request:**

```http
DELETE /api/v1/auth/sessions
Authorization: Bearer {accessToken}
```

**Response 200:**

```json
{
  "success": true,
  "message": "Todas las demás sesiones han sido cerradas",
  "data": {
    "revokedCount": 3
  }
}
```

---

## Formato de Errores

Todos los errores siguen el formato estándar:

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Descripción legible del error",
    "details": {
      "field": "Detalle específico del campo si aplica"
    }
  }
}
```

---

## Rate Limiting Headers

```http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1701792060
```

---

## Referencias

- [OpenAPI Spec](../../../98-standards/openapi/auth.yaml)
- [Postman Collection](../../../96-quick-reference/postman/Trading Platform-Auth.json)
