The Model Context Protocol (MCP) specification standardizes remote agent access on OAuth 2.1, requiring PKCE, dynamic client registration, and RFC 9728 metadata for every connection. In theory, this gives platform engineers a clear security baseline for autonomous AI agents.
In practice, the infrastructure ecosystem has not caught up. According to Obsidian Security, only 4 percent of authorization servers support dynamic client registration, pushing teams toward proxy workarounds that introduce account takeover vulnerabilities. Even where the OAuth flow works correctly, the token itself only proves identity. It cannot constrain what an authenticated agent does with its access.
TL;DR
-
Only 4 percent of standard authorization servers support the dynamic client registration required by initial specifications, forcing a transition toward static metadata files.
-
OAuth proxies that bypass infrastructure constraints force multiple clients to share a static Client ID, enabling account takeover vulnerabilities demonstrated in responsible disclosures.
-
Standard token exchanges consistently break due to basic naming mismatches between local host environments and numerical validation rules.
-
OAuth scopes define what an agent can theoretically do but cannot evaluate the dynamic, resource-level boundaries required to stop tool poisoning attacks.
-
OAuth secures the transport layer but leaves a gap between token validation and resource-level access control that current MCP implementations do not address.
The reality of the MCP OAuth 2.1 specification
If you try to implement the baseline MCP instructions today, you will immediately hit a wall. According to the Official MCP spec, the protocol requires Proof Key for Code Exchange (PKCE) and mandates the S256 challenge method for all capable clients. Under the officially released framework, the architecture defines the MCP server as the resource server, the MCP app as the OAuth client, and requires a separate or co-hosted authorization server to issue tokens.
Also, establishing baseline functions of the Model Context Protocol requires explicit use of RFC 9728 for protected resource metadata and RFC 8414 for authorization server metadata. The mandate relies on an evolving foundation, given that OAuth 2.1 was still an IETF Internet-Draft (draft-ietf-oauth-v2-1-15) as of March 2, 2026. Because standard authorization servers almost universally reject dynamic client registration, developers hit technical roadblocks instantly.
In a recent test of 660 standard authorization endpoints, Obsidian Security reported that only 4 percent supported dynamic client registration in practice. Because the industry cannot support the requirement, the official MCP draft specification actively shifts toward Client ID Metadata Documents (CIMD). The new pattern provides a safer static metadata file to prove client provenance, mitigating the severe client impersonation risks associated with dynamic registration.
Implementation traps in standard client exchanges
Bypassing the macro infrastructure hurdles of dynamic registration only reveals deeper micro-validation errors. Rigid validation rules routinely break MCP token exchanges due to fundamental credential and naming mismatches. For example, as documented by Upstash, developers frequently register redirect URIs using the string "localhost" but send "127.0.0.1" during the actual token request. OAuth 2.1 requires precise programmatic string matching for all redirect URIs, causing the minor discrepancy to trigger a fatal validation failure before the agent even establishes a connection.
Upstash research shows teams also commonly send a client_secret_basic token endpoint authentication method to register as confidential clients, despite functioning as public applications. Because public clients cannot securely hold basic secrets, the authorization server rejects the mismatch outright. The Official MCP spec also mandates that clients include a resource parameter in authorization and token requests, using the canonical URI of the MCP server to ensure tokens remain bound to a specific server.
The Official MCP spec mandates refresh-token rotation for public clients, aligning with the recommendations in RFC 9700. Creating an architecture that continually swaps these tokens adds another layer of state management complexity directly to the development cycle.
How proxy workarounds invite account takeovers
Frustrated by standard integration failures, engineering teams routinely reach for serverless tools to bridge the gap between their disconnected components. Deploying these proxy workarounds introduces serious vulnerabilities. Obsidian Security identifies how standard proxy setups handle the dynamic onboarding process by forcing thousands of discrete MCP clients to share one static Client ID. The architectural choice strips individual client identities from the downstream authorization server, masking the true origin of incoming requests.
Single points of failure emerge rapidly when proxies fail to bind the external state parameter to individual user sessions. Obsidian Security demonstrated how one-click account takeovers could exploit platforms via subdomain takeover and malicious cookie injection into the session state, using Wix's MCP server as an illustrative example. In a separate responsible disclosure from the same research, a consent bypass on Square's MCP server (mcp.squareup.com) could grant access to merchant profiles and private transaction histories through a shared static client_id.
Resolving a Cross-Site Request Forgery vulnerability secures the pathway, yet does nothing to constrain the agent carrying the token.
The danger of trusting OAuth scopes for access control
Standard API integrations have operated on scope-based delegated access for over a decade, leading developers to assume OAuth scopes deliver solid internal security for AI deployments. The prevailing mental model suggests that a successfully verified token provides safe access for any autonomous agent interacting with the system.
Scopes provide basic plumbing for delegated access. A token might carry a generalized permission to read documents or execute a specific calendar tool, but it cannot evaluate which resource among millions an agent has the right to access on behalf of a user. Authentication and authorization have distinct responsibilities. Security breaks when developers merge the two.
Applying broad scopes to large enterprise datasets leads to data over-fetching. A compromised agent could execute a search tool that retrieves confidential HR records simply because its token carries generic read access across the organization. Mapping broad scopes directly to resources is a common architectural error concerning agent authorization today. Standard OAuth 2.1 handles the transport verification alone, leaving internal boundaries undefined.
Securing the gap between authentication and execution
Because standard protocol handles transport alone, you need firm boundary conditions downstream before allowing any tool execution. Building a resilient setup requires separating the token exchange from the granular engine that evaluates subsequent permissions. Current implementations regularly fail to build the necessary separation. A study of 1,899 open-source MCP servers revealed that 7.2 percent contain known vulnerabilities, with 5.5 percent directly exposed to tool poisoning attacks. Authenticated agents manipulate tool inputs to access resources far beyond their intended scope.
To survive these attacks, fine-grained decisions must operate purely downstream of the transport gateway. The agent presents a valid token, and before any tool fires, a separate authorization layer evaluates whether that specific user has permission to access that specific resource. This is where authentication and authorization diverge: the token proves identity, but the authorization layer determines what that identity is allowed to touch.
Relationship-based access control (ReBAC) fits this gap because it models permissions as a graph of relationships between users, roles, and resources rather than flat scope strings. When an agent invokes a file-search tool, the authorization layer checks whether the authenticated user has a relationship path to each returned document before the agent processes it. The check runs per-resource and per-action, so even a compromised agent cannot over-fetch data outside its authorized boundary.
Google's internal authorization system, Zanzibar, pioneered this model for managing permissions across billions of users and resources. AuthZed implements it as an infrastructure layer, evaluating permissions at each tool invocation so authorization checks do not bottleneck real-time agent coordination.
Where authentication ends and authorization begins
A flawless OAuth deployment delivers access tokens securely to agents, but the token itself cannot constrain what those agents do inside the application boundary. Tool poisoning and data over-fetching succeed because the transport layer has no opinion about resource-level access.
The architectures that survive these attacks enforce boundaries at every tool invocation, not just at the front gate. A relationship-based authorization layer evaluates each request against a graph of permissions, so the same system that blocks unauthorized access also produces the audit trail proving it. Standardizing identity is the prerequisite for security, not the end of it. Real protection starts after the token clears the gate.
FAQs
What is the difference between dynamic client registration and metadata documents in MCP?
Dynamic client registration requires clients to register automatically with the authorization server, while Client ID metadata documents provide a static metadata file to prove client provenance. The specification draft shifts away from dynamic registration because it introduces client impersonation risks in remote environments.
Why does my MCP token exchange fail with a URI validation error?
OAuth 2.1 requires precise string matching for all redirect URIs during the token exchange. Developers commonly register their application using a string like "localhost" but send "127.0.0.1" in the payload, causing the authorization server to reject the request outright.
Can I use OAuth 2.0 implicit flows for MCP servers?
Legacy implicit flows fail the security requirement because the official specification prohibits them. The protocol mandates OAuth 2.1 mechanisms and requires Proof Key for Code Exchange for all remote clients to prevent interception attacks.
Why do OAuth proxies cause account takeovers?
Proxy setups frequently force multiple clients to share a single static identity, which prevents the authorization server from binding the state parameter reliably to specific user sessions. Security researchers have demonstrated how attackers could exploit the resulting state mismatch by injecting malicious cookies, as shown in responsible disclosures involving platforms like Square and Wix.
Do OAuth scopes replace the need for a permissions system?
Scopes provide only high-level delegated access plumbing and lack the capacity to enforce resource-level permissions natively. A scope grants an agent the general ability to read files, but a dedicated permissions system remains necessary to evaluate which specific files that user has access to.