OAuth2 e Autenticação Segura para APIs de Assinatura: Guia Técnico Completo

Quando sua aplicação assina documentos digitalmente via API, cada requisição carrega um peso jurídico e financeiro que vai muito além de uma chamada HTTP convencional. Uma transação de assinatura pode representar a formalização de um contrato de milhões de reais, a aceitação de termos regulatórios ou a emissão de um laudo médico. Se o mecanismo de autenticação falha, as consequências não são apenas técnicas — são jurídicas, financeiras e reputacionais.

Este guia técnico aprofundado explora como implementar autenticação robusta para APIs de assinatura digital, cobrindo desde a comparação de modelos de autenticação até a implementação prática de OAuth2 com JWT assinados por ECDSA e gerenciamento de chaves via KMS.

Por que a autenticação é crítica em APIs de assinatura

APIs de assinatura digital diferem fundamentalmente de APIs de dados convencionais. Enquanto uma API de consulta de CEP retorna informações públicas, uma API de assinatura executa ações com valor jurídico irrevogável. Considere os riscos de uma autenticação frágil:

  • Assinaturas não autorizadas: Um atacante que obtém acesso pode assinar contratos em nome da sua organização
  • Exfiltração de documentos: Acesso indevido a documentos confidenciais aguardando assinatura
  • Negativação repudiável: Sem autenticação forte, torna-se difícil provar quem autorizou uma transação
  • Violações de compliance: Regulamentações como LGPD e normas ICP-Brasil exigem controles de acesso documentados
  • Responsabilidade civil: A empresa pode ser responsabilizada por assinaturas realizadas por terceiros não autorizados

Por essas razões, a escolha do modelo de autenticação não é uma decisão puramente técnica — é uma decisão de negócio e de conformidade que impacta diretamente a segurança dos seus documentos digitais.

Comparação de modelos de autenticação

Existem três abordagens principais para autenticar chamadas a APIs de assinatura digital. Cada uma oferece trade-offs distintos entre simplicidade, segurança e flexibilidade.

Critério API Keys OAuth2 + JWT mTLS
Complexidade de implementação Baixa — header estático Média — fluxo de tokens Alta — PKI e certificados
Granularidade de permissões Limitada — tudo ou nada Alta — scopes por token Baixa — binário (conecta ou não)
Rotação de credenciais Manual e arriscada Automática via expiração Via renovação de certificados
Proteção contra vazamento Frágil — chave estática vaza e funciona Forte — tokens expiram Forte — requer chave privada
Auditoria e rastreabilidade Básica — apenas por chave Rica — claims no JWT Média — por certificado
Adequação para machine-to-machine Sim, mas sem controle fino Ideal — Client Credentials Sim, excelente
Padrão de mercado (2026) Legado / prototipagem Padrão dominante Enterprise / regulatório
Recomendação Sandbox e testes Produção (obrigatório) Camada adicional para enterprise

Recomendação prática: Para APIs de assinatura em produção, utilize OAuth2 Client Credentials + JWT como padrão mínimo. Para ambientes enterprise com requisitos regulatórios elevados, combine OAuth2 com mTLS como camada adicional. Reserve API Keys apenas para ambientes de sandbox e desenvolvimento.

OAuth2 Client Credentials: deep dive

O fluxo Client Credentials (RFC 6749, Seção 4.4) é projetado especificamente para comunicação servidor-a-servidor, sem envolvimento de usuário final. É o fluxo ideal para integrações B2B com APIs de assinatura digital.

Como o fluxo funciona

