Stop Treating MCP Auth Like API Keys

Jul 15 2026 · updated Jul 28 2026 · 13 min · Sieon

Remote Model Context Protocol servers are becoming shared infrastructure. That changes the authorization problem.

When MCP lived mostly as a local stdio bridge inside a developer workstation, many teams could treat credentials as environment configuration: a token in a local config file, a narrowly scoped service account, maybe a wrapper script that knew which API it was allowed to touch. That model does not survive contact with hosted MCP endpoints, multi-tenant agent platforms, browser-based clients, or enterprise identity systems.

A protected remote MCP server is not just another API endpoint that happens to speak JSON-RPC. Under the MCP authorization specification, it acts as an OAuth protected resource. The client discovers which authorization server to use, requests a token for a specific MCP resource, sends the token on every protected request, and the server validates that the token was issued for that MCP server. That boundary is the difference between an agent platform and a token relay.

The practical takeaway: if you are putting MCP servers on the network, design the auth layer around resource identity, audience validation, consent, and least privilege. Do not build a thin API-key passthrough and call it production-ready.

Why this matters now

Agent infrastructure expands the number of places where a token can move.

A single user prompt can cause a host to choose a client connector, discover a server, list tools, invoke a tool, follow a resource link, and ask the user for more information. Each step creates an opportunity to confuse identities: user identity, client identity, MCP server identity, downstream API identity, and model-facing tool identity. If the system blurs those boundaries, a compromised client or malicious server can turn one valid token into broader access than the user intended.

MCP's 2025-06-18 authorization model is explicit about this. Authorization is optional for MCP overall, but when an HTTP-based protected MCP server supports authorization, the server is treated as an OAuth resource server. It must publish protected resource metadata, the client must discover authorization server metadata, and tokens must be requested for the MCP server as the intended resource.

There is one caveat worth making explicit: the MCP spec references OAuth 2.1 draft behavior alongside established OAuth specifications such as RFC 8414, RFC 7591, RFC 8707, and RFC 9728. Production teams should implement the normative MCP requirements, but they should also test their chosen identity provider for exact support around dynamic client registration, resource indicators, PKCE, token audience claims, and metadata discovery. Do not assume every enterprise IdP supports the same subset.

That sounds like OAuth plumbing, but the engineering implication is larger: the MCP server has its own security perimeter. It is responsible for validating inbound tokens, applying access controls to tools and resources, and acquiring separate downstream credentials when it calls another API. It must not accept or forward arbitrary user tokens as if it were an untrusted pipe.

The production architecture

A production remote MCP deployment has at least four roles:

  • Host: the application the user interacts with, such as an IDE, internal agent console, or chat product.
  • MCP client: the connector inside the host that speaks MCP to one server.
  • MCP server: the protected resource exposing tools, resources, prompts, and protocol utilities.
  • Authorization server: the identity component that authenticates the user and issues access tokens for the MCP server.

The authorization server may be co-hosted with the MCP server, but it is still a distinct role. That distinction is useful because the MCP server should be able to say, "These are the authorization servers that can issue tokens for me," and the client should be able to request a token whose audience is this MCP server, not a random downstream API.

sequenceDiagram
    autonumber
    participant User
    participant Host as Agent host
    participant Client as MCP client
    participant MCP as Protected MCP server
    participant AS as Authorization server
    participant API as Downstream API

    User->>Host: Connect data/tool source
    Host->>Client: Create MCP connection
    Client->>MCP: Request protected resource
    MCP-->>Client: 401 + WWW-Authenticate metadata URL
    Client->>MCP: Fetch protected resource metadata
    Client->>AS: Discover auth metadata
    Client->>AS: Authorize with resource=MCP canonical URI
    AS-->>Client: Access token for MCP server
    Client->>MCP: MCP request + Authorization: Bearer ***
    MCP->>MCP: Validate issuer, audience, scopes, expiry
    MCP->>API: Call downstream API with separate credential
    API-->>MCP: API response
    MCP-->>Client: Tool/resource result
    Client-->>Host: Result with policy context

