Padrões de autenticação MCP: OAuth, tokens e quando usar cada um

Padrões de autenticação MCP: OAuth, tokens e quando usar cada um

Última verificação: 22 de setembro de 2026
10 min de leitura
Guia
500+ projetos WP
Integração IA

#Padrões de autenticação MCP: OAuth, tokens e quando usar cada um

A especificação Model Context Protocol (modelcontextprotocol.io) define transportes e primitivas. Não define autenticação. Com razão, porque a autenticação é uma questão de transporte e de implementação, não do protocolo. É também a origem do erro de produção mais comum que encontro: um servidor MCP exposto à Internet sem autenticação, com ferramentas que alteram estado. Quando há dados pessoais em jogo, junta-se ainda o artigo 32.º do RGPD, que obriga a medidas técnicas adequadas.

Este artigo faz parte do pilar sobre desenvolvimento de servidores MCP.

#TL;DR

  • Acesso só de leitura a dados públicos com rate limiting é o único caso legítimo de acesso anónimo.
  • Tokens de API com scopes, guardados com hash e com TTL curto, cobrem B2B e headless.
  • OAuth 2.1 com PKCE cobre assistentes de consumo que atuam em nome de um utilizador autenticado.
  • O rate limiting fica associado ao principal; as ferramentas que alteram estado recebem um bucket mais apertado.
  • Cada evento de autenticação é registado: emitido, usado, revogado, rejeitado.

#Como escolher o método de autenticação MCP

Antes de escolher um padrão de autenticação, passo sempre pelas mesmas cinco perguntas:

  1. Alguma ferramenta altera estado? Se sim, o acesso anónimo fica de fora.
  2. O agente atua em nome de um utilizador humano concreto? Se sim, OAuth.
  3. O agente atua como integração B2B sem utilizador humano? Se sim, token de API com scopes.
  4. A superfície de dados é acessível a partir da Internet pública? Se sim, o rate limiting é obrigatório.
  5. No mesmo servidor misturam-se ferramentas que alteram estado e ferramentas só de leitura? Se sim, scope de autenticação por ferramenta, não por servidor.

A árvore resulta em três padrões:

PadrãoQuando usarImplementação
Anónimo + rate limit por IPCatálogo público, só de leitura, carga limitadaO Worker verifica apenas o bucket do IP
Token de API com scopesIntegração B2B, runtime de agente headless, sem utilizador humanoJWT com claims, armazenamento com hash, rotação
OAuth 2.1 + PKCEAgente de consumo para um utilizador autenticadoAuthorization code flow padrão

Os padrões não se excluem. Um servidor de produção real costuma correr os três, e cada ferramenta fica marcada com o scope que exige.

#Acesso MCP anónimo só de leitura com rate limiting

Uma ferramenta de navegação no catálogo que envolve /wp-json/wc/v3/products?stock_status=instock expõe os mesmos dados que o site público já mostra. Pôr autenticação à frente dela não melhora a postura de segurança; apenas reduz a disponibilidade. A ameaça real é a carga: um ciclo de um agente ou um scrape da concorrência consegue esmagar a origem WooCommerce.

Implementação:

async function checkAnonymousRateLimit(request: Request, env: Env): Promise<boolean> {
  const ip = request.headers.get("CF-Connecting-IP") ?? "unknown";
  const key = `rl:anon:${ip}`;
  const count = Number((await env.RATE_LIMIT.get(key)) ?? "0");
  if (count >= 60) return false;
  await env.RATE_LIMIT.put(key, String(count + 1), { expirationTtl: 60 });
  return true;
}

60 pedidos por minuto por IP é o meu valor por omissão. Um contador baseado em KV é aproximado (as escritas em KV são eventually consistent); para limites estritos, os Durable Objects ou o binding nativo de Rate Limiting da Cloudflare são a ferramenta certa.

O que este padrão não cobre: nenhuma ferramenta que devolva dados específicos de um utilizador, nenhuma que altere estado, nenhuma que revele o stock de produtos não públicos. Essas exigem um principal real.

#Tokens de API com scopes para integrações B2B com MCP

O caso B2B: a integração de um parceiro fornece um agente que chama o seu servidor MCP em nome da organização do parceiro, não de um utilizador humano concreto. Exemplos: um agente de sincronização de stock num grossista, uma integração de marketplace num agregador, um agente de análise que reporta tendências de encomendas ao BI.

O fluxo:

  1. Um administrador no backend do WordPress cria um token com nome, um conjunto de scopes (catalogue:read, inventory:read, orders:write) e uma data de expiração (por omissão, 90 dias).
  2. O token é mostrado ao administrador uma vez e depois guardado como hash SHA-256 mais o conjunto de scopes mais a expiração.
  3. O parceiro configura o seu cliente MCP com o token no cabeçalho Authorization: Bearer <token>.
  4. O Worker calcula o hash do token recebido e procura-o no KV. Se o hash corresponder, a expiração estiver no futuro e o scope exigido pela ferramenta estiver no conjunto do token, a chamada segue.
