Forgedocs
Integration Guides

Authentication

Overview

The Avista API uses authentication based on OAuth 2.0 with X.509 certificates (mTLS). This layered security model ensures that only authorized clients with valid certificates can access the API.

Why mTLS?

mTLS (mutual TLS) offers superior security compared to simple tokens:

  • Mutual authentication: Both the client and the server authenticate each other
  • Non-repudiation: Certificates linked to the account ensure traceability
  • Protection against credential theft: Even with leaked clientId/clientSecret, the attacker would need the certificate

Prerequisites

Before you begin, you will need:

Obtain your client certificate through the Avista portal. The certificate must be in PEM format and will be linked to your account.

Request your credentials (clientId and clientSecret) in the admin panel.

Configure your environment to send the certificate in the X-SSL-Client-Cert header.

The X.509 certificate must be linked to your account before being used. Unlinked certificates will be rejected even if technically valid.

Authentication Endpoint

POST /api/auth/token

Generates a JWT access token valid for 30 minutes (1800 seconds).

The X.509 certificate must be sent URL-encoded in the X-SSL-Client-Cert header. The system validates the SHA256 fingerprint of the certificate against the records linked to the account.

Request

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

Response (201 Created)

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

Practical Example: Node.js

Installation

npm install axios

Complete Code

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.gateway.goforge.com.br/api/auth/token',
  headers: {
    'Content-Type': 'application/json',
    'X-SSL-Client-Cert': encodedCert
  },
  data: {
    clientId: process.env.FORGE_CLIENT_ID,
    clientSecret: process.env.FORGE_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();

Practical Example: Python

Installation

pip install requests

Complete Code

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.gateway.goforge.com.br/api/auth/token'
headers = {
    'Content-Type': 'application/json',
    'X-SSL-Client-Cert': encoded_cert
}
payload = {
    'clientId': os.environ.get('FORGE_CLIENT_ID'),
    'clientSecret': os.environ.get('FORGE_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}')

Using the Token

After obtaining the token, include it in the Authorization header of all requests:

curl -X GET https://api.gateway.goforge.com.br/api/balance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Renewal

Tokens expire after 30 minutes. Implement automatic renewal logic in your application to avoid interruptions.

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.gateway.goforge.com.br/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.FORGE_CLIENT_ID,
  process.env.FORGE_CLIENT_SECRET,
  './client-cert.pem'
);

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

Certificate Validation

The system performs the following validations on the certificate:

  1. Valid PEM format: The certificate must be in PEM format and URL-encoded
  2. Account linkage: The SHA256 fingerprint of the certificate must be registered and linked to your account
  3. Credential matching: The certificate must belong to the same account as the OAuth credentials

Certificates that are unlinked or linked to another account will be rejected, even if technically valid.

Common Errors

400 Bad Request

Cause: Certificate missing or malformed

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

Solution: Check if:

  • The certificate is in PEM format
  • The certificate is URL-encoded (use encodeURIComponent())
  • The X-SSL-Client-Cert header is present in the request

401 Unauthorized

Cause: Invalid credentials or unauthorized certificate

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

Solution: Check if:

  • The clientId and clientSecret are correct
  • The certificate is linked to your account in the Avista portal
  • The certificate matches the OAuth credentials being used

403 Forbidden

Cause: Certificate not linked to the account

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

Solution: Contact Avista support to link the certificate to your account.

Best Practices

Next Steps

On this page