Avistadocs
Guías de Integración

Autenticación

Descripción General

La API de Avista utiliza autenticación basada en OAuth 2.0 con certificados X.509 (mTLS). Este modelo de seguridad en capas garantiza que solo los clientes autorizados con certificados válidos puedan acceder a la API.

¿Por qué mTLS?

mTLS (mutual TLS) ofrece una seguridad superior en comparación con tokens simples:

  • Autenticación mutua: Tanto el cliente como el servidor se autentican entre sí
  • No repudio: Los certificados vinculados a la cuenta garantizan la trazabilidad
  • Protección contra robo de credenciales: Incluso con clientId/clientSecret filtrados, el atacante necesitaría el certificado

Requisitos Previos

Antes de comenzar, necesitará:

Obtenga su certificado de cliente a través del portal de Avista. El certificado debe estar en formato PEM y será vinculado a su cuenta.

Solicite sus credenciales (clientId y clientSecret) en el panel de administración.

Configure su entorno para enviar el certificado en el encabezado X-SSL-Client-Cert.

El certificado X.509 debe estar vinculado a su cuenta antes de usarse. Los certificados no vinculados serán rechazados aunque sean técnicamente válidos.

Endpoint de Autenticación

POST /api/auth/token

Genera un token de acceso JWT válido por 30 minutos (1800 segundos).

El certificado X.509 debe enviarse codificado en URL en el encabezado X-SSL-Client-Cert. El sistema valida la huella digital SHA256 del certificado contra los registros vinculados a la cuenta.

Solicitud

curl -X POST https://api.avista.global/api/auth/token \
  -H "Content-Type: application/json" \
  -H "X-SSL-Client-Cert: -----BEGIN%20CERTIFICATE-----%0AMIIB..." \
  -d '{
    "clientId": "account-93-550e8400",
    "clientSecret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
  }'

Respuesta (201 Created)

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 1800
}

Ejemplo Práctico: Node.js

Instalación

npm install axios

Código Completo

const axios = require('axios');
const fs = require('fs');

// Load X.509 certificate
const certificate = fs.readFileSync('./client-cert.pem', 'utf8');
const encodedCert = encodeURIComponent(certificate);

// Request configuration
const config = {
  method: 'post',
  url: 'https://api.avista.global/api/auth/token',
  headers: {
    'Content-Type': 'application/json',
    'X-SSL-Client-Cert': encodedCert
  },
  data: {
    clientId: process.env.AVISTA_CLIENT_ID,
    clientSecret: process.env.AVISTA_CLIENT_SECRET
  }
};

// Make request
async function getToken() {
  try {
    const response = await axios(config);

    console.log('Token obtained successfully!');
    console.log('Expires in:', response.data.expires_in, 'seconds');

    return response.data.access_token;
  } catch (error) {
    console.error('Error obtaining token:', error.response?.data || error.message);
    throw error;
  }
}

getToken();

Ejemplo Práctico: Python

Instalación

pip install requests

Código Completo

import os
import requests
import urllib.parse

# Load and encode certificate
with open('client-cert.pem', 'r') as f:
    certificate = f.read()
    encoded_cert = urllib.parse.quote(certificate)

# Request configuration
url = 'https://api.avista.global/api/auth/token'
headers = {
    'Content-Type': 'application/json',
    'X-SSL-Client-Cert': encoded_cert
}
payload = {
    'clientId': os.environ.get('AVISTA_CLIENT_ID'),
    'clientSecret': os.environ.get('AVISTA_CLIENT_SECRET')
}

# Make request
try:
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()

    data = response.json()

    print('Token obtained successfully!')
    print(f"Expires in: {data['expires_in']} seconds")

except requests.exceptions.RequestException as e:
    print(f'Error obtaining token: {e}')
    if hasattr(e.response, 'text'):
        print(f'Response: {e.response.text}')

Uso del Token

Después de obtener el token, inclúyalo en el encabezado Authorization de todas las solicitudes:

curl -X GET https://api.avista.global/api/balance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Renovación del Token

Los tokens expiran después de 30 minutos. Implemente lógica de renovación automática en su aplicación para evitar interrupciones.

Estrategia Recomendada

class TokenManager {
  constructor(clientId, clientSecret, certificatePath) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.certificatePath = certificatePath;
    this.token = null;
    this.expiresAt = null;
  }

  async getValidToken() {
    // Check if the token is still valid (with a 30-second margin)
    if (this.token && this.expiresAt && Date.now() < this.expiresAt - 30000) {
      return this.token;
    }

    // Renew the token
    return await this.refreshToken();
  }

  async refreshToken() {
    const response = await this.requestNewToken();
    this.token = response.access_token;
    this.expiresAt = Date.now() + (response.expires_in * 1000);
    return this.token;
  }

  async requestNewToken() {
    const fs = require('fs');
    const axios = require('axios');

    const certificate = fs.readFileSync(this.certificatePath, 'utf8');
    const encodedCert = encodeURIComponent(certificate);

    const response = await axios.post('https://api.avista.global/api/auth/token', {
      clientId: this.clientId,
      clientSecret: this.clientSecret
    }, {
      headers: {
        'Content-Type': 'application/json',
        'X-SSL-Client-Cert': encodedCert
      }
    });

    return response.data;
  }
}

// Usage
const tokenManager = new TokenManager(
  process.env.AVISTA_CLIENT_ID,
  process.env.AVISTA_CLIENT_SECRET,
  './client-cert.pem'
);

// In any request
const token = await tokenManager.getValidToken();

Validación del Certificado

El sistema realiza las siguientes validaciones sobre el certificado:

  1. Formato PEM válido: El certificado debe estar en formato PEM y codificado en URL
  2. Vinculación a la cuenta: La huella digital SHA256 del certificado debe estar registrada y vinculada a su cuenta
  3. Coincidencia de credenciales: El certificado debe pertenecer a la misma cuenta que las credenciales OAuth

Los certificados que no estén vinculados o estén vinculados a otra cuenta serán rechazados, aunque sean técnicamente válidos.

Errores Comunes

400 Bad Request

Causa: Certificado ausente o malformado

{
  "statusCode": 400,
  "message": "Certificado ausente no header X-SSL-Client-Cert"
}

Solución: Verifique que:

  • El certificado esté en formato PEM
  • El certificado esté codificado en URL (use encodeURIComponent())
  • El encabezado X-SSL-Client-Cert esté presente en la solicitud

401 Unauthorized

Causa: Credenciales inválidas o certificado no autorizado

{
  "statusCode": 401,
  "message": "Credenciais inválidas ou certificado inválido"
}

Solución: Verifique que:

  • El clientId y clientSecret sean correctos
  • El certificado esté vinculado a su cuenta en el portal de Avista
  • El certificado coincida con las credenciales OAuth utilizadas

403 Forbidden

Causa: Certificado no vinculado a la cuenta

{
  "statusCode": 403,
  "message": "Certificado não vinculado à conta"
}

Solución: Contacte al soporte de Avista para vincular el certificado a su cuenta.

Mejores Prácticas

Próximos Pasos

En esta página