MCP OAuth Follows a Spec—Yet All 119 Tested Servers Had Flaws

A 2026 measurement study found at least one OAuth flaw in every one of 119 testable remote MCP servers, even though MCP defines authorization requirements. The remote-MCP authentication study counted 325 confirmed flaw instances; 115 servers, or 96.6%, had at least one dynamic-client-registration flaw. The result applies to the researchers’ DCR-enabled testable subset, not to every MCP deployment.
Defending a remote service therefore means testing the bindings that OAuth is supposed to preserve: token to resource, response and credentials to authorization-server issuer, client to registered callback, code to PKCE verifier, consent to the requesting client, and every artifact to one transaction. A successful login proves only that the happy path works.
Turn the findings into a control map
The study classified nine flaws: malicious DCR binding, blind client trust, inconsistent protection across delegated layers, mutable routing context inside state, PKCE downgrade, consent-page bypass, open redirect, weak state and authorization-code replay. These are not nine unrelated bugs. Most arise because one component accepts an identifier or artifact without checking what it was originally bound to.
Draw one complete authorization path before testing: MCP client, protected MCP resource, authorization server, browser callback and any upstream provider. For each client_id, redirect_uri, issuer, state value, code challenge, authorization code and access token, identify the component that creates it, the component that validates it and the stored value used for comparison. An undocumented validation owner is an audit gap.
Bind tokens and responses to the intended parties

The MCP authorization security requirements require clients to include the resource parameter and servers to accept only tokens issued specifically for themselves. Before tool execution or data return, validate the token’s signature, issuer, expiry, intended audience and required scopes. A correctly signed token for a sibling service or upstream API must still fail.
Keep delegated OAuth roles separate. When an MCP server calls an upstream API, it must obtain a distinct token for that API rather than passing through the token received from the MCP client. Store MCP-facing and upstream tokens separately, and test that substituting either token at the other boundary is rejected.
Clients also need authorization-server binding. Record the selected issuer before opening the browser, validate the issuer returned in the authorization response and redeem the code only at that issuer’s token endpoint. Keep client credentials scoped to the issuer that created them; PKCE does not stop a client from sending its verifier to the wrong token endpoint.
- Present a valid token whose audience identifies another resource; expect rejection before tool dispatch.
- Change the response issuer or selected token endpoint; expect the client to stop before redemption.
- Attempt to use an upstream token at the MCP endpoint and an MCP token upstream; expect both requests to fail.
Audit registration, redirects and consent together

Open registration can turn a malicious callback into a formally registered one. The official 2026-07-28 specification release notes say Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents, while remaining available for backward compatibility. Prefer metadata documents or pre-registration; retain DCR only when open onboarding is an actual requirement.
If DCR remains enabled, submit an anonymous registration with an external callback you control in an authorized test environment. The server should enforce its redirect policy, reject misleading or unverified client identities, rate-limit registration and expire unused records. During authorization, replace the registered client_id with an unknown value; a familiar name on the consent page must never substitute for registration validation.
Validate redirect_uri by exact comparison with the registered value. Test a changed scheme or host, user-info syntax, encoded characters, added query parameters and unexpected subpaths. Apply the same rule at the authorization endpoint and at any callback handler involved in delegated authorization.
Consent must show enough information to distinguish the requesting client and destination, including the exact redirect hostname and requested scopes. Give localhost callbacks additional scrutiny because client metadata cannot identify which local process owns a port. For a proxy that uses one shared upstream client identity, require MCP-level consent for each requesting client; an existing upstream consent session must not silently authorize a new MCP client.
Enforce PKCE, state and single use at every hop
Require a code_challenge for public clients, advertise supported methods in authorization-server metadata and allow S256 rather than plain. At the token endpoint, bind the authorization code to its original client, redirect URI and challenge; compare the verifier, then invalidate the code atomically when redemption succeeds.
Repeat PKCE checks across delegated authorization. Protection between the MCP client and server does not repair an unprotected flow between that server and an upstream provider. Test each hop independently by removing PKCE parameters and replacing S256 with plain; both mutations should fail.
Use an unpredictable, single-use state value bound server-side to the browser session, issuer and intended callback. Do not embed a mutable downstream redirect in state. If routing context must cross the browser, protect its integrity and validate the decoded destination against the original transaction.
- Omit state, replace it and replay it after successful use; every callback should fail closed.
- Redeem the same authorization code twice; the second exchange must issue no token.
- Modify any redirect information nested in delegated context; the callback must not change destination.
Finish with an evidence-producing audit

Run mutation tests only against systems you own or are authorized to assess. A broader pre-connection verification process can cover provenance and tool permissions; this review stays focused on the remote authorization boundary.
- Inventory every protected resource, issuer, authorization endpoint, token endpoint, client identity and registered callback.
- Test invalid audiences, issuers, client IDs, redirects, scopes, PKCE parameters and state values.
- Test registration with an untrusted callback and authorization with an unregistered client identifier.
- Review consent for a new client, a previously authorized user and a localhost callback.
- Replay codes and state values, and confirm that failed checks return no protected data or tool result.
- Repeat the relevant tests at every delegated OAuth hop.
Logs should show that each control ran without retaining credentials. Record a correlation ID, time, resource identifier, issuer, client identifier, normalized callback identifier, requested scopes, outcome and stable reason code. Exclude access and refresh tokens, authorization codes, PKCE verifiers, raw state values and session cookies.
Alert on repeated wrong-audience tokens, unknown issuers, rejected callbacks, registration bursts, PKCE downgrades, state mismatches and code replays. The completion criterion is a reproducible set of negative tests that stop invalid requests before tool execution and leave useful, redacted evidence.
Also read:
Subscribe to our newsletter
Get the latest Web3, AI, and crypto news delivered straight to your inbox.