Pular para o conteúdo principal

Assinatura de Requisições

Nossa API utiliza assinatura de requisições baseada em JWT para autenticar e verificar a integridade das suas requisições. Este guia explica como implementar a assinatura de requisições em suas aplicações.

Visão Geral

Cada requisição para nossa API deve incluir um token JWT assinado no cabeçalho Authorization. O token contém declarações sobre a requisição e é assinado usando sua chave privada com o algoritmo RS256.
1

Preparar credenciais

Você precisa de duas credenciais:
  • Chave de API: Seu identificador único
  • Chave Privada: Sua chave privada RSA em formato PEM
2

Extrair caminho da URL

Extraia o caminho e os parâmetros de consulta da URL da requisição. Não inclua o domínio ou protocolo nos dados assinados.
3

Gerar hash do corpo

Calcule um hash SHA-256 do corpo da requisição para garantir integridade.
4

Criar e assinar JWT

Crie um JWT com declarações específicas e assine-o com sua chave privada.
5

Adicionar cabeçalho Authorization

Inclua o JWT assinado no cabeçalho Authorization como um token Bearer.

Implementação

Abaixo está uma implementação em TypeScript que você pode usar como referência para outras linguagens:
import * as jwt from 'jsonwebtoken';
import * as crypto from 'crypto';
import { URL } from 'url';

/**
 * Assina uma requisição de API com JWT usando algoritmo RS256
 * @param privateKey Sua chave privada RSA
 * @param apiKey Sua chave de API
 * @param requestUrl URL completa da requisição
 * @param requestBody Corpo da requisição (string ou undefined)
 * @returns String do token Bearer para o cabeçalho Authorization
 */
export function signRequest(
  privateKey: string,
  apiKey: string,
  requestUrl: string,
  requestBody?: string
): string {
  // Construir url da requisição - extrair caminho e consulta
  const parsedUrl = new URL(requestUrl);
  const url = parsedUrl.pathname + parsedUrl.search;

  // Hash do corpo da requisição
  const body = requestBody || '{}';
  const bodyHash = crypto
    .createHash('sha256')
    .update(body)
    .digest('hex');

  // Gerar o JWT assinado
  const jwtHeader = {
    typ: 'JWT',
    alg: 'RS256'
  };

  const now = Math.floor(Date.now() / 1000);

  const jwtData = {
    uri: url,
    iat: now,
    exp: now + 55,
    sub: apiKey,
    bodyHash: bodyHash
  };

  const signedJwt = jwt.sign(jwtData, privateKey, {
    algorithm: 'RS256',
    header: jwtHeader
  });

  // Retorna valor do cabeçalho Authorization
  return `Bearer ${signedJwt}`;
}

Estrutura do Payload JWT

O payload JWT deve conter as seguintes declarações:
uri
string
required
O caminho da URL com parâmetros de consulta (excluindo o domínio). Exemplo: /v1/resources?filter=active
iat
number
required
Horário de emissão como timestamp Unix (segundos desde a época).
exp
number
required
Horário de expiração como timestamp Unix. Deve ser definido como iat + 55 segundos.
sub
string
required
Sua chave de API.
bodyHash
string
required
Hash SHA-256 do corpo da requisição como uma string hexadecimal.

Implementações em Linguagens Específicas

  • Node.js
  • Python
  • Java
  • PHP
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const { URL } = require('url');

function signRequest(privateKey, apiKey, requestUrl, requestBody) {
  // Extrair caminho com consulta
  const parsedUrl = new URL(requestUrl);
  const url = parsedUrl.pathname + parsedUrl.search;

  // Hash do corpo
  const body = requestBody || '{}';
  const bodyHash = crypto.createHash('sha256').update(body).digest('hex');

  // Criar JWT
  const now = Math.floor(Date.now() / 1000);
  const payload = {
    uri: url,
    iat: now,
    exp: now + 55,
    sub: apiKey,
    bodyHash: bodyHash
  };

  const signedJwt = jwt.sign(payload, privateKey, {
    algorithm: 'RS256',
    header: { typ: 'JWT', alg: 'RS256' }
  });

  return `Bearer ${signedJwt}`;
}

Testando Sua Implementação

Você pode verificar se sua implementação está funcionando corretamente:
  1. Gerando uma requisição assinada
  2. Decodificando o JWT em jwt.io para verificar as declarações
  3. Fazendo uma requisição de teste para nosso endpoint de API
Certifique-se de que o corpo da requisição seja idêntico entre a geração da assinatura e a requisição real. Mesmo uma única diferença de espaço em branco resultará em um hash diferente e falha na validação da assinatura.

Problemas Comuns

Isso normalmente significa:
  • O formato da sua chave privada está incorreto
  • O hash do corpo não corresponde ao corpo da requisição real
  • O JWT expirou (deve ser válido apenas por 55 segundos)
Certifique-se de que seu JWT inclua todas as declarações obrigatórias: uri, iat, exp, sub e bodyHash.
A declaração uri deve incluir apenas o caminho e a string de consulta, não o domínio ou protocolo.

Melhores Práticas de Segurança

  • Armazene sua chave privada com segurança e nunca a exponha em código do lado do cliente
  • Gere um novo JWT para cada requisição
  • Mantenha o tempo de expiração curto (55 segundos) para evitar ataques de repetição
  • Implemente tratamento adequado de erros para falhas de autenticação