Pular para o conteúdo principal

Autenticacao

A Banking API usa autenticacao EdDSA Proof-of-Possession (PoP). Este metodo criptografico garante que as requisicoes sejam assinadas pelo proprietario legitimo da chave privada da conta de servico e previne ataques de replay.

Visao Geral

Diferente da autenticacao tradicional por API key ou OAuth, a autenticacao PoP requer que voce assine criptograficamente cada requisicao. Isso fornece:
  • Nao-repudio - Requisicoes podem ser comprovadas como originarias da sua conta de servico
  • Protecao contra replay - Timestamps previnem reutilizacao de requisicoes capturadas
  • Vinculacao de IP - Requisicoes sao validadas contra sua lista de IPs permitidos

Pre-requisitos

Antes de fazer requisicoes a API, voce precisa:
  1. Conta de Servico - Criada no seu painel Conta Digital
  2. Par de Chaves Ed25519 - Gere um par de chaves publica/privada
  3. Chave Publica Registrada - Faca upload da sua chave publica na conta de servico
  4. Lista de IPs Permitidos - Configure os enderecos IP permitidos para sua conta de servico

Headers Obrigatorios

Toda requisicao autenticada deve incluir estes 5 headers:
HeaderDescricaoExemplo
x-access-idUUID da sua conta de servico550e8400-e29b-41d4-a716-446655440000
X-PoP-SignatureAssinatura EdDSA (base64)MEUCIQDx...
X-PoP-ChallengeTimestamp Unix em milissegundos1705423200000
X-PoP-FormatIdentificador do tipo de autenticacaoservice-account
true-client-ipEndereco IP do seu cliente203.0.113.50

Processo de Assinatura

A assinatura e criada assinando um formato de mensagem especifico com sua chave privada Ed25519.

Passo 1: Construir a Mensagem

Concatene os seguintes valores separados por dois-pontos (:):
{uri}:{method}:{body}:{timestamp}
ComponenteDescricao
uriCaminho completo da requisicao incluindo query string (ex: /v1/account?include=balance)
methodMetodo HTTP em maiusculas (ex: GET, POST)
bodyCorpo da requisicao como string, ou string vazia para requisicoes GET
timestampMesmo valor do header X-PoP-Challenge

Passo 2: Assinar a Mensagem

Assine a mensagem usando sua chave privada Ed25519 e codifique a assinatura em base64.

Passo 3: Definir os Headers

Inclua a assinatura e todos os headers obrigatorios na sua requisicao.

Exemplos de Codigo

Node.js

import { sign } from '@noble/ed25519';
import { Buffer } from 'buffer';

async function signRequest({ uri, method, body, privateKey, accessId, clientIp }) {
  const timestamp = Date.now().toString();
  const bodyStr = body ? JSON.stringify(body) : '';

  // Construir mensagem para assinar
  const message = `${uri}:${method}:${bodyStr}:${timestamp}`;
  const messageBytes = new TextEncoder().encode(message);

  // Assinar com Ed25519
  const signature = await sign(messageBytes, privateKey);
  const signatureBase64 = Buffer.from(signature).toString('base64');

  return {
    'x-access-id': accessId,
    'X-PoP-Signature': signatureBase64,
    'X-PoP-Challenge': timestamp,
    'X-PoP-Format': 'service-account',
    'true-client-ip': clientIp,
    'Content-Type': 'application/json',
  };
}

// Exemplo de uso
const headers = await signRequest({
  uri: '/v1/account',
  method: 'GET',
  body: null,
  privateKey: process.env.ED25519_PRIVATE_KEY,
  accessId: process.env.SERVICE_ACCOUNT_ID,
  clientIp: '203.0.113.50',
});

const response = await fetch('https://conta-public-api.kiwify.com/v1/account', {
  method: 'GET',
  headers,
});

Python

import time
import base64
import json
import requests
from nacl.signing import SigningKey

def sign_request(uri: str, method: str, body: dict | None, private_key_hex: str, access_id: str, client_ip: str) -> dict:
    timestamp = str(int(time.time() * 1000))
    body_str = json.dumps(body) if body else ''

    # Construir mensagem para assinar
    message = f"{uri}:{method}:{body_str}:{timestamp}"
    message_bytes = message.encode('utf-8')

    # Assinar com Ed25519
    signing_key = SigningKey(bytes.fromhex(private_key_hex))
    signed = signing_key.sign(message_bytes)
    signature_base64 = base64.b64encode(signed.signature).decode('utf-8')

    return {
        'x-access-id': access_id,
        'X-PoP-Signature': signature_base64,
        'X-PoP-Challenge': timestamp,
        'X-PoP-Format': 'service-account',
        'true-client-ip': client_ip,
        'Content-Type': 'application/json',
    }

