OAuth em servidor MCP: como ele autentica um agente e o que quebra
Um servidor MCP remoto autentica um agente como servidor de recursos OAuth 2.1. Ele responde a uma chamada sem token com 401 e um cabeçalho WWW-Authenticate, o cliente segue esse caminho até os metadados do servidor, roda um fluxo de código de autorização com PKCE e envia o token como cabeçalho bearer em cada requisição. Aqui vai cada passo, executado ao vivo hoje.
A autorização é opcional no MCP, e só para transportes HTTP: um servidor stdio local lê as credenciais do ambiente. Tudo o que vem abaixo é da revisão atual da especificação, 2026-07-28, e cada resposta é uma chamada real para https://api.plugkit.co/mcp feita em 16 de outubro de 2026.
O aperto de mão, em quatro respostas
Passo 1. Chamar sem token e ler o desafio. O servidor precisa devolver um 401 com um cabeçalho WWW-Authenticate apontando para os metadados dele.
curl -i -X POST https://api.plugkit.co/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://api.plugkit.co/.well-known/oauth-protected-resource", scope="mcp:read mcp:write"
{"error":"Missing credentials. Send `Authorization: Bearer <api key or session token>`.","type":"authentication_error","code":"unauthorized"}
A dica de scope é um SHOULD na especificação, não um MUST, e é a diferença entre um cliente que pede as permissões certas de primeira e um que adivinha. Os clientes precisam tratar os escopos do desafio como autoridade para a operação.
Passo 2. Buscar os metadados do recurso protegido. É a RFC 9728, e é um MUST para todo servidor MCP.
{"resource":"https://api.plugkit.co/mcp",
"authorization_servers":["https://api.plugkit.co"],
"scopes_supported":["mcp:read","mcp:write"],
"bearer_methods_supported":["header"],
"resource_documentation":"https://docs.plugkit.co"}
Passo 3. Buscar os metadados do servidor de autorização. RFC 8414, ou OpenID Connect Discovery; o servidor precisa oferecer pelo menos um, e o cliente precisa suportar os dois.
{"issuer":"https://api.plugkit.co",
"authorization_endpoint":"https://api.plugkit.co/oauth/authorize",
"token_endpoint":"https://api.plugkit.co/oauth/token",
"registration_endpoint":"https://api.plugkit.co/oauth/register",
"revocation_endpoint":"https://api.plugkit.co/oauth/revoke",
"code_challenge_methods_supported":["S256"],
"token_endpoint_auth_methods_supported":["none"],
"client_id_metadata_document_supported":true,
"authorization_response_iss_parameter_supported":true}
Passo 4. Rodar o fluxo de código e chamar de novo com o token. Código de autorização com PKCE, o parâmetro resource nas duas requisições, e depois Authorization: Bearer <token> em absolutamente toda requisição HTTP, inclusive nas da mesma sessão.
Autenticação de um servidor MCP: de onde vem o client ID
Essa é a parte que mudou, e a parte que a maioria dos tutoriais ainda conta errado. A revisão atual lista três mecanismos e os ordena.
| Mecanismo | Status na revisão 2026-07-28 | O que o cliente envia como client_id |
|---|---|---|
| Client ID Metadata Documents | SHOULD support | Uma URL HTTPS que o servidor busca para ler os metadados do cliente |
| Registro prévio | Permitido | Um client ID que te entregaram por fora |
| Dynamic Client Registration (RFC 7591) | MAY support, obsoleto | Um client ID vindo de POST /register |
O registro dinâmico era um SHOULD na revisão 2025-06-18. Agora é explicitamente “deprecated and retained for backwards compatibility with authorization servers that do not support Client ID Metadata Documents”. O CIMD substitui isso por algo mais simples: o cliente usa uma URL como identificador, o servidor de autorização busca essa URL, lê os metadados e valida as redirect URIs. Sem chamada de registro e sem um cadastro de cliente guardado por cliente.
O servidor do PlugKit acima anuncia as duas coisas, client_id_metadata_document_supported: true e um registration_endpoint ativo, que é a postura correta enquanto os clientes se atualizam.
OAuth 2.1 no MCP: PKCE, o parâmetro resource e a vinculação de audiência
Três exigências fazem o trabalho de segurança, e as três são MUST para os clientes.
- PKCE. Obrigatório em todos os clientes.
S256é o método que o servidor acima publica. Ele impede que um código de autorização interceptado seja trocado por qualquer cliente que não seja o que iniciou o fluxo. - O parâmetro
resource. RFC 8707, tanto na requisição de autorização quanto na de token, levando a URI canônica do servidor MCP:&resource=https%3A%2F%2Fapi.plugkit.co%2Fmcp. Os clientes devem enviá-lo, com ou sem suporte do servidor. - Validação da audiência. O servidor precisa verificar que o token foi emitido para ele, e precisa recusar qualquer outro. A especificação é categórica: um servidor MCP “MUST NOT accept or transit any other tokens”.
Essa última tem nome. Se um servidor pega um token destinado a outro e repassa para cima, aparece o problema do delegado confuso: a API de destino confia na chamada porque ela veio do servidor. O passthrough de token é proibido, e é por isso que um servidor que chama uma API externa precisa ter o próprio token separado para ela. Essa é a metade “servidor para plataforma” do problema, e ela tem as próprias falhas, tratadas em os tokens de acesso das redes sociais.
O que quebra
Cinco falhas, na ordem em que você provavelmente vai encontrá-las.
| Sintoma | Status | Causa | Correção |
|---|---|---|---|
| O cliente nunca inicia o fluxo | 401 | Sem cabeçalho WWW-Authenticate, ou sem metadados de recurso |
Implemente a RFC 9728 e devolva o cabeçalho em cada 401 |
| Chamada de ferramenta recusada, e o token parece certo | 403 | Falta ao token um escopo de que a operação precisa | Devolva error="insufficient_scope" com scope, e deixe o cliente elevar os escopos |
| Loop de pedidos de consentimento no navegador | 401 | Refresh token expirado ou revogado, com um cliente que tenta de novo às cegas | Limite as tentativas e trate a falha repetida como definitiva |
| Token recusado por um segundo servidor | 401 | Token enviado a um servidor para o qual não foi emitido | Envie o parâmetro resource e valide a audiência |
| O fluxo termina contra o servidor errado | nenhum | Ataque de confusão de servidores, iss não verificado |
Guarde o emissor antes de redirecionar e compare na volta |
A dos escopos merece detalhe, porque é nova e fácil de errar. Quando um token é válido mas estreito demais, o servidor deve responder 403 com error="insufficient_scope" e os escopos de que a operação precisa. O cliente então reautoriza com a união do que já tinha e do que acabou de ser pedido, não só o escopo novo, ou perde as permissões que estava usando antes. Os servidores deveriam enviar todos os escopos de que a operação precisa num único desafio, em vez de pingar um por ida e volta.
A verificação do iss é a mais silenciosa das cinco, porque nada falha de forma visível. O cliente precisa guardar o emissor dos metadados validados antes de abrir o navegador, e depois comparar o iss que volta no callback, sem nenhuma normalização: sem igualar maiúsculas, sem barra final, sem limpar o percent-encoding. O servidor acima define authorization_response_iss_parameter_supported: true, então um iss ausente na resposta precisa ser recusado de imediato.
Auth de servidor MCP remoto: OAuth ou chave de API?
As duas funcionam, e a escolha é sobre escopos e revogação, não sobre teatro de segurança.
| OAuth 2.1 | Chave de API | |
|---|---|---|
| Granularidade | Escopos, só leitura é possível | Acesso total, nenhum escopo |
| Quem guarda o segredo | O cliente, renovável | Você, num arquivo de configuração ou variável de ambiente |
| Revogação | Por aplicativo conectado, pelo usuário | Rode a chave, e quebra todos os consumidores |
| Serve para | Clientes interativos, terceiros | Scripts, cron, seu próprio backend |
No servidor acima, os dois escopos se separam com clareza: mcp:read cobre perfis, contas, posts, conversas e analytics; mcp:write acrescenta publicar, enviar mensagens, responder comentários e conectar ou desconectar contas. Uma chave de API carrega acesso total e nenhum escopo, que é a razão honesta para preferir OAuth em tudo que um modelo conduz de forma interativa. Deixe um agente em mcp:read por uma semana antes de permitir que ele escreva, como em Claude como seu gestor de redes sociais.
O mesmo servidor é acessível no Claude, no ChatGPT, no Claude Code, no Cursor e no VS Code, e custa US$ 49 por mês para 5 contas conectadas, cancela quando quiser, com 7 dias grátis e sem cartão. Se um servidor MCP é a forma certa para a sua integração é outra pergunta, respondida em MCP ou API REST para o seu agente; para um exemplo real dentro de um cliente, veja o servidor MCP do Instagram.
Texto normativo completo: a especificação de autorização do MCP, lida em 16 de outubro de 2026.
Perguntas frequentes
Um servidor MCP precisa de OAuth?
Só se ele for remoto e você quiser autorização por usuário. A autorização é opcional na especificação, e servidores stdio locais não deveriam usá-la de jeito nenhum, e sim pegar as credenciais do ambiente. Um servidor HTTP que atende vários usuários deveria implementá-la, porque uma chave de API dá o mesmo acesso total a quem a tiver.
Qual versão do OAuth o MCP usa?
OAuth 2.1, com PKCE obrigatório em todos os clientes, metadados de recurso protegido (RFC 9728) obrigatórios em todos os servidores, e o parâmetro resource da RFC 8707 obrigatório nas requisições de autorização e de token. A revisão atual é a 2026-07-28, que também traz a identificação do emissor da RFC 9207.
O registro dinâmico de cliente ainda é obrigatório no MCP?
Não. Passou de SHOULD na revisão 2025-06-18 para MAY na atual, e a especificação chama isso de obsoleto, mantido para servidores que não suportam Client ID Metadata Documents. O CIMD é o caminho recomendado: o cliente usa uma URL HTTPS como client ID e o servidor de autorização lê os metadados ali.
Por que meu cliente MCP fica pedindo que eu entre de novo?
Normalmente um refresh token expirado ou revogado com um cliente que repete o fluxo em vez de parar. A especificação manda os clientes limitarem as tentativas e tratarem a falha repetida como definitiva. Verifique se o usuário revogou o app, e se o seu servidor de autorização rotaciona os refresh tokens, o que ele precisa fazer para clientes públicos.
O que significa um 403 de um servidor MCP?
O token é válido mas falta um escopo de que a operação precisa. O servidor deveria devolver error="insufficient_scope" junto com os escopos exigidos, e o cliente deveria reautorizar pedindo esses mais tudo que já tinha. Um 401 quer dizer credenciais ausentes ou inválidas, e um 400 quer dizer que a própria requisição de autorização estava malformada.
Um plug para cada plataforma que seu agente usa
$49 por mês para 5 contas conectadas, cancele quando quiser. Todos os recursos em todos os planos.
Pegue sua chave de API →