Skip to content

fix: use exact match for root apiHandler route#140

Merged
mattzcarey merged 3 commits intocloudflare:mainfrom
mattzcarey:fix/root-api-handler-matching
Feb 25, 2026
Merged

fix: use exact match for root apiHandler route#140
mattzcarey merged 3 commits intocloudflare:mainfrom
mattzcarey:fix/root-api-handler-matching

Conversation

@mattzcarey
Copy link
Contributor

Summary

When apiHandler is set to '/', the library incorrectly matches all routes instead of just the root path, breaking OAuth endpoints like /authorize.

Problem

The matchApiRoute() method uses startsWith(route) to check if a URL matches the API handler route. When route is '/', every pathname starts with '/', so all requests are treated as API requests - including the default handler's OAuth UI pages.

This causes an infinite loop or auth failure because:

  1. User visits /authorize to see login UI
  2. Request matches '/' apiHandler (everything starts with /)
  3. Request goes to API handler instead of default handler
  4. Auth flow breaks

Solution

Add a special case for '/' to use exact pathname matching:

if (route === '/') {
  return url.pathname === '/';
}
return url.pathname.startsWith(route);

This makes '/' match only the root path, while other routes like /api continue to use prefix matching as expected.

Test plan

  • All existing tests pass (142 tests)
  • Formatting passes

Fixes #53

@changeset-bot
Copy link

changeset-bot bot commented Feb 23, 2026

🦋 Changeset detected

Latest commit: 4253d69

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

When apiHandler is set to '/', the previous startsWith check would match
all paths (since every path starts with '/'), incorrectly routing OAuth
endpoints like /authorize to the API handler instead of the default handler.

This change makes '/' use exact match, so only requests to the root path
are treated as API requests. Other paths like /authorize, /callback, etc.
will correctly fall through to the default handler.

Fixes cloudflare#53
@mattzcarey mattzcarey force-pushed the fix/root-api-handler-matching branch from 93ae67a to 2cb51e1 Compare February 23, 2026 17:58
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 23, 2026

Open in StackBlitz

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

commit: 4253d69

@mattzcarey mattzcarey merged commit 65d5cfa into cloudflare:main Feb 25, 2026
4 checks passed
@github-actions github-actions bot mentioned this pull request Feb 25, 2026
mattzcarey pushed a commit that referenced this pull request Feb 26, 2026
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.2.4

### Patch Changes

- [#136](#136)
[`a8c5936`](a8c5936)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Add
`/.well-known/oauth-protected-resource` endpoint (RFC 9728) for OAuth
2.0 Protected Resource Metadata discovery, as required by the MCP
authorization specification. The endpoint is always served with sensible
defaults (request origin as resource and authorization server), and can
be customized via the new `resourceMetadata` option.

- [#151](#151)
[`dbb150e`](dbb150e)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Add
`allowPlainPKCE` option to enforce S256-only PKCE as recommended by
OAuth 2.1. When set to false, the plain PKCE method is rejected and only
S256 is accepted. Defaults to true for backward compatibility.

- [#140](#140)
[`65d5cfa`](65d5cfa)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Fix apiHandler
route matching when set to '/' to use exact match instead of prefix
match, preventing it from matching all routes and breaking OAuth
endpoints

- [#150](#150)
[`734738c`](734738c)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Fix TypeScript
types by making OAuthProviderOptions generic over Env, eliminating the
need for @ts-expect-error workarounds when using typed environments

- [#145](#145)
[`6ce5c10`](6ce5c10)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Add RFC 8252
Section 7.3 compliance: allow any port for loopback redirect URIs
(127.x.x.x, ::1) to support native apps that use ephemeral ports

- [#143](#143)
[`8909060`](8909060)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Include
`resource_metadata` URL in `WWW-Authenticate` headers on 401 responses
per RFC 9728 §5.1, enabling clients to discover the protected resource
metadata endpoint directly from authentication challenges.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

FR: allow apiHandler route to be /

2 participants