Skip to content

MCP sobre OAuth 2.1

A doc de MCP em Fundamentos cobre o que o protocolo é: os papéis de host, cliente e servidor, o transporte, a questão de estado. Esta página pega o fio onde a autorização entra. Quando um MCP server é exposto na rede, como ele sabe que quem chama tem permissão? A resposta do MCP é não reinventar autenticação: ele exige OAuth 2.1 mais um punhado de RFCs de descoberta, e monta sobre isso.

Vale delimitar o escopo antes: autorização no MCP se aplica aos transportes sobre HTTP. Um servidor stdio, que roda como subprocesso local do host, pega credenciais do ambiente e fica fora desta conversa. Tudo o que segue é sobre o servidor remoto, o do Streamable HTTP.

O ponto de arquitetura que organiza o resto é o mapeamento de papéis do OAuth sobre o MCP. O MCP server é um resource server do OAuth 2.1: ele recebe e valida access tokens, e mais nada. Quem emite os tokens é um authorization server à parte, que pode ser um provedor de identidade externo. E o MCP client é o cliente OAuth, que faz as requisições em nome do usuário.

Nem sempre foi assim, e a mudança vale ser conhecida porque muda o desenho de quem opera um servidor. Na primeira versão da spec de autorização (revisão 2025-03-26), o MCP server acumulava o papel de seu próprio authorization server: era ele quem servia /authorize, /token e /register e cunhava os próprios tokens. A revisão 2025-06-18 separou os papéis: o MCP server virou só um resource server, que aponta o cliente para o authorization server em que confia. Na prática, isso libera o operador de um MCP server de manter um authorization server completo; ele delega a emissão a um Google, um Auth0, um Keycloak da vida. É a mesma lógica de fronteira de segurança que atravessa o protocolo, agora aplicada à emissão de tokens: cada responsabilidade num papel só.

Como o cliente descobre o authorization server

Section titled “Como o cliente descobre o authorization server”

O MCP é um ecossistema aberto onde o cliente encontra servidores que nunca viu, e portanto não sabe de antemão quem emite os tokens daquele servidor. O discovery resolve isso a partir de uma primeira requisição sem token.

sequenceDiagram
    participant C as MCP Client
    participant S as MCP Server
    participant AS as Authorization Server
    C->>S: requisição sem token
    S->>C: 401 com cabeçalho WWW-Authenticate
    C->>S: GET /.well-known/oauth-protected-resource
    S->>C: lista de authorization_servers
    C->>AS: GET /.well-known/oauth-authorization-server
    AS->>C: endpoints de autorização
    Note over C,AS: Authorization Code com PKCE
    C->>AS: obtém o access token
    C->>S: requisição com Authorization Bearer
    S->>C: valida o audience e devolve o recurso

O servidor responde à chamada sem token com um 401 Unauthorized e um cabeçalho WWW-Authenticate que aponta para os seus metadados de recurso protegido, conforme a RFC 9728 (Protected Resource Metadata). O cliente busca esses metadados em /.well-known/oauth-protected-resource, e ali encontra o campo authorization_servers, que lista quem emite tokens para aquele servidor. De posse do endereço do AS, o cliente busca os metadados dele em /.well-known/oauth-authorization-server, conforme a RFC 8414 (Authorization Server Metadata), e descobre os endpoints de /authorize e /token. Dali em diante roda o Authorization Code com PKCE normal, e passa a anexar o bearer token às requisições ao MCP server.

Um cliente OAuth precisa de um client_id para se apresentar ao AS, e no mundo fechado tradicional esse ID sai de um cadastro manual feito antes. No MCP isso não escala: o cliente descobre servidores (e seus authorization servers) em tempo de execução, muitos deles, e ninguém vai pré-cadastrar cada combinação à mão. A saída é a RFC 7591 (Dynamic Client Registration): o cliente faz um POST ao endpoint de registro do AS e recebe um client_id na hora, sem interação humana, na primeira vez que topa com aquele servidor. Onde o AS não suporta registro dinâmico, o cliente cai para um ID fixo ou digitado pelo usuário. Revisões mais novas da spec acrescentaram alternativas ao DCR (IDs de cliente baseados em URL), porque o registro dinâmico aberto pesa para quem opera o AS, mas o mecanismo continua central.

A peça que fecha a segurança é o parâmetro resource, da RFC 8707 (Resource Indicators). O MCP client é obrigado a enviá-lo tanto na requisição de autorização quanto na de token, nomeando, pela URI canônica, o MCP server específico para o qual aquele token serve. Isso atrela o audience do token a um servidor só, e resolve dois problemas que andam juntos.

O primeiro é o replay entre serviços. Sem essa amarra, um token que o usuário obteve para o servidor A poderia ser reapresentado ao servidor B. Com o audience preso ao A, o B rejeita o token que não o nomeia, e o MCP server é obrigado a validar que o token apresentado foi emitido para ele.

O segundo é o confused deputy, o problema que dá nome à seção. Um MCP server costuma ser um intermediário: ele atende o cliente, mas por baixo chama APIs de terceiros. Se ele repassasse adiante o token que recebeu do cliente, a API de destino poderia confiar nesse token como se ele viesse do próprio MCP server, concedendo um acesso que o usuário nunca consentiu para aquele destino. O servidor seria o “delegado confuso”: usando sua posição de confiança para agir sobre um pedido que não devia honrar. A spec proíbe o repasse de token de forma explícita: o MCP server não deve repassar o token que recebeu do cliente; se precisa chamar uma API upstream, ele obtém um token próprio, como cliente OAuth por direito, emitido pelo authorization server daquela API. Tokens presos ao audience, validação obrigatória do audience e a proibição de repasse, juntos, fecham essa classe de ataque.

O cliente típico do MCP é um app desktop, uma CLI ou um agente local: um cliente público, que não guarda segredo, conectando-se a servidores que nunca viu. A linha de base obrigatória do OAuth 2.1 encaixa exatamente nessas restrições. O PKCE obrigatório para todos impede que o authorization code de um cliente público seja interceptado; a remoção do grant implícito e do de senha tira os tokens da URL e evita entregar a senha do usuário a um cliente MCP; a correspondência exata de redirect URI barra o roubo de code por redirecionamento; o token só no cabeçalho e a rotação de refresh token protegem a credencial que o cliente público não teria como guardar. Sobre essa base o MCP empilha as RFCs de descoberta, registro e audience (9728, 8414, 7591, 8707) para fazer o fluxo funcionar num ecossistema de conhecimento zero. Escolher a 2.1 em vez da 2.0 é herdar os padrões endurecidos de uma vez, em vez de reabrir cada decisão de segurança.

Papéis: resource server, authorization server, MCP client, MCP server Padrões: RFC 9728, RFC 8414, RFC 7591, RFC 8707, OAuth 2.1 Discovery: /.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server, WWW-Authenticate Segurança: confused deputy, token passthrough, audience binding, registro dinâmico de cliente