async function verifyApiToken(authHeader: string | null, env: Env): Promise<TokenContext | null> {
  if (!authHeader?.startsWith("Bearer ")) return null;
  const token = authHeader.slice(7);
  const hash = await sha256(token);
  const record = await env.TOKENS.get(`tok:${hash}`, "json") as TokenRecord | null;
  if (!record) return null;
  if (record.expiresAt < Date.now()) return null;
  return { tokenId: record.id, scopes: record.scopes, principal: record.principal };
}

function requireScope(ctx: TokenContext, scope: string): void {
  if (!ctx.scopes.includes(scope)) {
    throw new McpError("forbidden", `Tool requires scope ${scope}`);
  }
}

Dois hábitos operacionais mantêm o padrão honesto:

Rotação, não eternidade. Um token que nunca expira é um token que daqui a seis meses aparece num repositório público no GitHub. 90 dias por omissão, com um fluxo de renovação em que o token antigo e o novo coexistem durante 7 dias, é o padrão que sobrevive a parceiros reais.

Armazenamento com hash, não em texto simples. Se o seu KV for exposto, os hashes sem o token original são inúteis. Se guardou os tokens em texto simples, todas as integrações de parceiros precisam de rotação imediata. A diferença de custo no momento da emissão é uma chamada a sha256.

#OAuth 2.1 com PKCE num servidor MCP

O caso de consumo: o utilizador abre o Claude Desktop, liga a conta da sua loja por OAuth e pede ao agente “mostra a minha última encomenda”. O agente tem agora de chamar o seu servidor MCP com credenciais que dizem “estou a atuar pelo utilizador 4231”.

O OAuth 2.1 (draft-ietf-oauth-v2-1) é o perfil consolidado. O PKCE (RFC 7636) é obrigatório para clientes públicos (onde se incluem os assistentes de desktop sem uma parte de servidor confidencial).

O fluxo em cinco passos:

  1. O host MCP abre o browser no seu endpoint de autorização com response_type=code, code_challenge=<hash S256 do verifier> e os scopes pedidos.
  2. O utilizador inicia sessão no seu site WordPress (ou no seu fornecedor de autenticação) e aceita os scopes.
  3. O seu endpoint de autorização redireciona o host com um código de autorização de utilização única.
  4. O host troca o código mais o code_verifier original no seu endpoint de tokens por um access token (TTL curto, 1 hora) e um refresh token (TTL mais longo, 30 dias).
  5. O host chama o servidor MCP com Authorization: Bearer <access_token>. O Worker verifica a assinatura do token, a expiração e o scope face à ferramenta chamada.

O access token é um JWT assinado. O Worker verifica-o com a Web Crypto API (documentação do Cloudflare Workers) sem biblioteca externa:

async function verifyJwt(token: string, env: Env): Promise<JwtClaims | null> {
  const [headerB64, payloadB64, sigB64] = token.split(".");
  const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
  const sig = base64UrlDecode(sigB64);
  const valid = await crypto.subtle.verify("RS256", env.PUBLIC_KEY, sig, data);
  if (!valid) return null;
  const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(payloadB64)));
  if (payload.exp * 1000 < Date.now()) return null;
  return payload as JwtClaims;
}

Os scopes no JWT espelham os do padrão de token com scopes: catalogue:read, orders:read, orders:write. O ID do utilizador WordPress fica no claim sub, por isso uma chamada orders:read devolve apenas as encomendas desse utilizador.

#Como combinar OAuth, tokens e acesso anónimo num servidor MCP

Um servidor MCP real para WooCommerce costuma expor:

  • catalogue.list e product.detail: anónimo + rate limit por IP.
  • inventory.check (para parceiros): token com scope inventory:read.
  • order.status (para o utilizador autenticado): OAuth com orders:read.
  • order.intent (para o utilizador autenticado): OAuth com orders:write, mais um rate limit mais apertado.

A lógica de pre-dispatch no Worker percorre os padrões:

async function authenticate(request: Request, toolName: string, env: Env): Promise<Principal> {
  const requirement = TOOL_AUTH_REQUIREMENTS[toolName];
  if (requirement === "anonymous") {
    if (!await checkAnonymousRateLimit(request, env)) throw new McpError("rate_limit");
    return { kind: "anonymous" };
  }
  const auth = request.headers.get("Authorization");
  if (requirement === "api_token") {
    const ctx = await verifyApiToken(auth, env);
    if (!ctx) throw new McpError("unauthorized");
    return { kind: "api_token", ctx };
  }
  if (requirement === "oauth") {
    const claims = auth?.startsWith("Bearer ") ? await verifyJwt(auth.slice(7), env) : null;
    if (!claims) throw new McpError("unauthorized");
    return { kind: "oauth", claims };
  }
  throw new Error(`Unknown auth requirement for ${toolName}`);
}

