8000 fix: open redirect vulnerability in completeAuthorization by roerohan · Pull Request #92 · cloudflare/workers-oauth-provider · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@roerohan
Copy link
Contributor

Summary of Fix

This change addresses an open redirect vulnerability in the completeAuthorization method.

The Vulnerability

The completeAuthorization method previously trusted the redirect_uri provided within the AuthRequest object without re-validating it. An application using this library could inadvertently pass an AuthRequest object constructed from untrusted data (e.g., a state parameter from the URL). We were trusting the implementor of the library to ensure that a validated redirect_uri is passed in completeAuthorization.

For example, if the implementor stores the redirect_uri in the "?code=...&state=..." URL string, it gives an attacker a vector to modify the redirect_uri. An attacker can craft a malicious authorization link with a modified state parameter containing an attacker-controlled redirect_uri. If a victim authenticated through this link, the authorization code would be sent to the attacker's server, leading to a potential account takeover.

The Solution

This fix adds a validation block at the beginning of the completeAuthorization method. Before any grant is created, the method now:

  1. Looks up the client using the clientId from the request.
  2. Verifies that the redirectUri from the request is present in the client's list of registered redirectUris.

This change ensures that the redirect_uri is always validated before use, making the library secure by default and protecting all downstream applications from this vulnerability.

@changeset-bot
Copy link
changeset-bot bot commented Oct 16, 2025

🦋 Changeset detected

Latest commit: b845bf0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/workers-oauth-provider Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
pkg-pr-new bot commented Oct 16, 2025

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/workers-oauth-provider/@cloudflare/workers-oauth-provider@92

commit: b845bf0

@kentonv
Copy link
Member
kentonv commented Oct 17, 2025

This validation is reasonable to do as a defensive measure, but note that this doesn't really solve any "open redirect" issue because an attacker can just use dynamic client registration to register a client with whatever redirect_uri they want.

Moreover, it should not be possible for an attacker to inject a bogus request_uri between parseAuthRequest() and completeAuthorization(). It is the responsibility of the app not to pass the parsed request out of its custody such that it could be modified or injected by an attacker between these two steps.

@roerohan
Copy link
Contributor Author
roerohan commented Oct 19, 2025

an attacker can just use dynamic client registration to register a client with whatever redirect_uri they want

This is true. If the authorization server allows open, unauthenticated Dynamic Client Registration, then an attacker doesn't need to exploit a flaw in an existing client; they can just create their own malicious client.

However, I believe this is a separate and distinct vulnerability. The threat is not an open redirect, but rather an "unvalidated client registration" flow. I'm happy to discuss how this can be fixed and make relevant changes in my PR.

It is the responsibility of the app not to pass the parsed request out of its custody...

The fix in this PR is still necessary because it protects against an attacker hijacking the session of a legitimate, already-registered client. The two fixes address two different attack vectors.

In a perfect world, the application developer would handle the AuthRequest object securely. They would use the state parameter only for a non-guessable CSRF token and would never expose the AuthRequest object to tampering.

I've commonly seen the following implementation where the application serializes the entire AuthRequest object into the state parameter, passes it through the user's browser, and then trusts it on return. This is a common and foreseeable mistake.

const res = await getAuthorizationURL({
	client_id: c.env.CLIENT_ID,
	redirect_uri: new URL('/oauth/callback', c.req.url).href,
	state: oauthReqInfo,
	scopes,
})

Then, it trusts the state received in the /callback route to re-create the oauthReqInfo object.

const oauthReqInfo = AuthRequestSchemaWithExtraParams.parse(JSON.parse(atob(state)))
// Get the oathReqInfo out of KV
if (!oauthReqInfo.clientId) {
	throw new McpError('Invalid State', 400)
}

// Return back to the MCP client a new token
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
	request: oauthReqInfo,
	userId: user.id,
	metadata: {
		label: user.email,
	},
	scope: oauthReqInfo.scope,
	props: {
		type: 'user_token',
		user,
		accounts,
		accessToken,
		refreshToken,
	} satisfies AuthProps,
})

The old API assumed the developer would do the right thing. If they made a mistake (like misusing the state parameter), the application became vulnerable. The new API is defensive. It does not trust its caller. By re-validating the redirect_uri inside completeAuthorization(), the library protects the developer from this common mistake. Even if the application code is flawed, the library enforces a critical security check and prevents the vulnerability. Like you said, it's a defensive measure that makes the library secure by default.

@mschwarzl
Copy link

Hi, as you both correctly discussed as long as you can arbitrarily register a client, the mitigation won't help against the phishing issue. That's why I was also asking to do csrf checking on the application/client side as well.

Preventing these kind of phishing attacks where a victim just copy/pastes an URL and it directly causes an action in the logged in env. I agree that the suggested fix would still improve the current situation.

Moreover, I'd like to see some recommendations / sample client usage that users/customers could refer to.

@threepointone threepointone merged commit 5a59d78 into cloudflare:main Oct 20, 2025
4 checks passed
@threepointone threepointone mentioned this pull request Oct 20, 2025
threepointone added a commit that referenced this pull request Oct 20, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @cloudflare/workers-oauth-provider@0.0.12

### Patch Changes

- [#92](#92)
[`5a59d78`](5a59d78)
Thanks [@roerohan](https://github.com/roerohan)! - fix: open redirect
vulnerability in completeAuthorization

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@roerohan roerohan deleted the fix-open-redirect branch October 20, 2025 17:15
@roerohan
Copy link
Contributor Author

Hi, as you both correctly discussed as long as you can arbitrarily register a client, the mitigation won't help against the phishing issue.

#96 adds a helper method to allow users of the library to validate a client before it gets registered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

0