Skip to content

OAuth Misconfig Patterns in MCP / Agent Integrations

As agent ecosystems and “Model Context Protocol” (MCP)-style integrations grow, OAuth is showing up in lots of places written by teams who aren’t OAuth specialists.

This page documents high-leverage misconfigurations that lead to one-click account takeover.

The dangerous combo

The most common catastrophic combo is:

  • Open Dynamic Client Registration (DCR)
  • Weak redirect URI validation
  • PKCE not mandatory (or not validated)

Result: attacker registers a client, sets a redirect URI they control, and hijacks the victim’s OAuth authorization.

What to look for

Dynamic Client Registration (DCR)

DCR endpoints are often exposed as: - /.well-known/openid-configuration - /oauth/register / /connect/register

Tests: - Can anyone create a client without authentication? - Can the attacker specify: - redirect URIs - grant types - response types - token auth methods

Redirect URI validation weaknesses

Common mistakes: - substring matching (evil.com contains vil.com) - allowing http:// in production - wildcard domains (*.example.com) without strict subdomain control - allowing arbitrary query params that enable secondary redirects

Tests: - Attempt redirect URIs like: - https://trusted.example.com.evil.com/callback - https://evil.com/?next=https://trusted.example.com/callback - https://trusted.example.com@evil.com/callback

PKCE

PKCE must be: - required for public clients - verified server-side (code_verifier → code_challenge)

Tests: - Can you complete a code exchange without PKCE? - Is PKCE accepted but not validated (always succeeds)?

Practical discovery tips

  • Look for MCP servers or agent gateways exposing OAuth metadata.
  • Check for “dev” deployments with permissive configs.
  • Look for documentation or SDK defaults that enable DCR.

What to recommend

  • Disable open DCR unless you explicitly need it.
  • If you need DCR, require authenticated registration + policy checks.
  • Enforce exact-match redirect URIs.
  • Require PKCE, validate state, and pin issuer/audience.

Also watch for: cross-client data leakage in MCP server deployments

Even with “correct” auth, MCP deployments can leak data between clients if the server/transport objects are shared incorrectly.

Misconfiguration pattern

  • A single shared McpServer/Server instance is reused across multiple concurrent client connections.
  • In stateless Streamable HTTP mode, this often looks like “create one server globally and call handleRequest() for every request”.

Why it’s dangerous

  • JSON-RPC message IDs can collide across clients.
  • Responses can be routed to the wrong client connection.
  • Result: Client A receives response data intended for Client B (cross-tenant data leak).

Recommendation

  • Stateless mode: create a fresh server + transport per request.
  • Stateful mode: create a fresh server + transport per session; do not share instances across sessions.
  • Upgrade the TypeScript SDK to a patched version that throws loudly on unsafe reuse.

Reference: - https://github.com/advisories/GHSA-345p-7cg4-v4c7

October 1 follow-up: Authlib discovery-metadata cache — the OIDC trust bootstrap is a fetch, not a constant

Authlib ≤1.7.2 (GHSA-8wx9-759m-hfm8): OpenID discovery JSON metadata is cached without validation or issuer-origin binding, so a poisoned discovery response replaces all endpoint values with attacker-controlled ones — not just endpoint URLs sharing the configured server metadata origin.

Durable axes for any OAuth/OIDC integration test (extends this page's open-DCR/redirect/PKCE combo): 1. The discovery document is attacker-reachable trust material. Every derived value (authorization_endpoint, token_endpoint, jwks_uri, issuer) is data from a network fetch. Test integrations for: does the client verify the returned issuer string matches the configured one exactly, post-cache? Does it re-anchor after cache refresh? A poisoned or MITM'd /.well-known/openid-configuration response that swaps jwks_uri silently converts the integration into verifying tokens against your keys. 2. Black-box probes: configure an integration against an owned IdP; mutate the discovery document fields (different-origin jwks_uri, altered issuer) and observe whether the relying party accepts the substitution or rejects on origin/issuer mismatch. Cache-behavior check: change the IdP's discovery content, force a refresh (restart or TTL expiry), and record which values win — stale-cache-vs-live-config drift is this class's timing twin. 3. Report shape: "server metadata is fetched material; the RP must enforce issuer equality and endpoint-origin binding after every fetch/cache load" — that sentence is the finding, with your substitution decision table as evidence.

Proof stays on owned IdPs/RPs; never intercept a real third party's discovery traffic.

Source / inspiration

  • Inspired by research on OAuth misconfigurations in MCP-like systems (open DCR + weak redirect + missing PKCE → one-click ATO).