O fluxo Client Credentials envolve apenas duas entidades: sua aplicação (o client) e o Authorization Server da API de assinatura. O diagrama a seguir ilustra as etapas:

  1. Registro prévio: Sua aplicação recebe um client_id e client_secret ao registrar-se no portal da API
  2. Solicitação de token: Sua aplicação envia uma requisição POST ao token endpoint com as credenciais e os scopes desejados
  3. Emissão do token: O Authorization Server valida as credenciais, verifica os scopes permitidos e emite um access token (JWT)
  4. Uso do token: Sua aplicação inclui o access token no header Authorization: Bearer de cada requisição à API
  5. Validação: O Resource Server (API de assinatura) valida o JWT, verifica expiração, scopes e executa a operação
  6. Renovação: Quando o token expira, sua aplicação solicita um novo token automaticamente

Requisição ao token endpoint

A seguir, o exemplo completo de uma requisição OAuth2 Client Credentials para obter um access token:

# Requisição ao token endpoint OAuth2 curl -X POST https://auth.signdocs.com.br/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=sua_app_client_id" \ -d "client_secret=sua_app_client_secret" \ -d "scope=transactions:create documents:upload documents:read"

A resposta do Authorization Server segue o padrão OAuth2:

// Resposta do token endpoint { "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3Mi...", "token_type": "Bearer", "expires_in": 1800, "scope": "transactions:create documents:upload documents:read", "issued_at": "2026-02-18T10:30:00Z" }

Usando o access token na API de assinatura

Com o token obtido, cada chamada à API inclui o header Authorization. Veja um exemplo completo de criação de transação, como descrito no fluxo transacional da API:

# Criar transação de assinatura usando o access token curl -X POST https://api.signdocs.com.br/v1/transactions \ -H "Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{ "document_id": "doc_8f3a2b1c", "signers": [ { "email": "diretor@empresa.com.br", "name": "Carlos Mendes", "role": "Diretor Financeiro" } ], "webhook_url": "https://app.suaempresa.com.br/webhooks/signdocs", "expires_at": "2026-03-18T23:59:59Z" }'

Dica de implementação: Implemente um mecanismo de cache do access token na sua aplicação. Solicite um novo token apenas quando o atual estiver a menos de 60 segundos de expirar (campo expires_in). Isso evita chamadas desnecessárias ao token endpoint e melhora a latência das suas operações. Para receber notificações sobre o status da assinatura, configure webhooks de eventos.

JWT com ECDSA (ES256/ES384): por que e como

O access token emitido pelo OAuth2 é, na prática, um JSON Web Token (JWT) assinado criptograficamente. A escolha do algoritmo de assinatura do JWT impacta diretamente a segurança, performance e tamanho dos seus tokens.

Por que ECDSA em vez de RSA

Historicamente, RSA (RS256) foi o algoritmo padrão para assinatura de JWTs. Porém, em 2026, ECDSA com curvas elípticas é a escolha recomendada para APIs de assinatura digital por razões concretas:

Característica RSA (RS256) ECDSA (ES256) ECDSA (ES384)
Tamanho da chave 2048-4096 bits 256 bits 384 bits
Segurança equivalente 112 bits (RSA-2048) 128 bits 192 bits
Tamanho da assinatura 256 bytes 64 bytes 96 bytes
Velocidade de assinatura Lenta Rápida Rápida
Velocidade de verificação Rápida Muito rápida Rápida
Tamanho do JWT resultante ~800 bytes ~400 bytes ~450 bytes
Compatibilidade KMS Ampla Ampla Ampla

Em APIs de alto volume — onde cada requisição carrega um JWT no header — a redução de ~400 bytes por token se traduz em economia de banda e menor latência em escala.

Estrutura de um JWT (header.payload.signature)

Todo JWT é composto por três partes separadas por ponto, cada uma codificada em Base64URL:

// HEADER (algoritmo e tipo) { "alg": "ES256", "typ": "JWT", "kid": "signdocs-prod-key-2026-02" } // PAYLOAD (claims - dados do token) { "iss": "https://auth.signdocs.com.br", "sub": "client_app_id_abc123", "aud": "https://api.signdocs.com.br", "exp": 1771329600, "iat": 1771327800, "jti": "tok_f8a3b2c1d4e5", "scope": "transactions:create documents:upload", "org_id": "org_empresa_xyz" } // SIGNATURE (assinatura ECDSA sobre header + payload) // ECDSA-SHA256(base64url(header) + "." + base64url(payload), privateKey)

