Você conecta Claude Code a um servidor MCP. O servidor requer autenticação. Seu cliente envia uma solicitação; o servidor responde com um 401 Unauthorized e um cabeçalho WWW-Authenticate: Bearer que carrega uma URL resource_metadata. O que acontece a seguir é um fluxo OAuth 2.1 definido por três RFCs e um conjunto de extensões específicas do MCP que, juntas, resolvem um dos problemas mais difíceis em segurança de agentes: garantir que um token obtido para se comunicar com este servidor MCP não possa ser reutilizado contra aquele.
Este post traça o fluxo completo — o que a especificação diz, o que as RFCs realmente fornecem e onde os pontos fracos de segurança se escondem na prática.
O fluxo: de 401 para um token vinculado a recurso
O modelo de autorização MCP (revisão 2025-11-25, mantido inalterado no candidato a lançamento — congelado 2026-05-21 — para a especificação final programada para 2026-07-28) é um processo em duas fases. A Fase 1 é a detecção: o 401 que carrega WWW-Authenticate: Bearer resource_metadata="..." informa ao cliente que este servidor é protegido por OAuth e onde encontrar seus metadados. A Fase 2 é o acesso autorizado: consumir os metadados, descobrir o servidor de autorização, obter um token vinculado a este servidor específico e usá-lo.
Os passos concretos:
-
401 + WWW-Authenticate: O servidor MCP rejeita uma solicitação não autenticada. O parâmetro
resource_metadatano desafio aponta para o documento de Metadados de Recursos Protegidos do servidor. -
PRM buscar (RFC 9728): O cliente realiza um GET na URL
/.well-known/oauth-protected-resource. A resposta é um documento JSON declarando o URI canônico do recurso do servidor, o(s) servidor(es) de autorização que o protegem e os escopos que o recurso suporta. O cliente valida se o camporesourcedo documento corresponde ao servidor que ele pretendia alcançar — uma incompatibilidade é um sinal de personificação e deve ser rejeitada. -
AS descoberta (RFC 8414): Usando a URL do emissor do array
authorization_serversdo PRM, o cliente percorre os candidatos conhecidos (/.well-known/oauth-authorization-server, depois/.well-known/openid-configuration) para buscar os metadados do servidor de autorização. O emissor no documento retornado deve ser idêntico byte a byte ao emissor para o qual foi buscado — não “equivalente após normalização”, não “quase igual”. Idêntico byte a byte. -
Aquisição de token (RFC 8707): O cliente solicita um token do endpoint de token do AS, incluindo
resource=<canonical server URI>tanto na requisição de autorização quanto na de token. Isso vincula o público do token a este servidor MCP específico. O AS emite um token cujo público é esse recurso, e o cliente o apresenta ao servidor. -
Acesso autorizado: O cliente usa o token para chamar os métodos de introspecção somente leitura do servidor MCP (
tools/list,resources/list, etc.). O token comprova que o cliente está autorizado; o indicador de recurso comprova que o token foi emitido para este servidor.
O que o RFC 9728 realmente fornece
RFC 9728 (Metadados de Recurso Protegido) é um mecanismo de descoberta. Ele responde: “qual servidor de autorização protege este recurso e o que ele espera?” Ele não autentica o servidor, não valida o token, nem aplica controle de acesso. Essas são preocupações separadas.
O documento PRM possui um campo obrigatório (resource) e um conjunto de opcionais, dos quais authorization_servers é o mais importante. A especificação MCP restringe a opcionalidade do RFC: pelo menos um servidor de autorização é obrigatório. Um documento PRM que não liste nenhum é tratado como um erro.
O campo resource é o URI canônico do recurso protegido. O cliente deve compará-lo com o servidor que pretendia contatar e rejeitar uma discrepância:
// RFC 9728 §3.3: o valor do recurso PRM DEVE identificar o protegido
// recurso que o cliente está acessando — comparação simples de strings com
// o indicador de recurso ao qual este cliente vincula seus tokens.
if prm.Resource != c.resource {
return authServerMetadata{}, fmt.Errorf(
"mcp: oauth: protected resource metadata declares resource %q, "+
"expected %q (RFC 9728 §3.3 reject)", prm.Resource, c.resource)
}
Esta verificação previne uma classe de ataque onde um documento PRM malicioso ou mal configurado alega falar em nome de um recurso diferente. A comparação não é normalizada — é uma correspondência direta de string com o URI do recurso canônico que o cliente calculou para o URL do servidor fornecido a ele.
Indicadores de recursos RFC 8707 — por que a vinculação de token é importante
Sem indicadores de recurso, um token de acesso obtido de um AS pode potencialmente ser usado em qualquer servidor de recursos que AS proteja. Se você tiver dois servidores MCP — digamos, um servidor de documentação apenas para leitura e um sandbox de execução de código — ambos atrás do mesmo provedor de identidade, um token obtido para um poderia ser apresentado ao outro. Isso é um problema de substituto confuso.
RFC 8707 resolve isso adicionando um parâmetro resource às solicitações de autorização e token. O AS emite um token cujo público-alvo é explicitamente aquele URI de recurso. Um servidor de recursos que valida corretamente rejeita um token cujo público-alvo não corresponda à sua própria identidade.
Na prática, a solicitação de token inclui o indicador de recurso juntamente com a concessão:
form := url.Values{
"grant_type": {"client_credentials"},
"resource": {c.resource}, // RFC 8707 — vincular o token à audiência
}
Isso aparece em todos os tipos de concessão que o conector usa: credenciais do cliente, resgate de código de autorização e rotação de token de atualização. O indicador de recurso não é opcional — ele está presente em todas as solicitações de token, portanto, cada token é vinculado ao público por construção.
A verificação de emissor idêntica em bytes
A validação mais crítica para a segurança em todo o fluxo também é a mais simples de expressar e a mais fácil de errar: o valor do emissor no documento de metadados AS deve ser idêntico em bytes ao emissor que o cliente usou para construir a URL bem conhecida.
Não igual sem considerar maiúsculas/minúsculas. Não equivalente após normalização do esquema. Não o mesmo após remover uma barra final. Idêntico em bytes. A seção 3.3 do RFC 8414 é explícita sobre isso, e a especificação MCP herda o requisito.
// discoverASMetadata: RECUSA um documento cujo issuer não seja
// IDÊNTICO EM BYTES ao issuer para o qual foi obtido (RFC 8414 §3.3).
// Um documento incompatível é um sinal de usurpação de identidade.
if as.Issuer != issuer {
return authServerMetadata{}, fmt.Errorf(
"mcp: oauth: AS metadata at %s declares issuer %q, "+
"expected %q (RFC 8414 §3.3 reject)", cand, as.Issuer, issuer)
}
Por que tão rigoroso? Porque um atacante que controla o DNS ou está no caminho da rede pode fornecer um documento de metadados que aponta para seu próprio endpoint de token enquanto afirma ser um emissor legítimo. Se o cliente normalizasse o emissor antes de comparar, https://auth.example.com e https://AUTH.example.com corresponderiam — e o documento do atacante seria aceito. A comparação idêntica por byte resolve isso.
A mesma disciplina se aplica ao RFC 9207 (validação do emissor da resposta de autorização). Quando o cliente inicia um fluxo de código de autorização, ele registra o emissor a partir dos metadados AS validados. No redirecionamento de volta, o parâmetro iss na resposta deve corresponder exatamente ao valor registrado — novamente, byte a byte, sem normalização — antes de o código de autorização ser resgatado. Esta é a defesa contra confusões: sem ela, um AS malicioso poderia interceptar o código e fazer com que o cliente o resgatasse no endpoint de token do atacante.
Identificação do cliente: CIMD substitui DCR
A especificação MCP define uma ordem de prioridade para como um cliente se identifica perante o servidor de autorização:
- Credenciais pré-registradas — o operador provisiona um
client_ideclient_secretantecipadamente - CIMD (Documentos de Metadados do ID do Cliente) — o cliente hospeda um documento JSON em uma URL HTTPS; essa URL é o
client_id - Registro Dinâmico de Cliente (RFC 7591) — o cliente se registra no endpoint de registro do AS
- Solicitar ao usuário — não aplicável para agentes sem interface
DCR está obsoleto na versão candidata para a especificação final programada para 2026-07-28, em favor do CIMD. A razão é operacional: DCR cria estado persistente do cliente no servidor de autorização. Cada agente que se registra deixa para trás um par client_id/client_secret que o AS deve armazenar, e ninguém os rastreia ou revoga. Para uma frota de agentes, isso resulta em um espalhamento descontrolado de credenciais.
CIMD inverte o modelo. O cliente hospeda um documento em uma URL que ele controla. O AS busca o documento quando precisa validar o cliente, armazena em cache de acordo com os cabeçalhos de cache HTTP, e não armazena nada permanentemente. A rotação de chaves é uma atualização do documento. A desativação de um cliente é a remoção do documento. Nenhum registro órfão se acumula no AS.
O trade-off: CIMD exige que o cliente execute um endpoint HTTPS. Para um plano de governança auto-hospedado, isso é natural — o plano já roda serviços HTTPS. Para uma ferramenta CLI em um laptop de desenvolvedor, é menos natural, razão pela qual credenciais pré-registradas continuam sendo a primeira opção na ordem de prioridade.
Uma identidade CIMD não pode conter um segredo compartilhado (a URL do documento é pública — um segredo nela seria um vazamento de credencial). A autenticação do cliente usa private_key_jwt (RFC 7523): o cliente assina um JWT de curta duração com uma chave privada cujo contraparte pública é publicada no campo jwks do documento CIMD.
A regra do “nunca passar adiante”
Uma defesa estrutural que é fácil de ignorar: o conector só usa um token que ele próprio obteve, para um servidor específico, através do fluxo de descoberta descrito acima. Ele nunca aceita um token de terceiros e o encaminha para um servidor MCP. Ele nunca lê um token de uma solicitação de entrada e o repassa.
Esta é a defesa de deputado confuso no nível do protocolo. Se um cliente encaminhasse tokens que recebeu, um atacante poderia apresentar um token restrito a um recurso de baixo privilégio e fazer com que o cliente o encaminhasse para um recurso de alto privilégio (ou vice-versa — extrair um token de alto privilégio enganando o cliente para apresentá-lo a um servidor controlado pelo atacante). Ao obter seus próprios tokens e nunca tocar nos de terceiros, o conector não pode ser usado como um relé de tokens.
A passagem de token não é apenas desencorajada — ela é estruturalmente impossível. O cliente HTTP do conector para fluxos OAuth é separado de qualquer manipulador de solicitação de entrada. Não existe um caminho de código que leia um token bearer de uma solicitação recebida e o escreva em uma de saída.
SSRF: a defesa contra rebinding de DNS
Cada URL de metadados e de endpoint de token que o conector busca é protegida contra SSRF em duas camadas. A primeira é uma verificação preliminar: a URL deve ser HTTPS (exceto loopback para desenvolvimento local) e não deve ser um endereço IP reservado literal.
O segundo é uma verificação de tempo de discagem que fecha o TOCTOU de reatribuição de DNS. Um nome de host que resolve para um IP público durante a verificação pré-voo poderia se reatribuir para um IP privado na hora em que o socket fosse acessado. O cliente HTTP do conector instala uma função net.Dialer.Control que inspeciona o IP resolvido concreto no momento da conexão e recusa qualquer endereço reservado. Esta é a verificação autoritária — a verificação pré-voo é uma rejeição rápida para casos óbvios, mas a verificação no tempo de discagem é a que importa.
Escalonamento de escopo
Um servidor MCP pode responder a uma solicitação com WWW-Authenticate: Bearer error="insufficient_scope" scope="mcp:tools:list mcp:resources:read". A especificação (SEP-835/SEP-2350) define como o cliente lida com isso: calcular a união dos escopos que ele solicitou anteriormente e dos escopos que o servidor acabou de desafiar, e então adquirir novamente um token com esse conjunto expandido. A união preserva as permissões concedidas anteriormente e adiciona as novas. O aumento de privilégios ocorre uma vez — um segundo desafio de escopo insuficiente na mesma solicitação não é tentado novamente, para evitar loops infinitos.
O servidor tem permissão para ser sem estado em seu desafio: ele nomeia apenas os escopos que a operação atual precisa, não o conjunto completo que o cliente pode ter solicitado antes. A acumulação do lado do cliente faz isso funcionar sem que o servidor rastreie o histórico de escopos por cliente.
O que isso significa na prática
O fluxo OAuth para servidores MCP é bem especificado e, quando implementado rigorosamente, aborda as superfícies de ataque reais: repetição de token entre servidores, falsificação de metadados, SSRF com DNS-rebinding e dispersão descontrolada de credenciais do cliente. A verificação do emissor idêntica em bytes, o indicador de recurso em cada solicitação de token e a regra estrutural de não-passthrough são as defesas principais. Elas não são recursos que você pode implementar pela metade — cada uma é uma verificação rigorosa que falha de forma segura.
O problema mais difícil é operacional. As equipes que implementam servidores MCP precisam:
- Publicar um documento PRM válido no
/.well-known/oauth-protected-resourcecom um camporesourceque corresponda ao URI canônico do seu servidor. - Use um AS que suporte indicadores de recurso — muitos provedores de identidade ainda não o fazem, ou tratam o parâmetro
resourcecomo consultivo em vez de vinculado ao público - Mude do DCR para o CIMD antes que a descontinuação se torne uma remoção, ou pré-registre credenciais explicitamente
- Vincule credenciais a um emissor para evitar o reuso silencioso entre emissores quando a topologia AS mudar
A documentação do conector MCP aborda a configuração operacional, e o modelo de segurança descreve como o token vinculado a recurso se encaixa no mapa de acesso mais amplo.