Como um servidor MCP chama sua API em nome dos usuários: uma estratégia de token para produção
Explica a estratégia de tokens outbound para servidores MCP: por que o repasse de tokens e M2M falham, e como a troca de tokens mantém as permissões alinhadas com os usuários.
YijunDeveloper
Pare de perder semanas com autenticação de usuários
Lance aplicativos seguros mais rapidamente com o Logto. Integre a autenticação de usuários em minutos e concentre-se no seu produto principal.
Como um servidor MCP chama sua API em nome dos usuários: uma estratégia de token para produção#
Em nosso artigo anterior, compartilhamos nossa experiência geral na construção do servidor MCP remoto do Logto. Neste artigo, abordamos em detalhes o design da arquitetura e o fluxo OAuth.
Com o servidor MCP como limite, a autenticação para um servidor MCP remoto possui duas pernas: entrada e saída.
Entrada: o cliente MCP (VS Code, Cursor, etc.) faz login via OAuth, obtém um token de acesso e o usa para acessar seu servidor MCP
Saída: ao realizar chamadas de ferramentas, o servidor MCP solicita à sua própria API de negócios em nome do usuário
A perna de entrada está bem definida na especificação MCP, com muitas discussões e implementações na comunidade. Também escrevemos um guia de implementação anteriormente. A perna de saída recebe bem menos atenção: o servidor MCP só possui um token para acessar o próprio servidor MCP. Como ele pode chamar sua API de negócios em nome do usuário?
Essa foi a pergunta com a qual continuamos lutando durante a construção do servidor MCP do Logto. O Logto Cloud é um produto típico B2B multi-inquilino: um usuário pode pertencer a vários inquilinos, e as permissões vêm do papel do usuário em cada inquilino. A IA precisa não apenas agir como o usuário, mas também aterrissar no inquilino correto.
Este artigo segue o nosso processo real de tomada de decisões:
Por que o servidor MCP deve ser implantado de forma independente, como um recurso protegido separado
Duas abordagens de saída que rejeitamos (repasse de token e token M2M) e seus problemas
O design final, troca de token mais subject token (token de sujeito), incluindo como os tokens de organização lidam com multi-inquilinato
Alguns princípios a manter ao construir algo semelhante
O núcleo do problema: o duplo papel de um servidor MCP#
Do ponto de vista do OAuth, um servidor MCP remoto desempenha dois papéis ao mesmo tempo:
Para o cliente MCP, ele é um servidor de recurso: o cliente deve fornecer um token para acessá-lo
Para a API de negócios, ele é um cliente: ele fornece um token para acessar outro serviço
Em outras palavras, o servidor MCP exerce um papel diferente em cada lado, e cada lado usa um token diferente. O token que o cliente MCP obtém via OAuth tem o servidor MCP como audiência, portanto a API de negócios vai rejeitá-lo ao validar a audiência.
Assim, a perna de saída é essencialmente um problema de delegação: como o servidor MCP chama APIs downstream como o usuário, dentro das permissões do usuário, sem possuir as credenciais do usuário?
Decisão de arquitetura: o servidor MCP como serviço independente#
Incorporar o endpoint MCP na API de negócios, compartilhando o mesmo processo e pilha de autenticação, parece ser a opção mais direta. Após avaliação, optamos pela implantação independente:
Isolamento de risco: quando tomamos a decisão, o SDK oficial do MCP não estava pronto para produção e o protocolo estava evoluindo rapidamente (a troca de transporte de SSE para HTTP Streamable é um exemplo). O Logto é um serviço IAM, e a disponibilidade do caminho de login é essencial. Com a implantação independente, se o servidor MCP falhar, apenas o ponto de entrada de IA cai. O serviço principal permanece inalterado.
Iteração independente: o ecossistema MCP muda semanalmente, e problemas de compatibilidade de cliente precisam de correções rápidas a qualquer momento, enquanto o serviço principal tem um processo de release rigoroso com testes de regressão. Implantações separadas evitam atrasos mútuos.
Liberdade de runtime: um serviço independente pode escolher o runtime que mais lhe convém. O servidor MCP do Logto roda no Cloudflare Workers: sem estado, escala por requisição e praticamente zero operações. A opção embutida não oferece essa escolha.
O servidor MCP é implantado em seu próprio domínio, mcp.logto.io, sem acoplamento privado ao serviço principal. Se quisermos abrir o código no futuro, nada impede.
A opção embutida também não resolve o problema do token: o endpoint MCP e a API de negócios compartilhariam o mesmo identificador de recurso, então o token recebido pelo cliente MCP teria permissões totais de API. A pergunta "com as permissões de quem essa chamada é feita" muda da troca de token entre serviços para a passagem de permissões em processo, mas o problema permanece. A implantação independente nos força a desenhar explicitamente a fronteira de permissões, que é o tema do restante do artigo.
O servidor MCP deve ter seu próprio identificador de recurso#
A discussão sobre a saída começa por uma questão mais básica: qual deve ser a audiência do token que o cliente MCP obtém?
Reutilizar o identificador de recurso da API de negócios é a opção mais direta: a Management API do Logto já é um recurso protegido padrão do OAuth, então o cliente MCP poderia pedir o token diretamente e o servidor MCP apenas repassaria. Muitas implementações iniciais de servidores MCP fizeram exatamente isso.
O custo: se a audiência do token MCP for a API de negócios, a fronteira de permissões deixa de existir.
Suas ferramentas MCP cuidadosamente desenhadas viram decoração: o token pode chamar toda a API sem passar pelas suas ferramentas
O impacto de um token vazado passa de "algumas operações controladas expostas pelo servidor MCP" para "toda a Management API"
Auditoria independente, limitação de taxa e escopo de permissões para cenários MCP ficam sem base
Então nossa primeira decisão: o servidor MCP é um recurso protegido separado, com seu próprio identificador de recurso (https://mcp.logto.io) e seu próprio escopo.
Essa decisão torna a fronteira de permissões clara, e também deixa a questão de saída concreta: o token só acessa o servidor MCP, então o que o servidor MCP usa para chamar a API?
O Logto Console é um SPA. O modo como chama a Management API é simples: o usuário faz login via OAuth no navegador, recebe um token com a Management API como audiência, e o frontend chama a API diretamente com ele.
Então, o servidor MCP pode ser "outro Console"? Basta fazer o cliente MCP solicitar o token da API de negócios no login, e o servidor MCP o repassa sem conversão:
O apelo dessa abordagem é a extrema simplicidade: o servidor MCP apenas repassa tokens. Mas existem problemas óbvios:
Primeiro, conflita diretamente com a decisão da fronteira de permissões acima. O repasse de token exige que o cliente MCP possua o token da API de negócios, exatamente o que rejeitamos.
Segundo, o servidor MCP deixa de ser realmente um recurso protegido. A audiência dos tokens que recebe não é ele próprio, então a validação de audiência perde sentido e se resume a "verificar a assinatura e issuer". Isso não condiz com a definição de autorização da especificação MCP (o servidor MCP deve agir como servidor de recurso e se declarar via RFC 9728 Protected Resource Metadata). É basicamente disfarçar um backend como SPA.
Terceiro, clientes MCP não cooperam. Um cliente MCP compatível com a especificação solicita tokens usando o Protected Resource Metadata, e a audiência é sempre o servidor MCP. Não existe meio padrão de fazê-lo pedir o token da API de negócios, então o caminho não funciona do lado do cliente.
Se o token do usuário não serve, e quanto à identidade do próprio servidor MCP?
Dê ao servidor MCP um aplicativo M2M (machine-to-machine), obtenha um token por credenciais de cliente e chame a API de negócios com ele. Também é a prática padrão entre serviços internos.
A autenticação de entrada agora também funciona: o token do cliente MCP tem o servidor MCP como audiência, o servidor valida normalmente, e então faz o trabalho com seu próprio token M2M.
A falha fatal é que as permissões do token M2M não têm relação com as do usuário:
Permissão excessiva: as permissões do token M2M representam "o que o servidor MCP pode fazer", não "o que este usuário pode fazer". Um usuário com papel somente leitura consegue excluir aplicações via MCP porque o token M2M tem permissão para isso
Perda de identidade: a API downstream sempre vê o aplicativo M2M como quem chama, e os logs de auditoria não podem ser rastreados até uma pessoa
Deputado confuso: o servidor MCP vira um proxy de alta permissão, e qualquer um que consiga fazer ele chamar uma API pega emprestadas suas permissões
M2M faz sentido para cenários sem contexto de usuário, como jobs agendados e integração sistema a sistema. O servidor MCP é diferente: cada chamada é iniciada por um usuário específico, então deve rodar com a identidade e permissões desse usuário.
Juntando as lições das duas abordagens, temos os requisitos para o design correto:
O token do cliente MCP só pode acessar o servidor MCP (lição da abordagem 1)
Ao chamar APIs downstream, o servidor MCP deve agir como o usuário, dentro das permissões do usuário (lição da abordagem 2)
Isso aponta para um mecanismo padrão: troca de token (RFC 8693). O servidor MCP pega uma credencial que representa o usuário e a troca no servidor de autenticação por um token da API downstream. A identidade e as permissões do usuário são preservadas na troca.
No Logto, essa "credencial que representa o usuário" vem do recurso de impersonação de usuário: o subject token. É uma credencial de curta duração que o servidor solicita para um usuário específico, significando "a próxima troca de token será como este usuário". O usuário não precisa criar ou configurar nada, todo o fluxo é automatizado. O subject token é curto e de uso único, expirando após ser usado.
Existem quatro papéis na arquitetura. O servidor MCP é implantado independentemente em mcp.logto.io:
O fluxo completo de tokens de uma chamada de ferramenta:
Passo-a-passo:
① Validação de entrada. O cliente MCP chama uma ferramenta com o token do usuário. O token tem como audiência o identificador de recurso do servidor MCP, e seu escopo é mcp:all. O servidor MCP verifica assinatura, issuer, audiência e escopo, e obtém a identidade do usuário. A entrada termina aqui. Esse token nunca segue downstream.
② Identidade do serviço. O servidor MCP usa suas próprias credenciais M2M para obter um access token com escopo dedicado, access:mcp:api. Esse escopo tem propósito único: chamar o endpoint do próximo passo.
③ Solicitação do subject token, o passo-chave da cadeia. O servidor MCP chama POST /api/mcp/subject-tokens, um endpoint que a Cloud abre especificamente para MCP, apresentando duas credenciais ao mesmo tempo:
Header Authorization: o token M2M, provando "sou o servidor MCP oficial"
Header x-mcp-user-token: o token do usuário, provando "este usuário me autorizou e a autorização ainda é válida"
A Cloud verifica completamente o token do usuário: assinatura, issuer, expiração, audiência deve ser o identificador de recurso do servidor MCP, e o escopo deve incluir mcp:all. Após a verificação, o userId vem diretamente do claim sub do token. O endpoint não tem parâmetro para informar usuário.
Essa modelagem evita o abuso das credenciais M2M. Se o endpoint aceitasse userId arbitrário, qualquer um com a credencial M2M poderia se passar por qualquer usuário. Assim, o servidor MCP só pode trocar credenciais de um usuário quando a autorização válida desse usuário estiver presente.
O subject token emitido é curto e de uso único. A implementação nunca o armazena e sempre solicita um novo a cada uso.
④ Trocando por tokens de trabalho. Com o subject token, faz-se uma troca de token padrão para obter dois tipos de token conforme necessário:
Token do Cloud API: para operações de nível de usuário, como listar inquilinos e criar inquilinos
Token de organização: com escopo ao inquilino alvo (cada inquilino no Logto Cloud mapeia para uma organização). O token é emitido como o usuário, e suas permissões correspondem exatamente ao papel do usuário naquele inquilino
⑤ Chamada de saída. Chama a API de negócios com o token trocado, e então retorna o resultado ao cliente MCP.
O contexto multi-inquilino também é resolvido na etapa de troca: o inquilino vive na camada de troca, não na camada de conexão. list_tenants lista as opções com o token de Cloud API, o usuário escolhe uma na conversa, e o servidor MCP troca um token de organização para o inquilino escolhido. Um endpoint serve todos os inquilinos, sem necessidade de implantação por inquilino, e um inquilino criado na conversa já está disponível na hora.
Ao analisar os pontos de falha das abordagens anteriores, cada um está coberto:
O raio de impacto do token de usuário é limitado (lição da abordagem 1): sua audiência é apenas o servidor MCP. Se vazar, só pode chamar ferramentas controladas
O token M2M não pode ser abusado (lição da abordagem 2): ele não chama mais a API de negócios diretamente, só prova a identidade do serviço. Emitir um subject token exige apresentar o token válido do usuário ao mesmo tempo, então o servidor MCP só pode trocar por usuários que realmente autorizaram. O problema do deputado confuso desaparece
Sem armazenar credenciais de usuário: subject tokens são solicitados para cada uso e descartados em seguida. O servidor MCP só armazena suas próprias credenciais M2M
Revogação conectada: quando o usuário revoga a autorização, o token de usuário fica inválido, a checagem de x-mcp-user-token falha e a cadeia de saída para imediatamente
Permissões alinhadas ao usuário: tokens trocados são emitidos como o usuário. Um usuário somente leitura permanece somente leitura via MCP, e tentativas de escalonamento de privilégio falham no servidor de autenticação. Logs mostram a identidade real do usuário
Olhando em retrospecto, a credencial M2M tem o papel correto no design final: prova "quem eu sou", mas a habilidade de "agir como o usuário" precisa ser trocada na hora com a autorização válida.
Vendo a cadeia completa, a estratégia de token para um servidor MCP remoto se resume a alguns pontos:
Entrada e saída são duas pernas separadas da autenticação, e a audiência do token MCP deve ser o próprio servidor MCP
A saída é resolvida com troca de token: se seu servidor de autenticação suporta trocar diretamente o token de entrada, o padrão já basta; senão (como no Logto, que exige subject token), use um recurso de impersonação para iniciar a troca no servidor
Multi-inquilinato não muda o mecanismo. O contexto de organização é só parâmetro na troca, produtos single-tenant apenas ignoram esse passo
A IA age em nome do usuário, então as permissões downstream devem ser limitadas a esse usuário
Um teste simples: suponha que o token do cliente MCP vaze. Tudo o que o invasor deve conseguir é chamar ferramentas controladas no servidor MCP, ainda dentro das permissões do usuário. Se conseguir acesso total à API, a fronteira de permissões está rompida.
O ecossistema MCP ainda evolui rápido. A autenticação de entrada já está bem coberta, enquanto o "como o servidor MCP chama APIs downstream" ainda é solucionado por cada equipe. Esperamos que nossa experiência sirva de referência.