RC RANDOM CHAOS

Zero-Touch OAuth strips the 2025-06-18 MCP mandate

Zero-Touch OAuth for MCP fails as a trust-on-first-use design: unauthenticated dynamic client registration and unbound bearer tokens enable session hijack.

· 7 min read

Zero-Touch OAuth for MCP removes the human from client provisioning. An MCP client registers itself against an authorization server through RFC 7591 dynamic client registration, receives credentials, completes the authorization code flow, and holds a bearer token. No operator vets the client. No admin approves the grant. The convenience is the defect. There is no CVE here, no CVSS vector, no patch boundary. This is a design pattern that violates the trust assumptions OAuth 2.1 was written to enforce, and it fails the way every trust-on-first-use system fails at the first contact with someone hostile.

The Model Context Protocol authorization specification mandates OAuth 2.1, PKCE for every client, and - in the 2025-06-18 revision - Resource Indicators under RFC 8707. Zero-Touch deployments skip the parts that introduce friction. Dynamic registration goes unauthenticated. PKCE enforcement becomes optional. Resource indicators get omitted. Tokens issue as plain bearer credentials with no sender constraint. Each omission is individually defensible. Together they reconstruct the exact conditions OAuth 2.1 deprecated.

The bug class is two CWEs operating in series. CWE-862, missing authorization, at the registration boundary. CWE-384 and CWE-613, session fixation and insufficient session expiration, at the token boundary. Neither is novel. Both are predictable.

Mechanism, registration first. The dynamic client registration endpoint accepts client metadata: client name, redirect URIs, grant types, and the token endpoint auth method. In a Zero-Touch deployment the endpoint is reachable without an initial access token. The authorization server stores whatever metadata the registrant submits and treats it as authoritative for the lifetime of the client. The trust boundary is the registration request itself. The attacker controls both sides of it. A registrant supplies redirect_uris, and absent strict exact-match validation the server honours them. This is the open-redirect primitive that turns an authorization code flow into a code-capture flow.

PKCE exists to defeat exactly that interception. The S256 challenge binds the authorization code to the client that initiated the flow, so a captured code is useless without the verifier. PKCE only protects what it is enforced on. Zero-Touch clients registered as public clients with PKCE marked optional give the server no reason to reject a code redemption that omits the verifier. The mitigation is present in the spec and absent in the deployment.

Then the token. The access token issues as a bearer token. Bearer means possession is authorization. The token carries no binding to the client that requested it - no DPoP key thumbprint under RFC 9449, no mutual-TLS certificate under RFC 8705. A bearer token presented from any IP, any ASN, any process, any host is a valid token. That is the session hijack primitive stated plainly. Theft equals use.

Resource indicators are the last omission. RFC 8707 scopes a token to the specific resource server it was minted for. Without it, a token issued for one MCP server is replayable against any other resource server that trusts the same authorization server. This is the confused deputy condition. An MCP server acting as an OAuth client, holding tokens on behalf of users, becomes a relay. Present a token to it and it forwards the user’s authority downstream. Token passthrough, the anti-pattern the MCP spec explicitly names, is the same failure from the server side.

The architecture amplifies it. In the MCP authorization model the server is simultaneously a resource server to the client and an OAuth client to upstream APIs. It holds delegated authority - a Google token, a GitHub token, a database credential - and exposes tool calls that exercise that authority. A token accepted at the MCP server boundary does not stop at the server. It commands every downstream credential the server brokers. The blast radius of one hijacked MCP session is the union of every API the server is authorised against, not the single scope the user believed they granted.

The exploit path does not require breaking cryptography. It requires arriving where the trust is assumed. The attacker registers a client against the open DCR endpoint. Registration is unauthenticated, so the client is whatever the attacker declares it to be, including a redirect URI under attacker control or a client name that mirrors a trusted integration. The authorization step is then driven through consent - MITRE T1528, steal application access token, frequently delivered as consent phishing under T1566. The user authorises a client they believe is legitimate. The code returns to the attacker’s redirect. Without enforced PKCE the code redeems. A token exists, and it is the attacker’s.

