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 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
Endpoints OAuth2
1. Solicitud de Autorización
2. Aprobar Autorización
3. Denegar Autorización
4. Intercambiar Código por Token
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:
Implementación en JavaScript
Cliente OAuth2
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
// 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
- State Parameter: Siempre usa el parámetro
state para prevenir ataques CSRF
- PKCE: Implementa PKCE para aplicaciones públicas
- HTTPS: Usa HTTPS en todas las comunicaciones
- Validación de Redirect URI: Valida que la URI de redirección esté autorizada
- Scope Validation: Valida los permisos solicitados
- 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