OAuth Protected Resource Metadata: authorization_servers contains endpoint path instead of issuer URL #2417

Closed
opened 2026-03-13 09:52:36 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @mattzcarey on GitHub (Nov 28, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

MCP servers using better-auth are returning incorrect values in the OAuth Protected Resource Metadata (/.well-known/oauth-protected-resource), which breaks OAuth discovery for MCP Clients following RFC 9470.

The authorization_servers field contains an endpoint path (e.g., https://example.com/api/auth) instead of the authorization server issuer identifier (e.g., https://example.com).

Example response from a better-auth powered server:

{
  "resource": "https://greentea.fiberplane.io",
  "authorization_servers": ["https://greentea.fiberplane.io/api/auth"]
}

Per RFC 9470 Section 2, authorization_servers should contain authorization server issuer identifiers - the URLs where /.well-known/oauth-authorization-server metadata can be discovered.

This breaks all MCP Servers using Better-Auth with MCP Typescript SDK 1.23.0 and probably others.

Workaround

We are looking to maybe add a fallback to the MCP TypeScript SDK that tries root-level discovery when path-based discovery fails: https://github.com/modelcontextprotocol/typescript-sdk/pull/1192

However we really dont want to do this because its not spec compliant and it might break some other stuff. Fixing the authorization_servers value in better-auth would be the correct solution.

References

Current vs. Expected behavior

When a client receives authorization_servers: ["https://example.com/api/auth"], it attempts OAuth metadata discovery at:

  • https://example.com/.well-known/oauth-authorization-server/api/auth → 404
  • https://example.com/.well-known/openid-configuration/api/auth → 404

The correct discovery URL should be:

  • https://example.com/.well-known/oauth-authorization-server → 200

So it should be changed to:

authorization_servers: ["https://example.com"]

What version of Better Auth are you using?

not sure, reported by user

System info

n/a

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Originally created by @mattzcarey on GitHub (Nov 28, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce MCP servers using better-auth are returning incorrect values in the OAuth Protected Resource Metadata (`/.well-known/oauth-protected-resource`), which breaks OAuth discovery for MCP Clients following RFC 9470. The `authorization_servers` field contains an endpoint path (e.g., `https://example.com/api/auth`) instead of the authorization server issuer identifier (e.g., `https://example.com`). Example response from a better-auth powered server: ```json { "resource": "https://greentea.fiberplane.io", "authorization_servers": ["https://greentea.fiberplane.io/api/auth"] } ``` Per [RFC 9470 Section 2](https://datatracker.ietf.org/doc/html/rfc9470#section-2), `authorization_servers` should contain **authorization server issuer identifiers** - the URLs where `/.well-known/oauth-authorization-server` metadata can be discovered. This breaks all MCP Servers using Better-Auth with MCP Typescript SDK 1.23.0 and probably others. **Workaround** We are looking to maybe add a fallback to the MCP TypeScript SDK that tries root-level discovery when path-based discovery fails: https://github.com/modelcontextprotocol/typescript-sdk/pull/1192 However we really dont want to do this because its not spec compliant and it might break some other stuff. Fixing the `authorization_servers` value in better-auth would be the correct solution. **References** - RFC 9470: https://datatracker.ietf.org/doc/html/rfc9470 - MCP SDK PR: https://github.com/modelcontextprotocol/typescript-sdk/pull/1192 - Original issue: https://github.com/cloudflare/agents/issues/686 ### Current vs. Expected behavior When a client receives `authorization_servers: ["https://example.com/api/auth"]`, it attempts OAuth metadata discovery at: - `https://example.com/.well-known/oauth-authorization-server/api/auth` → 404 - `https://example.com/.well-known/openid-configuration/api/auth` → 404 The correct discovery URL should be: - `https://example.com/.well-known/oauth-authorization-server` → 200 So it should be changed to: `authorization_servers: ["https://example.com"]` ### What version of Better Auth are you using? not sure, reported by user ### System info ```bash n/a ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 09:52:36 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 28, 2025):

Hi there, thanks for reporting this issue. It looks like the metadata endpoint is currently returning an endpoint path instead of the expected issuer URL per the OAuth discovery guidelines outlined in RFC 9470 [1]. To resolve this, you might try the following:

• Confirm that your installation isn’t applying any custom path modifications that might be affecting the value of authorization_servers.
• Verify your configuration and consider manually overriding the value if an immediate fix is needed until we release an update.
• We’re reviewing this behavior on our end to ensure that future releases return the issuer identifier (e.g., “https://example.com”) rather than the full endpoint path.

Could you share any additional details about your environment or provide a sample configuration? Tag @better-auth-agent if you would like an answer from the Agent.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Nov 28, 2025): Hi there, thanks for reporting this issue. It looks like the metadata endpoint is currently returning an endpoint path instead of the expected issuer URL per the OAuth discovery guidelines outlined in RFC 9470 [[1]](https://github.com/better-auth/better-auth/issues/4125). To resolve this, you might try the following: • Confirm that your installation isn’t applying any custom path modifications that might be affecting the value of authorization_servers. • Verify your configuration and consider manually overriding the value if an immediate fix is needed until we release an update. • We’re reviewing this behavior on our end to ensure that future releases return the issuer identifier (e.g., “https://example.com”) rather than the full endpoint path. Could you share any additional details about your environment or provide a sample configuration? Tag @better-auth-agent if you would like an answer from the Agent. <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Nov 28, 2025):

Hi @mattzcarey! I'm Dosu and I’m helping the better-auth team.

This is a confirmed bug: per RFC 9470 Section 2, the authorization_servers field in the OAuth Protected Resource Metadata must contain the issuer URL (e.g., https://example.com), not an endpoint path like https://example.com/api/auth. Returning a path breaks OAuth discovery because clients will look for metadata at incorrect URLs and fail with 404s.

In better-auth, this happens because the MCP plugin sets authorization_servers to the computed baseURL, which often includes the API path segment. The relevant code is in packages/better-auth/src/plugins/mcp/index.ts, where authorization_servers: [baseURL] is used, and baseURL is constructed with a default path if not present. This is discussed in issue #2703 and issue #4540.

There is currently no built-in config option to override this behavior. Fixing it would require either using the issuer origin for authorization_servers or adding a config option to allow developers to set it explicitly. Until then, the SDK workaround you mentioned is necessary for compatibility.

Related PRs and issues for more context:

  • PR #4419 (resource customization, but not for authorization_servers)
  • PR #4462 (fixes header paths, not the metadata field)
  • Issue #4540 (root-level .well-known endpoint discussion)

Let me know if you want more details on the code or possible workarounds.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Nov 28, 2025): <!-- Greeting --> Hi @mattzcarey! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a confirmed bug: per [RFC 9470 Section 2](https://datatracker.ietf.org/doc/html/rfc9470#section-2), the `authorization_servers` field in the OAuth Protected Resource Metadata must contain the issuer URL (e.g., `https://example.com`), not an endpoint path like `https://example.com/api/auth`. Returning a path breaks OAuth discovery because clients will look for metadata at incorrect URLs and fail with 404s. In better-auth, this happens because the MCP plugin sets `authorization_servers` to the computed `baseURL`, which often includes the API path segment. The relevant code is in [`packages/better-auth/src/plugins/mcp/index.ts`](https://github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/plugins/mcp/index.ts#L38-L219), where `authorization_servers: [baseURL]` is used, and `baseURL` is constructed with a default path if not present. This is discussed in [issue #2703](https://github.com/better-auth/better-auth/issues/2703) and [issue #4540](https://github.com/better-auth/better-auth/issues/4540). There is currently no built-in config option to override this behavior. Fixing it would require either using the issuer origin for `authorization_servers` or adding a config option to allow developers to set it explicitly. Until then, the SDK workaround you mentioned is necessary for compatibility. Related PRs and issues for more context: - [PR #4419](https://github.com/better-auth/better-auth/pull/4419) (resource customization, but not for `authorization_servers`) - [PR #4462](https://github.com/better-auth/better-auth/pull/4462) (fixes header paths, not the metadata field) - [Issue #4540](https://github.com/better-auth/better-auth/issues/4540) (root-level .well-known endpoint discussion) Let me know if you want more details on the code or possible workarounds. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/fe30b8c2-5223-42b8-af06-32ebf33182d2?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20confirmed%20bug%3A%20per%20%5BRFC%209470%20Section%202%5D%28https%3A//datatracker.ietf.org/doc/html/rfc9470%23section-2%29%2C%20the%20%60authorization_servers%60%20field%20in%20the%20OAuth%20Protected%20Resource%20Metadata%20must%20contain%20the%20issuer%20URL%20%28e.g.%2C%20%60https%3A//example.com%60%29%2C%20not%20an%20endpoint%20path%20like%20%60https%3A//example.com/api/auth%60.%20Returning%20a%20path%20breaks%20OAuth%20discovery%20because%20clients%20will%20look%20for%20metadata%20at%20incorrect%20URLs%20and%20fail%20with%20404s.%0A%0AIn%20better-auth%2C%20this%20happens%20because%20the%20MCP%20plugin%20sets%20%60authorization_servers%60%20to%20the%20computed%20%60baseURL%60%2C%20which%20often%20includes%20the%20API%20path%20segment.%20The%20relevant%20code%20is%20in%20%5B%60packages/better-auth/src/plugins/mcp/index.ts%60%5D%28https%3A//github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/plugins/mcp/index.ts%23L38-L219%29%2C%20where%20%60authorization_servers%3A%20%5BbaseURL%5D%60%20is%20used%2C%20and%20%60baseURL%60%20is%20constructed%20with%20a%20default%20path%20if%20not%20present.%20This%20is%20discussed%20in%20%5Bissue%20%232703%5D%28https%3A//github.com/better-auth/better-auth/issues/2703%29%20and%20%5Bissue%20%234540%5D%28https%3A//github.com/better-auth/better-auth/issues/4540%29.%0A%0AThere%20is%20currently%20no%20built-in%20config%20option%20to%20override%20this%20behavior.%20Fixing%20it%20would%20require%20either%20using%20the%20issuer%20origin%20for%20%60authorization_servers%60%20or%20adding%20a%20config%20option%20to%20allow%20developers%20to%20set%20it%20explicitly.%20Until%20then%2C%20the%20SDK%20workaround%20you%20mentioned%20is%20necessary%20for%20compatibility.%0A%0ARelated%20PRs%20and%20issues%20for%20more%20context%3A%0A-%20%5BPR%20%234419%5D%28https%3A//github.com/better-auth/better-auth/pull/4419%29%20%28resource%20customization%2C%20but%20not%20for%20%60authorization_servers%60%29%0A-%20%5BPR%20%234462%5D%28https%3A//github.com/better-auth/better-auth/pull/4462%29%20%28fixes%20header%20paths%2C%20not%20the%20metadata%20field%29%0A-%20%5BIssue%20%234540%5D%28https%3A//github.com/better-auth/better-auth/issues/4540%29%20%28root-level%20.well-known%20endpoint%20discussion%29%0A%0ALet%20me%20know%20if%20you%20want%20more%20details%20on%20the%20code%20or%20possible%20workarounds.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6394)
Author
Owner

@rovertrack commented on GitHub (Nov 28, 2025):

i think it sets as same as baseURL

@rovertrack commented on GitHub (Nov 28, 2025): i think it sets as same as baseURL
Author
Owner

@jslno commented on GitHub (Nov 28, 2025):

Endpoints always include the basePath. To have an endpoint at the root (e.g., /.well-known/oauth-authorization-server), you must set basePath to / and mount the handler at the corresponding path.

Example:

  • basePath = "/": https://example.com/.well-known/oauth-authorization-server
  • basePath = "/api/auth": https://example.com/api/auth/.well-known/oauth-authorization-server
@jslno commented on GitHub (Nov 28, 2025): Endpoints always include the `basePath`. To have an endpoint at the root (e.g., `/.well-known/oauth-authorization-server`), you must set `basePath` to `/` and mount the handler at the corresponding path. Example: - **basePath = `"/"`**: `https://example.com/.well-known/oauth-authorization-server` - **basePath = `"/api/auth"`**: `https://example.com/api/auth/.well-known/oauth-authorization-server`
Author
Owner

@Bekacru commented on GitHub (Nov 28, 2025):

I feel like one fix here could just be exposing /.well-known/openid-configuration discovery instead.

So it can be caught by

https://github.com/modelcontextprotocol/typescript-sdk/pull/1192/files#diff-ac04c4c9810612210b0ac8fbf59911c8a9b826954a38eb7a0aeb26bbf73a5325R864-R869

The issues seems like pathname being appended here (https://github.com/modelcontextprotocol/typescript-sdk/pull/1192/files#diff-ac04c4c9810612210b0ac8fbf59911c8a9b826954a38eb7a0aeb26bbf73a5325R852-R855)

@Bekacru commented on GitHub (Nov 28, 2025): I feel like one fix here could just be exposing `/.well-known/openid-configuration` discovery instead. So it can be caught by https://github.com/modelcontextprotocol/typescript-sdk/pull/1192/files#diff-ac04c4c9810612210b0ac8fbf59911c8a9b826954a38eb7a0aeb26bbf73a5325R864-R869 The issues seems like `pathname` being appended here (https://github.com/modelcontextprotocol/typescript-sdk/pull/1192/files#diff-ac04c4c9810612210b0ac8fbf59911c8a9b826954a38eb7a0aeb26bbf73a5325R852-R855)
Author
Owner

@Bekacru commented on GitHub (Nov 28, 2025):

That said @jslno for /.well-known/oauth-authorization-server we probably should return the sever origin regardless of the base path

@Bekacru commented on GitHub (Nov 28, 2025): That said @jslno for `/.well-known/oauth-authorization-server` we probably should return the sever origin regardless of the base path
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2417