> ## Documentation Index
> Fetch the complete documentation index at: https://docs.capitalcheckin.app/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth2 Authorization Code

> Implementación de OAuth2 Authorization Code Grant en Capital Check In API

# OAuth2 Authorization Code Grant

Capital Check In API implementa el flujo OAuth2 Authorization Code Grant según el estándar RFC 6749. Este método es ideal para aplicaciones de terceros que necesitan acceder a recursos de usuarios de Capital Check In.

## Flujo OAuth2

```mermaid theme={null}
graph TD
    A[Aplicación Cliente] --> B[Redirigir a /oauth/authorize]
    B --> C[Usuario Autoriza]
    C --> D[Redirigir con Code]
    D --> E[Intercambiar Code por Token]
    E --> F[Acceso a API]
```

## Endpoints OAuth2

### 1. Solicitud de Autorización

<RequestExample>
  <Request method="GET" url="/oauth/authorize" title="Solicitar Autorización OAuth2">
    <RequestQuery>
      ```bash theme={null}
      client_id=your_client_id
      redirect_uri=https://your-app.com/callback
      response_type=code
      scope=read:profile read:groups
      state=random_state_value
      ```
    </RequestQuery>
  </Request>
</RequestExample>

<ResponseExample>
  <Response status="200" title="Página de Autorización">
    ```json theme={null}
    {
      "success": true,
      "message": "Autorización iniciada exitosamente",
      "state_id": "state_123456",
      "client": {
        "id": "client_123",
        "name": "Mi Aplicación",
        "description": "Aplicación de ejemplo",
        "logo": "https://example.com/logo.png",
        "redirect_uri": "https://your-app.com/callback",
        "grant_types": ["authorization_code"],
        "scopes": ["read:profile", "read:groups"]
      },
      "requires_consent": true,
      "scopes_requested": "read:profile read:groups",
      "state": "random_state_value"
    }
    ```
  </Response>
</ResponseExample>

### 2. Aprobar Autorización

<RequestExample>
  <Request method="POST" url="/oauth/authorize" title="Aprobar Autorización">
    <RequestBody>
      ```json theme={null}
      {
        "state_id": "state_123456",
        "jwt": "user_jwt_token"
      }
      ```
    </RequestBody>
  </Request>
</RequestExample>

<ResponseExample>
  <Response status="200" title="Redirección con Código">
    ```
    https://your-app.com/callback?code=AUTH_CODE_123&state=random_state_value
    ```
  </Response>
</ResponseExample>

### 3. Denegar Autorización

<RequestExample>
  <Request method="DELETE" url="/oauth/authorize" title="Denegar Autorización">
    <RequestBody>
      ```json theme={null}
      {
        "state_id": "state_123456",
        "jwt": "user_jwt_token"
      }
      ```
    </RequestBody>
  </Request>
</RequestExample>

<ResponseExample>
  <Response status="200" title="Redirección con Error">
    ```
    https://your-app.com/callback?error=access_denied&state=random_state_value
    ```
  </Response>
</ResponseExample>

### 4. Intercambiar Código por Token

<RequestExample>
  <Request method="POST" url="/oauth/token" title="Intercambiar Código por Token">
    <RequestBody>
      ```json theme={null}
      {
        "grant_type": "authorization_code",
        "client_id": "your_client_id",
        "client_secret": "your_client_secret",
        "code": "AUTH_CODE_123",
        "redirect_uri": "https://your-app.com/callback"
      }
      ```
    </RequestBody>
  </Request>
</RequestExample>

<ResponseExample>
  <Response status="200" title="Token de Acceso">
    ```json theme={null}
    {
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "token_type": "bearer",
      "expires_in": 3600,
      "refresh_token": "REFRESH_TOKEN_123",
      "scope": "read:profile read:groups"
    }
    ```
  </Response>
</ResponseExample>

## Scopes Disponibles

| Scope           | Descripción            | Permisos                                |
| --------------- | ---------------------- | --------------------------------------- |
| `read:profile`  | Leer perfil de usuario | Obtener información básica del usuario  |
| `read:groups`   | Leer grupos            | Obtener lista de grupos y colaboradores |
| `write:profile` | Modificar perfil       | Actualizar información del usuario      |
| `write:groups`  | Modificar grupos       | Crear, actualizar y eliminar grupos     |
| `read:reports`  | Leer reportes          | Acceder a reportes de check-in          |
| `write:reports` | Crear reportes         | Generar y modificar reportes            |

## Configuración de Cliente OAuth2

