AIARTICLE

MCP Made OAuth 2.1 Authorization Official in 2025: The Checklist for Servers Without Authentication

Article · AI
Reading
6 min
Tables
1
Code
1
In short

The Model Context Protocol's Authorization specification, published in the June 2025 revision, defines how MCP servers exposed over HTTP must validate OAuth 2.1 tokens. Anyone who deployed a server without this layer risks falling out of compliance and breaking when stricter clients start requiring the full flow.

Why This Matters Now, Even Though It's From 2025

The Model Context Protocol's Authorization specification was published in the protocol's June 18, 2025 revision and is available at modelcontextprotocol.io. It defines, for the first time in full, how an MCP server exposed over HTTP must behave as an OAuth 2.1 resource server: validating tokens, discovering the authorization server, and rejecting requests without a valid credential.

The problem is that a large share of MCP servers in production were built during the 2024-2025 agent rush without this layer. The spec itself makes clear that authorization is OPTIONAL for MCP implementations, but anyone who chooses to use HTTP transport (as opposed to STDIO, which pulls credentials from the environment) SHOULD follow the specification to the letter. That means every HTTP server without auth isn't breaking a mandatory rule, but it is off the recommended path, and that costs something the moment a stricter client shows up on the other side.

What the Spec Actually Defines

The design has three roles: the MCP server acts as the OAuth 2.1 resource server, the MCP client makes requests on behalf of the user (resource owner), and the authorization server is responsible for authenticating the person and issuing the token. The spec doesn't require the authorization server to be the MCP server itself: it can be a separate service, such as Auth0, Okta, or an internal Keycloak.

Four standards form the required technical base:

  • OAuth 2.1 (draft-ietf-oauth-v2-1-13), with security measures for confidential and public clients;
  • OAuth 2.0 Protected Resource Metadata (RFC9728), which the MCP server MUST implement;
  • OAuth 2.0 Authorization Server Metadata (RFC8414), which the authorization server MUST provide;
  • OAuth 2.0 Dynamic Client Registration (RFC7591), which the authorization server and client SHOULD support.

Protected Resource Metadata: How the Server Announces Itself

In practice, this means every HTTP MCP server needs to expose a metadata document describing where the authorization server responsible for it is located. When the server receives a request without a valid token, it MUST respond with 401 Unauthorized and include a WWW-Authenticate header pointing to the URL of that metadata, per RFC9728 Section 5.1:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"

The document returned at that URL needs to include the authorization_servers field with at least one valid authorization server. This is the field the MCP client uses to discover where to send the user to authenticate, then following RFC8414 to obtain the authorization server's endpoints (token endpoint, authorization endpoint, etc.).

PKCE and the Resource Parameter: Binding the Token to the Right Server

Two OAuth 2.1 mechanisms carry extra weight within MCP. The first is PKCE: every MCP client MUST implement it per OAuth 2.1 Section 7.5.2, because without it an intercepted authorization code can be exchanged for a token by anyone who captures it. The second is the resource parameter from RFC 8707: the client MUST include this parameter in both the authorization request and the token request, identifying the canonical URI of the target MCP server.

The spec itself lists examples. Valid canonical URIs include https://mcp.example.com/mcp, https://mcp.example.com:8443, and https://mcp.example.com/server/mcp. Meanwhile, mcp.example.com (without a scheme) and https://mcp.example.com#fragment (with a fragment) are invalid. A typical authorization request carries the parameter like this: &resource=https%3A%2F%2Fmcp.example.com.

This binding exists to prevent a token issued for one service from being reused on another. On the server side, the counterpart is mandatory: the MCP server MUST validate that the token it receives was issued specifically for it as the audience, per RFC 8707 Section 2. A token that doesn't carry the correct audience has to be rejected with 401.

Dynamic Client Registration: Avoids Friction, But Is Optional

Unlike the items above, RFC7591 comes in as SHOULD, not MUST. The idea is that the MCP client doesn't need to know all possible servers and their authorization servers in advance: it registers dynamically and receives a client_id without manual intervention. This reduces friction, especially for anyone building a generic client that connects to multiple third-party MCP servers.

If the authorization server doesn't support dynamic registration, the spec lays out two options: either the client embeds a fixed client_id (and credentials, if applicable) specific to that authorization server, or it presents a UI for the user to enter that data after manually registering the OAuth client. For anyone operating an internal MCP server with a small, known number of clients, skipping dynamic registration is usually acceptable; the cost shows up when the server needs to be accessed by varied, unknown clients.

The Mistake That Becomes a Vulnerability: Token Passthrough

The most sensitive part of the spec, and the easiest to get wrong under deadline pressure, is the section on confused deputy and token passthrough. If the MCP server acts as a proxy to an upstream API, it MUST NOT forward the token it received from the MCP client without modification. The token used against the upstream API must be a different one, issued by that API's authorization server.

The reason is straightforward:

Attackers can exploit MCP servers acting as intermediaries to third-party APIs, leading to confused deputy vulnerabilities. By using stolen authorization codes, they can obtain access tokens without user consent.

Model Context Protocol, official specification (2025-06-18 revision)

MCP servers that proxy with a static client_id also MUST obtain user consent for each dynamically registered client before forwarding the request to the third-party authorization server, even if that authorization server already requests additional consent on its own.

Checklist for Those Who Already Have an MCP Server in Production

For anyone who deployed an MCP server over HTTP without any of these layers, the path to compliance runs through seven concrete points:

  1. Confirm the transport: if it's pure STDIO, the spec recommends not following this flow and using credentials from the environment; if it's HTTP, compliance applies.
  2. Publish a Protected Resource Metadata endpoint (RFC9728) with the authorization_servers field populated.
  3. Respond with 401 and WWW-Authenticate pointing to that metadata whenever a valid token is missing.
  4. Validate the audience of every token received, rejecting any that wasn't issued specifically for that server.
  5. Ensure the server never forwards the MCP client's token to an upstream API without exchanging it for that API's own token.
  6. If the server also implements an MCP client, ensure support for PKCE and the resource parameter in every authorization and token request.
  7. Assess whether it's worth supporting Dynamic Client Registration (RFC7591) or whether a fixed client_id is enough, given the number of clients accessing the server.

The error codes the spec requires for each situation follow a simple pattern:

CodeSituation
401 UnauthorizedMissing authorization or invalid token
403 ForbiddenInsufficient scopes or permission denied
400 Bad RequestMalformed authorization request

Where It's Not Worth Implementing Everything

An MCP server that only runs locally via STDIO, consumed by a single trusted client on the same machine, doesn't need any of this: the spec itself recommends not following this flow in that case and pulling credentials from the environment (a variable like MCP_API_KEY, for example). The cost of implementing Protected Resource Metadata, authorization server discovery, and PKCE only pays off when the server is exposed over the network and accessed by clients you don't fully control.

For those in that middle ground, with an internal HTTP server and few known clients, the cheapest bet is usually to skip Dynamic Client Registration and fine-grained token audience at first, but implement Protected Resource Metadata and audience validation right away. These are the two points that, according to the spec itself, sustain the difference between an MCP server in compliance and one open to improper token reuse.

Translated from the Brazilian Portuguese original · Read the original

View profile →