Exemplo prático: criando e verificando JWT com ECDSA

O exemplo a seguir demonstra como gerar e verificar um JWT com ES256 em Node.js, utilizando a biblioteca jose:

import { SignJWT, jwtVerify, generateKeyPair } from 'jose'; // 1. Gerar par de chaves ECDSA (P-256 para ES256) const { publicKey, privateKey } = await generateKeyPair('ES256'); // 2. Criar e assinar o JWT const jwt = await new SignJWT({ sub: 'client_app_id_abc123', scope: 'transactions:create documents:upload', org_id: 'org_empresa_xyz' }) .setProtectedHeader({ alg: 'ES256', typ: 'JWT', kid: 'signdocs-prod-key-2026-02' }) .setIssuedAt() .setIssuer('https://auth.signdocs.com.br') .setAudience('https://api.signdocs.com.br') .setExpirationTime('30m') // TTL de 30 minutos .setJti('tok_' + crypto.randomUUID()) .sign(privateKey); console.log('JWT gerado:', jwt); // eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6... // 3. Verificar o JWT (lado do servidor da API) const { payload, protectedHeader } = await jwtVerify( jwt, publicKey, { issuer: 'https://auth.signdocs.com.br', audience: 'https://api.signdocs.com.br' } ); console.log('Claims verificados:', payload); // { sub: 'client_app_id_abc123', scope: 'transactions:create ...', ... } console.log('Algoritmo:', protectedHeader.alg); // ES256

Em Python, o equivalente utilizando a biblioteca PyJWT com cryptography:

import jwt from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import serialization from datetime import datetime, timedelta, timezone import uuid # 1. Gerar par de chaves ECDSA (P-256) private_key = ec.generate_private_key(ec.SECP256R1()) public_key = private_key.public_key() # 2. Criar payload e assinar JWT now = datetime.now(timezone.utc) payload = { "iss": "https://auth.signdocs.com.br", "sub": "client_app_id_abc123", "aud": "https://api.signdocs.com.br", "iat": now, "exp": now + timedelta(minutes=30), "jti": f"tok_{uuid.uuid4().hex[:12]}", "scope": "transactions:create documents:upload", "org_id": "org_empresa_xyz" } token = jwt.encode(payload, private_key, algorithm="ES256", headers={"kid": "signdocs-prod-key-2026-02"}) # 3. Verificar JWT decoded = jwt.decode(token, public_key, algorithms=["ES256"], audience="https://api.signdocs.com.br", issuer="https://auth.signdocs.com.br") print(decoded)

Gerenciamento de chaves com KMS

Em produção, as chaves privadas ECDSA usadas para assinar JWTs nunca devem ser armazenadas em disco, código-fonte ou variáveis de ambiente. A abordagem correta é utilizar um Key Management Service (KMS) que armazena chaves em hardware seguro (HSM) certificado FIPS 140-2.

Arquitetura com KMS

O princípio fundamental é simples: a chave privada nunca sai do KMS. Sua aplicação envia os dados a serem assinados ao KMS, e recebe de volta apenas a assinatura. O fluxo é:

  1. Criação da chave: Você cria uma chave ECDSA (P-256 ou P-384) dentro do KMS
  2. Exportação da chave pública: Apenas a chave pública é exportada para verificação de tokens
  3. Operação de assinatura: Sua aplicação envia o hash do JWT (header + payload) ao KMS
  4. Retorno da assinatura: O KMS retorna a assinatura ECDSA, que é concatenada ao JWT

Exemplo com AWS KMS