### Registrar Aplicación Cliente

Para usar OAuth2, necesitas registrar tu aplicación:

<RequestExample>
  <Request method="POST" url="/oauth/clients" title="Registrar Cliente OAuth2">
    <RequestBody>
      ```json theme={null}
      {
        "name": "Mi Aplicación",
        "description": "Aplicación de ejemplo para integración",
        "redirect_uri": "https://your-app.com/callback",
        "grant_types": ["authorization_code"],
        "scopes": ["read:profile", "read:groups"]
      }
      ```
    </RequestBody>
  </Request>
</RequestExample>

<ResponseExample>
  <Response status="201" title="Cliente Creado">
    ```json theme={null}
    {
      "id": "client_123",
      "client_id": "your_client_id",
      "client_secret": "your_client_secret",
      "name": "Mi Aplicación",
      "description": "Aplicación de ejemplo para integración",
      "redirect_uri": "https://your-app.com/callback",
      "grant_types": ["authorization_code"],
      "scopes": ["read:profile", "read:groups"],
      "created_at": "2024-01-15T10:00:00Z"
    }
    ```
  </Response>
</ResponseExample>

## Implementación en JavaScript

### Cliente OAuth2

```javascript theme={null}
class CapitalCheckInOAuth2 {
  constructor(clientId, clientSecret, redirectUri) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.redirectUri = redirectUri;
    this.baseURL = 'https://api.capitalcheckin.app';
  }

  // Paso 1: Redirigir al usuario para autorización
  initiateAuthorization(scope = 'read:profile', state = null) {
    const params = new URLSearchParams({
      client_id: this.clientId,
      redirect_uri: this.redirectUri,
      response_type: 'code',
      scope: scope
    });

    if (state) {
      params.append('state', state);
    }

    const authUrl = `${this.baseURL}/oauth/authorize?${params.toString()}`;
    window.location.href = authUrl;
  }

  // Paso 2: Intercambiar código por token
  async exchangeCodeForToken(code) {
    const response = await fetch(`${this.baseURL}/oauth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify({
        grant_type: 'authorization_code',
        client_id: this.clientId,
        client_secret: this.clientSecret,
        code: code,
        redirect_uri: this.redirectUri
      })
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Token exchange failed');
    }

    return response.json();
  }

  // Paso 3: Usar el token para acceder a la API
  async makeAuthenticatedRequest(endpoint, token) {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Accept': 'application/json'
      }
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Request failed');
    }

    return response.json();
  }

  // Paso 4: Renovar token usando refresh token
  async refreshToken(refreshToken) {
    const response = await fetch(`${this.baseURL}/oauth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify({
        grant_type: 'refresh_token',
        client_id: this.clientId,
        client_secret: this.clientSecret,
        refresh_token: refreshToken
      })
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Token refresh failed');
    }

    return response.json();
  }
}

// Uso
const oauth2 = new CapitalCheckInOAuth2(
  'your_client_id',
  'your_client_secret',
  'https://your-app.com/callback'
);

// Iniciar flujo de autorización
oauth2.initiateAuthorization('read:profile read:groups', 'random_state');

// En la página de callback
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');

if (code) {
  try {
    const tokenResponse = await oauth2.exchangeCodeForToken(code);
    console.log('Token obtenido:', tokenResponse);
    
    // Usar el token para acceder a la API
    const profile = await oauth2.makeAuthenticatedRequest(
      '/v1/users/profile',
      tokenResponse.access_token
    );
    console.log('Perfil:', profile);
  } catch (error) {
    console.error('Error:', error);
  }
}
```

## Página de Autorización

### Implementación del Frontend

```javascript theme={null}
// Página de autorización OAuth2
class OAuthAuthorizationPage {
  constructor() {
    this.stateId = null;
    this.client = null;
    this.scopesRequested = '';
    this.state = '';
  }

  async initialize() {
    // Obtener parámetros de la URL
    const urlParams = new URLSearchParams(window.location.search);
    const clientId = urlParams.get('client_id');
    const redirectUri = urlParams.get('redirect_uri');
    const scope = urlParams.get('scope');
    const state = urlParams.get('state');

    // Validar parámetros requeridos
    if (!clientId || !redirectUri) {
      this.showError('Parámetros OAuth2 inválidos');
      return;
    }

    // Obtener información del cliente
    try {
      const response = await fetch(`/api/oauth/authorize?${window.location.search}`);
      const data = await response.json();

      if (data.success) {
        this.stateId = data.state_id;
        this.client = data.client;
        this.scopesRequested = data.scopes_requested;
        this.state = data.state;
        this.renderAuthorizationPage();
      } else {
        this.showError(data.message);
      }
    } catch (error) {
      this.showError('Error al inicializar autorización');
    }
  }

  renderAuthorizationPage() {
    const container = document.getElementById('oauth-container');
    container.innerHTML = `
      <div class="oauth-card">
        <div class="client-info">
          <img src="${this.client.logo}" alt="${this.client.name}" class="client-logo">
          <h2>${this.client.name}</h2>
          <p>${this.client.description}</p>
        </div>
        
        <div class="permissions">
          <h3>Permisos Solicitados:</h3>
          <ul>
            ${this.scopesRequested.split(' ').map(scope => 
              `<li>${this.getScopeDescription(scope)}</li>`
            ).join('')}
          </ul>
        </div>

        <div class="actions">
          <button onclick="authorizeApp.approve()" class="btn-approve">
            Autorizar
          </button>
          <button onclick="authorizeApp.deny()" class="btn-deny">
            Denegar
          </button>
        </div>
      </div>
    `;
  }

  getScopeDescription(scope) {
    const scopeDescriptions = {
      'read:profile': 'Leer información de tu perfil',
      'read:groups': 'Leer información de grupos',
      'write:profile': 'Modificar tu perfil',
      'write:groups': 'Modificar grupos',
      'read:reports': 'Leer reportes',
      'write:reports': 'Crear reportes'
    };
    return scopeDescriptions[scope] || scope;
  }

  async approve() {
    try {
      const response = await fetch('/api/oauth/authorize', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          state_id: this.stateId,
          jwt: this.getUserToken()
        })
      });

      const data = await response.json();
      if (data.success) {
        window.location.href = data.redirect_url;
      } else {
        this.showError(data.message);
      }
    } catch (error) {
      this.showError('Error al autorizar aplicación');
    }
  }

  async deny() {
    try {
      const response = await fetch('/api/oauth/authorize', {
        method: 'DELETE',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          state_id: this.stateId,
          jwt: this.getUserToken()
        })
      });

      const data = await response.json();
      if (data.success) {
        window.location.href = data.redirect_url;
      } else {
        this.showError(data.message);
      }
    } catch (error) {
      this.showError('Error al denegar autorización');
    }
  }

  getUserToken() {
    // Obtener token del usuario desde localStorage o cookies
    return localStorage.getItem('access_token') || '';
  }

  showError(message) {
    const container = document.getElementById('oauth-container');
    container.innerHTML = `
      <div class="error-message">
        <h3>Error</h3>
        <p>${message}</p>
        <button onclick="window.close()">Cerrar</button>
      </div>
    `;
  }
}

// Inicializar página de autorización
const authorizeApp = new OAuthAuthorizationPage();
authorizeApp.initialize();
```

## Seguridad

### Mejores Prácticas

1. **State Parameter**: Siempre usa el parámetro `state` para prevenir ataques CSRF
2. **PKCE**: Implementa PKCE para aplicaciones públicas
3. **HTTPS**: Usa HTTPS en todas las comunicaciones
4. **Validación de Redirect URI**: Valida que la URI de redirección esté autorizada
5. **Scope Validation**: Valida los permisos solicitados
6. **Token Storage**: Almacena tokens de forma segura

### Validaciones de Seguridad

* **Client ID Validation**: Verificación del ID del cliente
* **Redirect URI Validation**: Validación de URI de redirección
* **Scope Validation**: Validación de permisos solicitados
* **State Parameter**: Prevención de ataques CSRF
* **JWT Validation**: Validación del token JWT del usuario

## Manejo de Errores

### Errores Comunes

<ResponseExample>
  <Response status="400" title="Parámetros Inválidos">
    ```json theme={null}
    {
      "error": "invalid_request",
      "message": "Los parámetros de la solicitud son inválidos",
      "details": {
        "client_id": ["El client_id es requerido"],
        "redirect_uri": ["La redirect_uri no está autorizada"]
      }
    }
    ```
  </Response>
</ResponseExample>

<ResponseExample>
  <Response status="401" title="No Autorizado">
    ```json theme={null}
    {
      "error": "unauthorized_client",
      "message": "El cliente no está autorizado"
    }
    ```
  </Response>
</ResponseExample>

<ResponseExample>
  <Response status="403" title="Acceso Denegado">
    ```json theme={null}
    {
      "error": "access_denied",
      "message": "El usuario denegó la autorización"
    }
    ```
  </Response>
</ResponseExample>