The important line is not the OAuth redirect. It is the handoff from MCP to the downstream API. The MCP server validates a token issued for itself, then uses a separate credential or delegated flow for the downstream system. It does not pass the client's token through.

A useful design review is to ask which boundary owns each decision:

Decision Owner Failing closed should mean
Which MCP server is being accessed? MCP client + protected resource metadata No token request until the resource is known
Which identity provider issues tokens? Protected resource metadata + auth server metadata Reject unknown or downgraded issuers
Is this token for this server? MCP server validation layer Reject before JSON-RPC dispatch
May this caller run this tool? MCP server authorization policy Return a scoped authorization challenge or denial
May the server call the downstream API? MCP server + downstream credential policy No passthrough; use a separate credential path

This table is also a good incident-response map. When a bad call lands in production, you can identify whether discovery, token issuance, token validation, tool authorization, or downstream delegation failed.

Discovery: make the MCP server identify itself

Protected resource metadata is the contract that lets a client understand a protected MCP server before it asks for a token.

RFC 9728 defines a metadata document for OAuth protected resources. In MCP, a protected server must use this mechanism to indicate which authorization servers can issue tokens for it. The metadata can also describe supported scopes, bearer-token methods, documentation URLs, terms, policy URLs, JWKs, and human-readable resource names.

A minimal metadata response for an MCP server might look like this:

{
  "resource": "https://mcp.example.com/mcp",
  "resource_name": "Example Production MCP Server",
  "authorization_servers": [
    "https://auth.example.com"
  ],
  "scopes_supported": [
    "mcp:tools.read",
    "mcp:tools.execute",
    "tickets.read",
    "tickets.write"
  ],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://docs.example.com/mcp"
}

The first production check is simple: the resource value must be the canonical URI the client uses in OAuth resource parameters. If the server is available at multiple paths or behind a gateway, standardize the resource identifier and test it. Inconsistent resource strings create intermittent failures, duplicate consent prompts, and weak audience validation.

The second check is operational: unauthenticated requests should fail in a way clients can use. MCP authorization relies on WWW-Authenticate to point clients at protected resource metadata during a 401 Unauthorized response. If your gateway strips that header, your OAuth flow may work in staging and fail in real clients.

The resource parameter is not optional glue

The MCP authorization specification requires clients to include the OAuth resource parameter in both authorization requests and token requests. That parameter identifies the MCP server the token is intended for.

This matters because without audience binding, tokens become portable bearer strings. A token minted for one service can be replayed against another service that forgets to validate audience. In agent systems, that is especially dangerous because tool calls can hide the path a credential takes. The user sees "read tickets" or "summarize repository"; the runtime may be crossing several services.

Production clients should treat resource as a first-class field in their connection configuration:

MCP server URL:      https://mcp.example.com/mcp
OAuth resource:      https://mcp.example.com/mcp
Authorization issuer: https://auth.example.com
Allowed redirect URI: http://127.0.0.1:3917/oauth/callback
Baseline scopes:     mcp:tools.read tickets.read

Production servers should reject tokens that are missing the expected audience, have the wrong issuer, have expired, or lack the required scope for the requested tool/resource. This validation belongs before JSON-RPC dispatch. The tool handler should not be the first line of authorization defense.

Token passthrough is the failure mode

The most tempting remote MCP implementation is also the most dangerous:

  1. The client obtains a token for a downstream SaaS API.
  2. The client sends it to the MCP server.
  3. The MCP server forwards that same token to the downstream API.
  4. The MCP server returns the result as a tool response.

That model makes the MCP server a confused proxy. It cannot reliably prove the token was issued for it. It may not know which client initiated the call. Its logs lose accountability. Downstream services may see access patterns that bypass the controls the MCP server was supposed to enforce. If the token leaks inside MCP logs, traces, prompts, or tool errors, the blast radius is the downstream API, not just the MCP layer.