import { KMSClient, SignCommand } from '@aws-sdk/client-kms'; import { createHash } from 'crypto'; const kms = new KMSClient({ region: 'sa-east-1' }); // Chave ECDSA criada no AWS KMS const KEY_ID = 'arn:aws:kms:sa-east-1:123456789:key/abcd-1234-efgh'; async function signJwtWithKms(header, payload) { // Codificar header e payload em Base64URL const headerB64 = Buffer.from(JSON.stringify(header)) .toString('base64url'); const payloadB64 = Buffer.from(JSON.stringify(payload)) .toString('base64url'); // Criar hash SHA-256 do conteúdo a ser assinado const message = `${headerB64}.${payloadB64}`; const digest = createHash('sha256').update(message).digest(); // Assinar via KMS (chave privada nunca sai do HSM) const command = new SignCommand({ KeyId: KEY_ID, Message: digest, MessageType: 'DIGEST', SigningAlgorithm: 'ECDSA_SHA_256' }); const response = await kms.send(command); const signatureB64 = Buffer.from(response.Signature) .toString('base64url'); return `${message}.${signatureB64}`; }

Comparação de provedores KMS

Provedor Serviço Curvas ECDSA Certificação HSM Região Brasil
AWS AWS KMS P-256, P-384 FIPS 140-2 Level 3 sa-east-1 (São Paulo)
Azure Azure Key Vault P-256, P-384, P-521 FIPS 140-2 Level 2/3 Brazil South
Google Cloud Cloud KMS P-256, P-384 FIPS 140-2 Level 3 southamerica-east1
HashiCorp Vault Transit P-256, P-384, P-521 Depende do backend Self-hosted

SignDocs e segurança de autenticação: A API do SignDocs Brasil implementa OAuth2 Client Credentials com JWTs assinados por ECDSA (ES256), chaves gerenciadas via KMS com HSMs certificados FIPS 140-2, e suporte completo a scopes granulares. Toda a infraestrutura de chaves opera na região Brasil (sa-east-1), garantindo conformidade com requisitos de residência de dados da LGPD. Conheça a API Enterprise.

Scopes e permissões: controle de acesso granular

Scopes OAuth2 permitem aplicar o princípio do menor privilégio às suas integrações. Em vez de conceder acesso total à API, cada aplicação recebe apenas as permissões necessárias para sua função específica.

Tabela de scopes para API de assinatura

Scope Descrição Caso de uso típico
transactions:create Criar novas transações de assinatura Sistema que inicia fluxos de assinatura
transactions:read Consultar status de transações Dashboard de monitoramento
transactions:cancel Cancelar transações pendentes Sistema administrativo
documents:upload Fazer upload de documentos para assinatura Integração com CRM/ERP
documents:read Baixar documentos e metadados Sistema de arquivamento
documents:delete Remover documentos não assinados Administração avançada
templates:manage Criar e editar templates de documentos Gestão de modelos de contrato
webhooks:manage Configurar e gerenciar webhooks Setup de integração
audit:read Acessar logs de auditoria e trilha de eventos Compliance e auditoria
org:admin Gerenciar usuários e configurações da organização Administração da conta

Exemplo de configuração por perfil de integração

// Integração CRM — apenas cria e monitora transações scope: "transactions:create transactions:read documents:upload" // Dashboard de compliance — apenas leitura e auditoria scope: "transactions:read documents:read audit:read" // Sistema administrativo — acesso amplo scope: "transactions:create transactions:read transactions:cancel documents:upload documents:read templates:manage webhooks:manage audit:read" // Microserviço de arquivamento — apenas download scope: "documents:read"

Princípio do menor privilégio na prática: Se uma integração só precisa consultar o status de documentos, conceda apenas transactions:read e documents:read. Se o token dessa integração for comprometido, o atacante não conseguirá criar transações nem deletar documentos. Essa segmentação é essencial para uma arquitetura de assinatura digital segura.

Boas práticas de segurança

Além da escolha correta de protocolo e algoritmo, a segurança de uma integração com API de assinatura depende de práticas operacionais rigorosas. Abaixo estão as recomendações críticas para produção.