O mapa TOOL_AUTH_REQUIREMENTS é a única fonte de verdade sobre que ferramenta precisa de que modo de autenticação. Nenhuma ferramenta é adicionada sem uma entrada explícita.

#Limites de pedidos MCP por IP, token e utilizador

O tráfego anónimo recebe um bucket por IP. O tráfego com token recebe um bucket por token. O tráfego OAuth recebe um bucket por utilizador. As ferramentas que alteram estado recebem um teto mais baixo independentemente do principal.

Para um cenário WooCommerce, os meus buckets por omissão são:

Categoria de ferramentaAnónimoTokenOAuth
catalogue.* (leitura)60 / minuto / IP600 / minuto / token120 / minuto / utilizador
inventory.* (leitura)não permitido300 / minuto / tokennão permitido
order.status (leitura)não permitido60 / minuto / token60 / minuto / utilizador
order.intent (escrita)não permitido30 / minuto / token10 / minuto / utilizador

Os números são um ponto de partida; os valores certos resultam de observar tráfego real durante duas semanas e afinar. O que conta é a estrutura.

#Que eventos de autenticação MCP registar

Cada evento relevante para a autenticação vai para o registo:

  • Token emitido. Administrador, principal de destino, scopes, expiração.
  • Token usado. ID do token, nome da ferramenta, principal, latência, sucesso/falha.
  • Token revogado. ID do token, quem revogou, porquê.
  • Token rejeitado. Motivo (expirado, scope em falta, hash não corresponde), IP, user agent.
  • Troca de código OAuth. ID do utilizador, scopes concedidos, refresh token emitido.
  • Refresh OAuth. ID do utilizador, novo access token, token antigo substituído.

Os registos seguem pelo Cloudflare Logpush para armazenamento de longo prazo. Uma consulta de dashboard que acompanha “tokens usados nas últimas 24 horas que não tinham sido usados nos 90 dias anteriores” apanha um provável roubo de token. Uma consulta sobre “verificações de token falhadas por IP” apanha tentativas de credential stuffing.

#Temas relacionados

Este artigo trata da autenticação. O reforço mais amplo do WordPress está no guia de segurança WordPress. A página do serviço é desenvolvimento de servidores MCP.

Orçamento individual, porque o âmbito da autenticação depende dos padrões que o seu ambiente exige; um servidor só de leitura em modo anónimo é um projeto diferente de uma superfície completa que emite OAuth.

Próximo passo

Transforme o artigo numa implementação real

Este bloco reforça a ligação interna e conduz o leitor para o passo seguinte mais útil dentro da arquitetura do site.

Quer implementar isto no seu site?

Se está a planear headless WordPress, desacoplamento de frontend ou migração para Astro, posso desenhar e implementar a arquitetura completa.

Cluster relacionado

Explorar outros serviços WordPress e base de conhecimento

Reforce o seu negócio com suporte técnico profissional em áreas-chave do ecossistema WordPress.

FAQ do artigo

Perguntas frequentes

Respostas práticas para aplicar o tema na execução real.

SEO-readyGEO-readyAEO-ready5 Q&A
A especificação MCP exige autenticação?#
Não. A especificação Model Context Protocol deixa a autenticação à camada de transporte. Os transportes stdio podem ser considerados de confiança só por correrem localmente. Os transportes HTTP expostos à Internet precisam de uma camada de autenticação que o SDK não fornece por omissão.
Quando é aceitável um servidor MCP anónimo?#
Quando todas as ferramentas expostas são só de leitura sobre dados públicos e o impacto da carga está limitado por rate limiting por IP. Uma ferramenta de navegação no catálogo que envolve /wp-json/wc/v3/products?stock_status=instock é aceitável. Uma ferramenta de consulta de encomendas não é.
Porquê precisamente OAuth 2.1?#
O OAuth 2.1 consolida as orientações atuais do OAuth 2.0 mais PKCE, remove os fluxos descontinuados implicit e resource-owner-password e corresponde ao que o Claude Desktop e outros hosts MCP suportam nativamente para acesso delegado de agentes.
Onde ficam guardados os tokens com scopes?#
Os tokens são emitidos a partir de uma área de administração do lado do WordPress, guardados como valor com hash mais scope mais data de expiração e verificados em cada pedido MCP. Guardá-los em texto simples é o mesmo erro que guardar palavras-passe em texto simples.
Como se relaciona o rate limiting com a autenticação?#
Os limites ficam associados ao principal: um token autenticado tem o seu próprio bucket, o tráfego anónimo partilha um bucket por IP. As ferramentas que alteram estado recebem um bucket mais apertado independentemente do principal, para que um token comprometido não consiga executar order.intent em ciclo.

Precisa de FAQ adaptado ao setor e mercado? Criamos uma versão alinhada com os seus objetivos de negócio.

Fale connosco

Artigos Relacionados