[PR #4462] [MERGED] fix(mcp): remove duplicate /api/auth from wwwAuthenticateValue and properly format the header #5396

Closed
opened 2026-03-13 12:21:22 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/4462
Author: @paoloricciuti
Created: 9/5/2025
Status: Merged
Merged: 9/5/2025
Merged by: @himself65

Base: canaryHead: www-authenticate-headers-fix


📝 Commits (3)

  • 255a7b5 fix: remove duplicate /api/auth from wwwAuthenticateValue and properly format the header
  • 9c0af4f fix: update comment in test file
  • 48183e8 Merge branch 'canary' into www-authenticate-headers-fix

📊 Changes

3 files changed (+26 additions, -4 deletions)

View changed files

📝 docs/content/docs/plugins/mcp.mdx (+2 -2)
📝 packages/better-auth/src/plugins/mcp/index.ts (+3 -1)
📝 packages/better-auth/src/plugins/mcp/mcp.test.ts (+21 -1)

📄 Description

While implementing Auth for MCP using better auth I've found some weirdness in the whole implementation.

  1. The header is not properly formatted. Per specification the header should be in the format Bearer resource_metadata="URL_TO_RESOURCE"...currently the withMcpAuth helper returns Bearer resource_metadata=URL_TO_RESOURCE (notice the missing quotes).
  2. The wwwAuthenticateValue adds /api/auth after baseURL but getBaseUrl does the same. This means the resource is actually a pretty funky looking /api/auth/api/auth/.well-known/oauth-protected-resource.
  3. In browser based clients (like the mcp inspector for instance) because of CORS headers are not exposed so the client can't read the www-authenticate header and so they default to /.well-known/oauth-protected-resource (notice the missing /api/auth. I've added the CORS header on the request so that this shouldn't happen again.
  4. Always referring to the same point as before I was pretty puzzled because the documentation tells the user to add a route for /.well-known/oauth-protected-resource but technically the resource should've been handled by the library handler by default (see 9833a7edee/packages/better-auth/src/plugins/mcp/index.ts (L175-L205)). I think the reason above is the reason those parts of the docs were added. So i guess those parts are not actually necessary if we merge this but maybe some funky CORS configuration could still lead the clients to do that default request so it might be a good idea to add them nonetheless.

I've also added a test for the withMcpAuth helper, since it doesn't really need a server running I just used a custom made request and asserted on the response. I didn't do that for the authenticated version because it's not fully clear to me how I could "authenticate" for MCP...I tried to pass the headers but it still didn't found any MCP session.

That said I'm willing to make any change to this. I'm developing a library to build mcp servers (which btw would be a good fit for better-auth since it uses WebRequest and WebResponse and you users would not need mcp-handler to interface with it like the official sdk) and I have built some helpers for the authentication but I would very much refer to better-auth instead that will definitely do a better (pun intended) job than me.


Summary by cubic

Fixes the MCP WWW-Authenticate challenge to use the correct Bearer format, point to the right protected resource URL, and make the header readable by browser clients. Also updates docs and adds a test to prevent regressions.

  • Bug Fixes
    • Use Bearer resource_metadata="URL" (adds quotes) per RFC 9728.
    • Remove duplicate /api/auth; URL now resolves to /.well-known/oauth-protected-resource correctly.
    • Expose WWW-Authenticate via Access-Control-Expose-Headers on 401 responses for browser clients.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/4462 **Author:** [@paoloricciuti](https://github.com/paoloricciuti) **Created:** 9/5/2025 **Status:** ✅ Merged **Merged:** 9/5/2025 **Merged by:** [@himself65](https://github.com/himself65) **Base:** `canary` ← **Head:** `www-authenticate-headers-fix` --- ### 📝 Commits (3) - [`255a7b5`](https://github.com/better-auth/better-auth/commit/255a7b57308d6cd536222e30da7dde582aa22a58) fix: remove duplicate `/api/auth` from `wwwAuthenticateValue` and properly format the header - [`9c0af4f`](https://github.com/better-auth/better-auth/commit/9c0af4f3abfecc8be98e22227d1648fa66affaa1) fix: update comment in test file - [`48183e8`](https://github.com/better-auth/better-auth/commit/48183e81593958578c3bd1bb7576a58482fa061b) Merge branch 'canary' into www-authenticate-headers-fix ### 📊 Changes **3 files changed** (+26 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `docs/content/docs/plugins/mcp.mdx` (+2 -2) 📝 `packages/better-auth/src/plugins/mcp/index.ts` (+3 -1) 📝 `packages/better-auth/src/plugins/mcp/mcp.test.ts` (+21 -1) </details> ### 📄 Description While implementing Auth for MCP using better auth I've found some weirdness in the whole implementation. 1. The header is not properly formatted. [Per specification](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1) the header should be in the format `Bearer resource_metadata="URL_TO_RESOURCE"`...currently the `withMcpAuth` helper returns `Bearer resource_metadata=URL_TO_RESOURCE` (notice the missing quotes). 2. The `wwwAuthenticateValue` adds `/api/auth` after baseURL but `getBaseUrl` does the same. This means the resource is actually a pretty funky looking `/api/auth/api/auth/.well-known/oauth-protected-resource`. 3. In browser based clients (like the mcp inspector for instance) because of CORS headers are not exposed so the client can't read the `www-authenticate` header and so they default to `/.well-known/oauth-protected-resource` (notice the missing `/api/auth`. I've added the CORS header on the request so that this shouldn't happen again. 4. Always referring to the same point as before I was pretty puzzled because the documentation tells the user to add a route for `/.well-known/oauth-protected-resource` but technically the resource should've been handled by the library handler by default (see https://github.com/better-auth/better-auth/blob/9833a7edeef38e6d287badc4b8d11fe598ad55be/packages/better-auth/src/plugins/mcp/index.ts#L175-L205). I think the reason above is the reason those parts of the docs were added. So i guess those parts are not actually necessary if we merge this but maybe some funky CORS configuration could still lead the clients to do that default request so it might be a good idea to add them nonetheless. I've also added a test for the `withMcpAuth` helper, since it doesn't really need a server running I just used a custom made request and asserted on the response. I didn't do that for the authenticated version because it's not fully clear to me how I could "authenticate" for MCP...I tried to pass the `headers` but it still didn't found any MCP session. That said I'm willing to make any change to this. I'm developing a [library to build mcp servers](https://github.com/paoloricciuti/tmcp) (which btw would be a good fit for `better-auth` since it uses WebRequest and WebResponse and you users would not need `mcp-handler` to interface with it like the official sdk) and I have built some helpers for the authentication but I would very much refer to `better-auth` instead that will definitely do a better (pun intended) job than me. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes the MCP WWW-Authenticate challenge to use the correct Bearer format, point to the right protected resource URL, and make the header readable by browser clients. Also updates docs and adds a test to prevent regressions. - **Bug Fixes** - Use Bearer resource_metadata="URL" (adds quotes) per RFC 9728. - Remove duplicate /api/auth; URL now resolves to /.well-known/oauth-protected-resource correctly. - Expose WWW-Authenticate via Access-Control-Expose-Headers on 401 responses for browser clients. <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-03-13 12:21:22 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#5396