1. Rotação de tokens e credenciais

  • Access tokens: TTL entre 15-60 minutos. Nunca use tokens de longa duração em produção
  • Client secrets: Rotacione a cada 90 dias. Mantenha dois secrets ativos durante a transição
  • Chaves de assinatura: Rotacione chaves KMS anualmente. Use o campo kid no JWT para identificar a chave ativa
  • Implementação: Suporte a múltiplas chaves públicas simultâneas (JWKS endpoint) para rotação sem downtime

2. TTL curto para access tokens

A duração do token define a janela de exposição em caso de comprometimento:

// Configuração recomendada de TTL por ambiente { "production": { "access_token_ttl": "30m", // 30 minutos "refresh_token_ttl": "8h", // 8 horas (horário comercial) "max_token_ttl": "1h" // limite máximo absoluto }, "sandbox": { "access_token_ttl": "24h", // mais longo para testes "refresh_token_ttl": "7d", "max_token_ttl": "24h" } }

3. Rate limiting por camada

  • Token endpoint: 10 requisições/minuto por client_id (previne brute force)
  • API geral: 1000 requisições/minuto por token (operações normais)
  • Endpoints críticos: POST /transactions limitado a 100/minuto (prevenção de abuso)
  • Headers de resposta: Sempre retorne X-RateLimit-Limit, X-RateLimit-Remaining e Retry-After

4. IP allowlisting

Restrinja o uso de credenciais OAuth2 a faixas de IP conhecidas:

// Configuração de IP allowlist por client_id { "client_id": "app_erp_empresa_xyz", "allowed_ips": [ "200.150.100.0/24", // Escritório principal "189.40.50.10/32", // Servidor de produção "10.0.0.0/8" // VPC interna (AWS) ], "block_action": "reject_with_403", "alert_on_block": true }

5. Validação rigorosa do JWT

No lado do servidor, valide todos os campos críticos do JWT a cada requisição:

  • exp: Token não expirado (com tolerância de clock skew de 30 segundos)
  • iss: Emissor é o Authorization Server esperado
  • aud: Audiência corresponde à API
  • scope: Token possui os scopes necessários para a operação
  • jti: Token não foi revogado (consulta à blacklist)
  • Assinatura: Verificar assinatura ECDSA com a chave pública do kid correspondente

6. Logging e monitoramento

  • Registre todas as emissões de token (sem logar o token em si)
  • Alerte sobre padrões anômalos: múltiplas solicitações de token em sequência rápida, uso de scopes inéditos, requisições de IPs não reconhecidos
  • Mantenha métricas de taxa de tokens expirados vs renovados para detectar configurações de TTL inadequadas

mTLS como camada adicional de segurança

Para organizações com requisitos de segurança elevados — bancos, fintechs, healthtechs e empresas sob regulamentação do Banco Central ou ANS — o mutual TLS (mTLS) adiciona uma camada de autenticação no nível de transporte que complementa o OAuth2.

Enquanto o OAuth2 controla o que uma aplicação pode fazer (autorização), o mTLS garante quem está se conectando (autenticação mútua). A combinação de ambos cria uma arquitetura de defesa em profundidade:

  • Camada de transporte (mTLS): Apenas clientes com certificado X.509 válido conseguem estabelecer conexão TLS
  • Camada de aplicação (OAuth2): Mesmo com conexão estabelecida, a requisição precisa de um access token válido com scopes adequados
  • Resultado: Um token vazado é inútil sem o certificado mTLS, e um certificado comprometido não concede acesso sem um token válido

Aprofunde-se em mTLS: para ambientes enterprise, vale estudar PKI, certificate pinning, gerenciamento de CRL/OCSP e a integração do mTLS com OAuth2 — a combinação padrão em setores regulados como BACEN e Open Finance.

Perguntas frequentes

Qual a diferença entre OAuth2 Authorization Code e Client Credentials para APIs de assinatura?