# Exemplo de uso
import os

headers = sign_request(
    uri='/v1/account',
    method='GET',
    body=None,
    private_key_hex=os.environ['ED25519_PRIVATE_KEY'],
    access_id=os.environ['SERVICE_ACCOUNT_ID'],
    client_ip='203.0.113.50',
)

response = requests.get(
    'https://conta-public-api.kiwify.com/v1/account',
    headers=headers,
)

cURL (com assinatura pre-computada)

# Nota: Voce precisara computar a assinatura externamente
# Este exemplo mostra o formato dos headers

TIMESTAMP=$(date +%s000)
ACCESS_ID="seu-uuid-da-conta-de-servico"
SIGNATURE="sua-assinatura-base64-computada"
CLIENT_IP="203.0.113.50"

curl -X GET "https://conta-public-api.kiwify.com/v1/account" \
  -H "x-access-id: ${ACCESS_ID}" \
  -H "X-PoP-Signature: ${SIGNATURE}" \
  -H "X-PoP-Challenge: ${TIMESTAMP}" \
  -H "X-PoP-Format: service-account" \
  -H "true-client-ip: ${CLIENT_IP}"

Validacao de Timestamp

O timestamp X-PoP-Challenge deve estar dentro de 5 minutos do horario atual do servidor. Isso previne ataques de replay enquanto permite uma margem razoavel para diferenca de relogios.
Certifique-se de que o relogio do seu sistema esta sincronizado usando NTP. Requisicoes com timestamps fora da janela de 5 minutos serao rejeitadas com erro 401 Unauthorized.

Lista de IPs Permitidos

O valor do header true-client-ip deve corresponder a um dos enderecos IP configurados na lista de permitidos da sua conta de servico. Isso fornece uma camada adicional de seguranca. Para configurar sua lista de IPs permitidos:
  1. Acesse seu painel Conta Digital
  2. Navegue ate Configuracoes > Contas de Servico
  3. Edite sua conta de servico
  4. Adicione os enderecos IP permitidos (IPv4 ou IPv6)

Solucao de Problemas

Erros Comuns

Causa: A assinatura nao corresponde ao valor esperado.Solucoes:
  • Verifique se esta assinando o formato exato da mensagem: {uri}:{method}:{body}:{timestamp}
  • Certifique-se de que a URI inclui o caminho completo e a query string
  • Verifique se a string do body corresponde exatamente ao que esta enviando
  • Confirme que sua chave privada corresponde a chave publica registrada na conta de servico
Causa: O timestamp X-PoP-Challenge esta mais de 5 minutos diferente do horario do servidor.Solucoes:
  • Sincronize o relogio do seu sistema usando NTP
  • Gere o timestamp imediatamente antes de fazer a requisicao
  • Certifique-se de usar milissegundos, nao segundos
Causa: O true-client-ip nao corresponde a lista de IPs permitidos da sua conta de servico.Solucoes:
  • Verifique seu endereco IP publico atual
  • Atualize a lista de IPs permitidos da sua conta de servico no painel
  • Se estiver atras de um proxy/NAT, use o endereco IP externo
Causa: O x-access-id nao corresponde a nenhuma conta de servico ativa.Solucoes:
  • Verifique se o UUID da conta de servico esta correto
  • Certifique-se de que a conta de servico esta ativa e nao desabilitada
  • Verifique se ha erros de digitacao ou espacos extras

Dicas de Depuracao

  1. Registre a mensagem antes de assinar - Imprima a string exata sendo assinada para verificar o formato
  2. Teste com uma assinatura conhecida - Use vetores de teste para verificar sua implementacao de assinatura
  3. Verifique os headers de resposta - Respostas de erro podem incluir informacoes adicionais de depuracao
  4. Verifique o formato da chave - Chaves privadas Ed25519 devem ter 32 bytes (64 caracteres hexadecimais)

Melhores Praticas de Seguranca

Proteja Sua Chave Privada

Nunca exponha sua chave privada em codigo client-side, logs ou controle de versao. Use variaveis de ambiente ou gerenciamento seguro de secrets.

Rotacione Chaves Regularmente

Gere novos pares de chaves periodicamente e atualize sua conta de servico. Revogue chaves antigas apos a rotacao.

Minimize a Lista de IPs

Permita apenas IPs que genuinamente precisam de acesso a API. Use IPs especificos ao inves de ranges CIDR amplos.

Monitore o Uso da API

Revise regularmente seus logs de acesso a API para padroes inesperados ou tentativas nao autorizadas.