The MCP security guidance calls token passthrough an anti-pattern and explicitly forbids accepting tokens that were not issued for the MCP server. In production, treat this as a deploy gate: an MCP server should either validate an audience-bound token issued for itself or refuse the request.

When the MCP server needs a downstream API, use one of these patterns instead:

Pattern Use when Operational requirement
Server-owned service credential The server performs bounded backend operations Strict server-side authorization, audit logs, and rate limits per user/client
On-behalf-of exchange The identity provider supports delegated token exchange Token exchange policy binds user, client, resource, scopes, and expiry
Per-user linked account The user connects a SaaS account through the MCP service Store refresh tokens securely, rotate, audit, and let users revoke
Human-approved elevation The tool performs sensitive writes or admin actions Step-up consent, visible tool input, short-lived scope, and callback audit trail

The pattern is less important than the boundary: inbound MCP token and outbound downstream credential are not the same artifact.

Tool schemas do not replace authorization

MCP tools can expose input schemas and output schemas. Structured tool output helps clients validate results and helps models reason about returned data. This is good engineering, but it is not an authorization model.

A tool schema can say that ticket_id is a string. It cannot prove the caller may read that ticket. A tool annotation can describe a tool as read-only. Clients must treat annotations as untrusted unless they come from trusted servers. A model may choose when to call a tool, but the server must still enforce access controls.

Put authorization checks on the server side:

  • Map scopes to tool families, not only to endpoints.
  • Check object-level permissions inside tool handlers.
  • Revalidate user/client identity on long-running operations.
  • Treat tool output as data that can leak secrets; sanitize before returning.
  • Rate-limit high-cost tools by user, client, and tenant.

For sensitive operations, keep a human in the loop. The tools specification recommends visible UI and confirmation prompts for operations where the user should understand what the model is about to do. That UX requirement is part of the security system, not decorative polish.

Scope design: start small, elevate deliberately

The bad scope model is easy to recognize: mcp:all, files:*, or admin granted during the first connection because the team did not want to revisit OAuth later.

A better model starts with discovery and low-risk reads, then elevates at the moment of need. For example:

  • mcp:tools.read for listing tools and reading tool metadata.
  • tickets.read for search and summarization.
  • tickets.write for creating or updating tickets.
  • tickets.admin for workflow configuration.

When a user asks the agent to perform a write, the server can return an authorization challenge for the missing scope. The client can request only that scope, show the user the reason, and continue if approved. The result is better security and better consent comprehension: users approve the operation they actually asked for, not a wall of speculative permissions.

Servers should also tolerate down-scoped tokens. If an authorization server grants fewer scopes than requested, the MCP server should allow what the token permits and challenge only when the user crosses a boundary.

Discovery is also an SSRF surface

OAuth metadata discovery requires clients to fetch URLs. A malicious or compromised MCP server can try to abuse that behavior by pointing metadata fields at internal addresses, cloud metadata endpoints, or unexpected hosts.

Client implementers should apply the same network controls they would use for webhook delivery or URL unfurling:

  • Require HTTPS for authorization metadata except explicit localhost development flows.
  • Block private IP ranges, link-local addresses, and cloud metadata endpoints.
  • Resolve DNS safely and defend against DNS rebinding.
  • Enforce allowlists for enterprise-managed deployments.
  • Set short timeouts and small response limits.
  • Log metadata URL, resolved IP, redirect chain, and decision.

This is not theoretical hygiene. Agent clients often run in privileged environments: developer laptops, CI runners, internal admin consoles, or cloud workers with metadata credentials. OAuth discovery must not become a general-purpose internal network scanner.

Rollout checklist for production teams

Use this checklist before exposing a remote MCP server beyond a single trusted development environment.