O Authorization Code é indicado para cenários com usuário final interagindo via navegador (front-end), enquanto o Client Credentials é projetado para comunicação servidor-a-servidor (machine-to-machine). No contexto de integrações B2B com APIs de assinatura digital, o Client Credentials é o fluxo recomendado porque não há usuário humano no fluxo — sua aplicação se autentica diretamente com client_id e client_secret, obtendo um access token para operar de forma autônoma.

Por que usar ECDSA (ES256) em vez de RSA (RS256) para JWT em APIs de assinatura?

ECDSA com curvas elípticas oferece o mesmo nível de segurança que RSA com chaves significativamente menores. Uma chave ECDSA de 256 bits (ES256) proporciona segurança equivalente a uma RSA de 3072 bits. Na prática, isso resulta em tokens JWT com aproximadamente metade do tamanho, verificação mais rápida e menor consumo de banda — vantagens críticas quando cada requisição à API carrega um JWT no header Authorization. Além disso, todos os principais serviços KMS (AWS, Azure, GCP) oferecem suporte nativo a ECDSA.

Qual o TTL recomendado para tokens OAuth2 em APIs de assinatura?

Para ambientes de produção, recomendamos um TTL entre 15 e 60 minutos para access tokens. Tokens de curta duração limitam a janela de exposição em caso de comprometimento. A configuração mais comum em APIs de assinatura é 30 minutos. Para operações batch de longa duração (como envio de centenas de documentos), utilize refresh tokens com rotação automática em vez de aumentar o TTL do access token.

Como proteger as chaves de assinatura JWT em produção?

A abordagem recomendada é utilizar serviços de gerenciamento de chaves (KMS) como AWS KMS, Azure Key Vault ou Google Cloud KMS. Essas soluções armazenam chaves em módulos HSM certificados FIPS 140-2, garantindo que a chave privada nunca saia do hardware seguro. A operação de assinatura ocorre inteiramente dentro do KMS — sua aplicação envia o hash dos dados e recebe apenas a assinatura como resultado. Nunca armazene chaves privadas em variáveis de ambiente, código-fonte ou arquivos em disco.

É possível usar OAuth2 e mTLS simultaneamente?

Sim, e essa é considerada a abordagem mais segura para APIs enterprise. O mTLS autentica a conexão no nível de transporte (TLS), enquanto o OAuth2 controla a autorização no nível da aplicação. Combinados, oferecem defesa em profundidade: mesmo que um token OAuth2 vaze, ele não pode ser usado sem o certificado mTLS correspondente. Essa combinação é especialmente importante para setores regulados como financeiro e saúde. Veja nosso guia completo sobre mTLS para detalhes de implementação.

Como implementar rate limiting eficaz para proteger a API de assinatura?

Implemente rate limiting em múltiplas camadas: por client_id (ex: 1000 req/min), por endpoint (ex: POST /transactions limitado a 100 req/min), e por IP. Utilize algoritmos como token bucket ou sliding window para suavizar picos de tráfego. Retorne sempre os headers HTTP padrão (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After) para que aplicações clientes possam implementar backoff automático sem intervenção manual.

O que são scopes OAuth2 e por que são importantes para APIs de assinatura?

Scopes são permissões granulares que definem exatamente o que um access token pode fazer. Em APIs de assinatura, scopes como transactions:create, documents:upload e audit:read permitem aplicar o princípio do menor privilégio. Uma integração de consulta, por exemplo, recebe apenas scopes de leitura (transactions:read, documents:read), sem capacidade de criar transações ou deletar documentos. Se o token dessa integração for comprometido, o dano potencial é significativamente limitado.

Pronto para integrar com segurança?

O SignDocs Brasil oferece API com OAuth2 Client Credentials, JWT ECDSA (ES256), scopes granulares e suporte a mTLS. Infraestrutura no Brasil com HSMs certificados FIPS 140-2.

Comece grátis Fale com nossa equipe sobre a API Enterprise