Skip to content

fix: support path-suffixed well-known URLs for protected resource metadata (RFC 9728 §3.1) - #173

Merged
mattzcarey merged 2 commits into
cloudflare:mainfrom
mattzcarey:fix/resource-field-origin-only
Mar 29, 2026
Merged

mattzcarey merged 2 commits into
cloudflare:mainfrom
mattzcarey:fix/resource-field-origin-only

Conversation

@mattzcarey

Copy link
Copy Markdown
Member

Summary

The /.well-known/oauth-protected-resource endpoint currently only matches the exact path, which is correct for origin-level resources but does not support resources with path components (e.g., /mcp). This is non-compliant with RFC 9728 §3.1 and the MCP Authorization Spec (draft 2025-03-26).

The Problem

When a resource identifier contains a path (e.g., https://example.com/mcp), RFC 9728 §3.1 requires the well-known URI to be constructed by inserting the well-known prefix between the authority and path:

If the resource identifier value contains a path or query component, any terminating slash (/) following the host component MUST be removed before inserting /.well-known/ and the well-known URI path suffix between the host component and the path and/or query components.

So for resource https://example.com/mcp, the metadata URL is:

GET /.well-known/oauth-protected-resource/mcp HTTP/1.1
Host: example.com

RFC 9728 §3.3 then requires the resource value in the response to match:

The resource value returned MUST be identical to the protected resource's resource identifier value into which the well-known URI path suffix was inserted to create the URL used to retrieve the metadata. If these values are not identical, the data contained in the response MUST NOT be used.

The MCP spec explicitly requires this:

Well-Known URI: Serve metadata at a well-known URI as specified in RFC 9728. This can be either:

  • At the path of the server's MCP endpoint: https://example.com/.well-known/oauth-protected-resource/public/mcp
  • At the root: https://example.com/.well-known/oauth-protected-resource

The Fix

  • Route both exact (/.well-known/oauth-protected-resource) and path-prefixed (/.well-known/oauth-protected-resource/*) requests to the metadata handler
  • Derive the resource identifier by removing the well-known prefix from the request path (e.g., /.well-known/oauth-protected-resource/mcp → https://example.com/mcp)
  • Handle OPTIONS/CORS preflight for path-suffixed variants
  • Explicit resourceMetadata.resource overrides still take precedence

Test plan

  • Existing tests pass (297/297)
  • New test: path-based resource identifier returns correct resource field
  • New test: nested path-based resource identifier (/api/v1/mcp)
  • New test: OPTIONS preflight for path-suffixed URLs returns 204 with CORS
  • New test: custom resourceMetadata.resource overrides derived value

…adata (RFC 9728 §3.1)

The protected resource metadata endpoint only matched the exact path
`/.well-known/oauth-protected-resource`, which is correct for resources
at the origin but not for resources with path components like `/mcp`.

Per RFC 9728 §3.1, when a resource identifier contains a path (e.g.
`https://example.com/mcp`), the well-known URI is constructed by
inserting the well-known prefix between the authority and path:
`https://example.com/.well-known/oauth-protected-resource/mcp`

The `resource` field in the response must then equal the original
resource identifier per RFC 9728 §3.3.

This commit:
- Routes both exact and path-prefixed well-known URLs
- Derives the resource identifier by removing the well-known prefix
- Handles OPTIONS/CORS for path-suffixed variants
- Adds tests for path-based resource identifiers
@changeset-bot

changeset-bot Bot commented Mar 29, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d0ada69

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

pkg-pr-new Bot commented Mar 29, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

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

commit: d0ada69

@mattzcarey
mattzcarey merged commit 1fe656e into cloudflare:main Mar 29, 2026
4 checks passed
@github-actions github-actions Bot mentioned this pull request Mar 29, 2026
mattzcarey pushed a commit that referenced this pull request Mar 29, 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.3.2

### Patch Changes

- [#173](#173)
[`1fe656e`](1fe656e)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Support
path-suffixed well-known URLs for OAuth Protected Resource Metadata (RFC
9728 §3.1). Resources with path components (e.g.
`https://example.com/mcp`) now correctly serve metadata at
`/.well-known/oauth-protected-resource/mcp` and return the derived
resource identifier in the `resource` field.

- [#174](#174)
[`ac120ff`](ac120ff)
Thanks [@mattzcarey](https://github.com/mattzcarey)! - Include the
request path in the `resource_metadata` URL within `WWW-Authenticate`
headers (RFC 9728 §5.1). API endpoints with path components (e.g.
`/mcp`) now advertise the correct path-suffixed metadata URL so clients
can discover the resource-specific metadata.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mattzcarey added a commit to cloudflare/mcp-server-cloudflare that referenced this pull request Mar 29, 2026
Updates all apps and packages to workers-oauth-provider v0.3.2 which
includes RFC 9728 compliance fixes:

- fix: support path-suffixed well-known URLs for protected resource
  metadata (cloudflare/workers-oauth-provider#173)
- fix: include request path in WWW-Authenticate resource_metadata URL
  (cloudflare/workers-oauth-provider#174)

MCP servers with path-based endpoints (e.g. /mcp) now correctly serve
protected resource metadata at /.well-known/oauth-protected-resource/mcp
and advertise the correct resource_metadata URL in 401 responses.
mattzcarey added a commit to cloudflare/mcp that referenced this pull request Mar 29, 2026
Updates to workers-oauth-provider v0.3.2 which includes RFC 9728
compliance fixes:

- fix: support path-suffixed well-known URLs for protected resource
  metadata (cloudflare/workers-oauth-provider#173)
- fix: include request path in WWW-Authenticate resource_metadata URL
  (cloudflare/workers-oauth-provider#174)

MCP servers with path-based endpoints (e.g. /mcp) now correctly serve
protected resource metadata at /.well-known/oauth-protected-resource/mcp
and advertise the correct resource_metadata URL in 401 responses.
mattzcarey added a commit to cloudflare/mcp that referenced this pull request Mar 30, 2026
…90)

Updates to workers-oauth-provider v0.3.2 which includes RFC 9728
compliance fixes:

- fix: support path-suffixed well-known URLs for protected resource
  metadata (cloudflare/workers-oauth-provider#173)
- fix: include request path in WWW-Authenticate resource_metadata URL
  (cloudflare/workers-oauth-provider#174)

MCP servers with path-based endpoints (e.g. /mcp) now correctly serve
protected resource metadata at /.well-known/oauth-protected-resource/mcp
and advertise the correct resource_metadata URL in 401 responses.
mattzcarey added a commit to cloudflare/mcp-server-cloudflare that referenced this pull request Mar 30, 2026
)

Updates all apps and packages to workers-oauth-provider v0.3.2 which
includes RFC 9728 compliance fixes:

- fix: support path-suffixed well-known URLs for protected resource
  metadata (cloudflare/workers-oauth-provider#173)
- fix: include request path in WWW-Authenticate resource_metadata URL
  (cloudflare/workers-oauth-provider#174)

MCP servers with path-based endpoints (e.g. /mcp) now correctly serve
protected resource metadata at /.well-known/oauth-protected-resource/mcp
and advertise the correct resource_metadata URL in 401 responses.
threepointone added a commit that referenced this pull request Mar 31, 2026
…urce metadata (RFC 9728 §3.1) (#173)"

This reverts commit 1fe656e.
mattzcarey added a commit to mattzcarey/workers-oauth-provider that referenced this pull request Mar 31, 2026
…adata (RFC 9728 §3.1) (cloudflare#173)

## Summary

The `/.well-known/oauth-protected-resource` endpoint currently only
matches the exact path, which is correct for origin-level resources but
does not support resources with path components (e.g., `/mcp`). This is
non-compliant with [RFC 9728
§3.1](https://www.rfc-editor.org/rfc/rfc9728#section-3.1) and the [MCP
Authorization Spec (draft
2025-03-26)](https://modelcontextprotocol.io/specification/draft/basic/authorization).

### The Problem

When a resource identifier contains a path (e.g.,
`https://example.com/mcp`), RFC 9728 §3.1 requires the well-known URI to
be constructed by inserting the well-known prefix between the authority
and path:

> If the resource identifier value contains a path or query component,
any terminating slash (`/`) following the host component MUST be removed
before inserting `/.well-known/` and the well-known URI path suffix
between the host component and the path and/or query components.

So for resource `https://example.com/mcp`, the metadata URL is:
```
GET /.well-known/oauth-protected-resource/mcp HTTP/1.1
Host: example.com
```

RFC 9728 §3.3 then requires the `resource` value in the response to
match:

> The `resource` value returned MUST be identical to the protected
resource's resource identifier value into which the well-known URI path
suffix was inserted to create the URL used to retrieve the metadata. If
these values are not identical, the data contained in the response MUST
NOT be used.

The MCP spec explicitly requires this:

> **Well-Known URI**: Serve metadata at a well-known URI as specified in
RFC 9728. This can be either:
> - At the path of the server's MCP endpoint:
`https://example.com/.well-known/oauth-protected-resource/public/mcp`
> - At the root:
`https://example.com/.well-known/oauth-protected-resource`

### The Fix

- Route both exact (`/.well-known/oauth-protected-resource`) and
path-prefixed (`/.well-known/oauth-protected-resource/*`) requests to
the metadata handler
- Derive the `resource` identifier by removing the well-known prefix
from the request path (e.g., `/.well-known/oauth-protected-resource/mcp`
→ `https://example.com/mcp`)
- Handle OPTIONS/CORS preflight for path-suffixed variants
- Explicit `resourceMetadata.resource` overrides still take precedence

## Test plan

- [x] Existing tests pass (297/297)
- [x] New test: path-based resource identifier returns correct
`resource` field
- [x] New test: nested path-based resource identifier (`/api/v1/mcp`)
- [x] New test: OPTIONS preflight for path-suffixed URLs returns 204
with CORS
- [x] New test: custom `resourceMetadata.resource` overrides derived
value
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.

1 participant