Server contract

  • Publish protected resource metadata for the canonical MCP resource.
  • Return 401 with a usable WWW-Authenticate challenge when authorization is missing.
  • Accept bearer tokens only in the Authorization header.
  • Validate issuer, audience/resource, expiry, signature, tenant, and scopes before dispatch.
  • Reject tokens issued for downstream APIs or other MCP resources.
  • Keep inbound MCP tokens out of logs, traces, tool results, and prompts.

Client contract

  • Discover protected resource metadata and authorization server metadata.
  • Send the resource parameter in both authorization and token requests.
  • Use PKCE for authorization-code flows.
  • Register exact redirect URIs; do not rely on wildcard matching.
  • Apply SSRF protections during metadata discovery.
  • Show meaningful consent and elevation UI for sensitive tools.

Operations contract

  • Version resource identifiers intentionally.
  • Alert on audience-validation failures, repeated scope challenges, and token validation errors.
  • Sample traces for tool latency and failures without logging bearer tokens.
  • Contract-test the WWW-Authenticate header through every gateway and CDN path.
  • Load-test metadata discovery and token validation caches.
  • Run periodic negative tests with wrong-audience, expired, and over-scoped tokens.

Common pitfalls

Using the public URL as the resource one day and the internal URL the next. Pick the canonical resource URI and stick to it. If paths distinguish tenants or server instances, document that choice.

Letting the gateway terminate auth but not forwarding identity safely. If a gateway validates tokens, the MCP server still needs a trustworthy identity context and defense against header spoofing. Mutual TLS, signed identity headers, or colocated validation are safer than plain forwarded headers.

Treating dynamic client registration as universally available. The MCP spec recommends support because clients may not know servers in advance. Some enterprise authorization servers still require explicit registration. Build a fallback onboarding path rather than silently skipping audience binding.

Mixing consent layers. User consent to connect an MCP client is not the same as consent for a proxy server to access a third-party API, and neither is the same as confirmation for a destructive tool call. Keep the layers visible.

Publishing scopes as a product catalog. scopes_supported can help clients, but it should not become an invitation to request every possible privilege. Design baseline scopes and challenge for elevation.

The engineering posture

Remote MCP is useful because it standardizes how agents reach tools and context. That standardization only pays off if teams also standardize the security boundary.

The boundary is straightforward:

  1. The MCP server identifies itself as a protected resource.
  2. The client asks for a token for that resource.
  3. The server validates that the token was issued for that resource.
  4. The server performs tool/resource authorization before execution.
  5. The server uses separate credentials for downstream systems.

Everything else is implementation detail. Important detail, but detail.

If your MCP server is still accepting long-lived API keys, forwarding user tokens to downstream APIs, or relying on tool schemas as a substitute for access control, treat that as technical debt. The production pattern is audience-bound OAuth, explicit consent, least-privilege scopes, and observable authorization failures.

That is what lets MCP become agent infrastructure instead of another place where secrets go to sprawl.

Pre-publish implementation checks

Before this pattern becomes a platform standard, automate the checks that tend to regress:

  • A request without a token returns 401 and a parseable WWW-Authenticate challenge.
  • A token with the wrong audience is rejected before the MCP method handler runs.
  • A token for the right audience but missing scope receives a precise denial or elevation challenge.
  • Metadata discovery refuses private IP ranges and unexpected redirects.
  • Tool execution logs include correlation IDs, user/client identity, tool name, and authorization decision, but never bearer tokens.
  • Documentation tells client teams the canonical resource URI, supported scopes, and redirect URI requirements.

These checks are mundane. That is the point. Authorization boundaries should be boring enough to test in CI and visible enough to debug at 2 a.m.

References

  1. Model Context Protocol specification, 2025-06-18
  2. MCP Authorization, 2025-06-18
  3. RFC 9728: OAuth 2.0 Protected Resource Metadata
  4. MCP Tools specification, 2025-06-18
  5. MCP Security Best Practices