The second path skips the user entirely. MCP clients persist tokens. They store them in config files, environment variables, or process memory on the host running the client. Plaintext at rest is common. A host with any code execution yields the token - T1552.001, credentials in files. The token is bearer, so the attacker replays it directly. T1550.001, application access token. No password, no second factor, no re-authentication. The work the legitimate user did at authorization is the work the attacker inherits.

Persistence follows from the same weak session management. Long-lived access tokens, refresh tokens that do not rotate, no binding to force re-auth. The session does not expire on a meaningful schedule and cannot be distinguished from a legitimate one. The attacker holds it as long as the token lives and refreshes it when it does not. This maps to T1078.004, valid accounts in cloud, and T1199, trusted relationship, because the MCP server is the trusted third party in the chain.

The pattern is shipping at speed. Cloudflare published an OAuth provider library for remote MCP servers, putting dynamic registration and the authorization code flow one dependency away from any Worker. Okta and other identity providers now front MCP authorization for enterprise deployments. The plumbing is correct. The deployments that strip the friction controls are where the condition lives, and those are the common ones because Zero-Touch is the selling point.

Because there is no CVE, there is no advisory to drive remediation and no scanner signature to flag the condition. Vulnerability management keys on identifiers. A pattern has none. The exposure persists in deployments that pass every scan, because nothing being scanned is wrong. Every component behaves to specification. The specification is being used to assemble an insecure whole.

Telemetry is where this gets uncomfortable. The authorization server emits a token issuance event on every grant. In Okta System Log terms, app.oauth2.as.token.grant and the client registration events around it. Dynamic client registration is a high-signal event. A new OAuth client appearing without a change ticket is anomalous by definition. It is also routinely unmonitored, because most detection content was written for human logins and not for machine client provisioning.

What does not fire is the replay. A bearer token presented from a new IP generates no new authentication event, because no authentication occurs - the token is simply accepted. There is no failed login, no MFA challenge, no step-up. The EDR on the MCP client host sees the client process opening outbound TLS to a resource server, which is the process doing exactly what it is supposed to do. Nothing in the host telemetry distinguishes the legitimate token holder from the thief. The signal defenders correlate on - authentication anomalies - does not exist for token replay.

On the client host the observable surface is thin. Sysmon Event ID 1 records the MCP client process launch, Event ID 3 records the outbound network connection to the resource server, and Event ID 11 records the token written to a config file where file-creation logging covers the path. None of that is exploitation. It is the application installing and running. Token theft through a sibling process reading the config surfaces as a file read against a path most baselines do not know to watch.

The detection has to move to the token layer. Geovelocity-impossible token use, the same token presented from two ASNs inside a window travel cannot explain. Client registrations with redirect URIs that do not match known infrastructure. Token grant volume from a single client that exceeds its baseline. Refresh patterns that indicate automated retention rather than interactive use. None of these are default content in a SIEM. All of them require treating OAuth token events as a first-class log source, which most deployments do not.

There is no patch, because there is no defect in a single artefact. The controls that break the chain are the ones Zero-Touch removes for convenience. Enforce PKCE with S256 and reject any redemption without a verifier. Require exact-match redirect URI comparison, never substring or prefix. Gate dynamic client registration behind a signed software statement or an initial access token under RFC 7591, so registration is not anonymous. Scope every token with RFC 8707 resource indicators so a token for one server is rejected by another. Constrain tokens to a sender with DPoP or mutual-TLS so possession alone is insufficient. Keep access token TTL short and rotate refresh tokens.

Residual exposure remains after all of it. The MCP client still stores a token on a host, and a compromised host still surrenders that token. The difference is what the stolen token is worth. A bearer token is a complete session. A sender-constrained token is inert without the private key that bound it, which forces the attacker to steal the key material and operate from the compromised host rather than replaying from anywhere. That does not eliminate the risk. It moves the boundary from possession to key compromise, which is the boundary OAuth 2.1 intended in the first place. Zero-Touch, deployed without these controls, operates on the wrong side of it.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.