Autenticação Bearer

Bearer Token é um esquema de autenticação HTTP padronizado pela RFC 6750. O cliente envia, em cada requisição, o header:

Authorization: Bearer <token>

A semântica é literal — "quem porta o token é tratado como autenticado". Isso significa que o token, sozinho, é a credencial: não há challenge, não há nonce, não há rotação automática. Toda a segurança vem de confidencialidade do token (HTTPS obrigatório) e comparação resistente a timing attacks no servidor.

Como funciona na plataforma Alcance

A Alcance usa Bearer em duas camadas distintas — entender a diferença evita confusão na hora de configurar integrações.

1. Cliente IA → MCP gateway

O cliente IA (Claude Desktop, Cursor, Lovable) é configurado com um token estático chamado MCP_BEARER_TOKEN, vivendo em env var do servidor MCP. Quando o cliente IA faz qualquer chamada tools/list, tools/call ou resources/read, ele envia o header Authorization: Bearer <MCP_BEARER_TOKEN>.

O gateway valida em src/lib/auth.ts e responde 401 Unauthorized com WWW-Authenticate: Bearer realm="mcp-alcance" quando o token está ausente, malformado ou diferente.

2. MCP gateway → WebApiAlcance

Validado o cliente IA, o gateway encaminha a chamada para a WebApiAlcance usando outra credencial — uma API Key dedicada ao MCP, também enviada no padrão Bearer. Essa API Key é restrita por escopos (recurso:acao), nunca global, e é validada pelo FastAPI da WebApi.

São, portanto, dois Bearer tokens distintos no mesmo fluxo, com responsabilidades separadas: o MCP_BEARER_TOKEN autentica o operador humano via cliente IA; a API Key autentica o gateway perante a API de negócio.

Mitigação de ataques de timing

Comparar strings com === (ou strcmp) é vulnerável a timing attacks: o tempo de comparação varia conforme o número de caracteres iniciais que coincidem, e um atacante pode inferir o token byte a byte medindo latências.

O gateway usa timingSafeEqual do módulo node:crypto, que executa a comparação em tempo constante — o tempo de resposta independe do conteúdo dos buffers. Quando os tokens têm tamanhos diferentes, fazemos uma comparação dummy antes de retornar false, para não vazar a diferença de tamanho via latência.

Exemplo de chamada

curl -X POST https://mcp-alcancecontabilidade.vercel.app/api/mcp \
  -H "Authorization: Bearer SEU_TOKEN_AQUI" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Resposta esperada com token válido: JSON com a lista de tools registradas. Com token inválido: HTTP 401 + corpo JSON {"error":"unauthorized"}.

Boas práticas

  • Rotacionar a cada 90 dias. SLA interno da Alcance — Calendar reminder obrigatório.
  • Nunca commitar o token em repositório, mesmo privado. Use .env.local (já no .gitignore).
  • Nunca logar o token em STDOUT, arquivos de log, ferramentas de observabilidade ou mensagens de erro.
  • HTTPS sempre. O Bearer é trafegado em texto puro dentro do header — sem TLS, qualquer proxy intermediário lê o token.
  • Tokens longos. O MCP_BEARER_TOKEN deve ter no mínimo 32 bytes de entropia (use openssl rand -base64 32).

Vazamento de token

Se houver suspeita de que o MCP_BEARER_TOKEN vazou — push acidental em repo, screenshot publicado, dispositivo comprometido — execute rotação imediata:

  1. Gere novo token: openssl rand -base64 32.
  2. vercel env rm MCP_BEARER_TOKEN production e vercel env add MCP_BEARER_TOKEN production.
  3. Force novo deploy: vercel --prod.
  4. Atualize o token em todos os clientes IA conectados.
  5. Audite os logs Vercel das últimas 24h em busca de invocações suspeitas.

Tempo total esperado: ~5 minutos de janela de exposição.

Migração para OAuth 2.1

O Bearer estático é a escolha consciente da Fase 1 — documentada no ADR-0003 — porque atende 2 operadores e 1 cliente IA com mínima complexidade.

Na Fase 2, a Alcance migra para OAuth 2.1 com Dynamic Client Registration (DCR) via Clerk, ganhando: revogação granular por cliente, identidade individual nos logs e — fundamental — suporte ao ChatGPT, que exige OAuth 2.1. O modo Bearer será mantido atrás de feature flag AUTH_MODE=bearer|oauth para fallback de emergência durante a transição.