[PR #19796] [CLOSED] fix: MCP OAuth discovery via Protected Resource metadata flow #25348

Closed
opened 2026-04-20 05:53:54 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/19796
Author: @jamie-dit
Created: 12/7/2025
Status: Closed

Base: devHead: fix/mcp-oauth-protected-resource-discovery-v2


📝 Commits (7)

📊 Changes

1 file changed (+77 additions, -2 deletions)

View changed files

📝 backend/open_webui/utils/oauth.py (+77 -2)

📄 Description

Summary

This PR fixes MCP OAuth 2.1 client registration failing when the OAuth authorization server is on a different domain than the MCP server (e.g., Todoist MCP at ai.todoist.net with OAuth at todoist.com).

Problem

Currently, Open WebUI only looks for OAuth metadata at:

{MCP_SERVER_URL}/.well-known/oauth-authorization-server

But according to the MCP Authorization spec, clients should follow the Protected Resource discovery flow when the OAuth server is on a different domain.

Solution

Implements the full MCP Protected Resource discovery flow:

  1. Make unauthenticated request to MCP endpoint → receives 401 with WWW-Authenticate header
  2. Parse resource_metadata URL from the header
  3. Fetch Protected Resource metadata → contains authorization_servers array
  4. Use discovered servers for OAuth metadata discovery

Example flow for Todoist:

POST ai.todoist.net/mcp (no auth)
    ↓ 401 + WWW-Authenticate: Bearer resource_metadata="https://ai.todoist.net/.well-known/oauth-protected-resource/mcp"

GET ai.todoist.net/.well-known/oauth-protected-resource/mcp
    ↓ { "authorization_servers": ["https://todoist.com"] }

GET todoist.com/.well-known/oauth-authorization-server
    ↓ { "registration_endpoint": "https://todoist.com/oauth/register", ... }

POST todoist.com/oauth/register (DCR)
    ↓ { "client_id": "...", "client_secret": "..." } ✅

Changes

  • Added discover_authorization_server_from_mcp() function that implements the Protected Resource discovery flow
  • Modified get_oauth_client_info_with_dynamic_client_registration() to:
    1. First try MCP Protected Resource discovery
    2. Build discovery URLs from discovered authorization servers
    3. Fall back to existing behaviour if discovery fails

Backwards Compatibility

The fix is fully backwards-compatible. If Protected Resource discovery fails (no WWW-Authenticate header, network error, etc.), it falls back to the existing discovery behavior.


Pull Request Checklist

  • Target branch: PR targets dev
  • Description: Included above
  • Testing: Manual validation performed (see below)
  • Code review: Self-review done

Testing

I have personally tested ALL changes in this PR

How I tested it:

  1. Applied the patch to a running Open WebUI Docker instance
  2. Navigated to Admin Settings → External Tools → Add Server
  3. Set Type to "MCP (Streamable HTTP)"
  4. Entered URL: https://ai.todoist.net/mcp
  5. Set Auth to "OAuth 2.1"
  6. Clicked "Register Client"
  7. Before fix: Registration failed with 400 Bad Request
  8. After fix: Registration succeeded, OAuth flow completed, Todoist tools available

Verification of discovery flow:

# Confirmed MCP server returns WWW-Authenticate header
$ curl -s -D - https://ai.todoist.net/mcp -X POST -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}'
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://ai.todoist.net/.well-known/oauth-protected-resource/mcp"

# Confirmed Protected Resource metadata returns authorization server
$ curl -s https://ai.todoist.net/.well-known/oauth-protected-resource/mcp
{"resource":"https://ai.todoist.net/mcp","authorization_servers":["https://todoist.com"],...}

# Confirmed DCR endpoint works at discovered authorization server
$ curl -s https://todoist.com/oauth/register -X POST -H "Content-Type: application/json" \
  -d '{"redirect_uris":["https://example.com/callback"],"client_name":"Test"}'
{"client_id":"tdd_xxx","client_secret":"xxx",...}

Fixes #19794


Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.


🔄 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/open-webui/open-webui/pull/19796 **Author:** [@jamie-dit](https://github.com/jamie-dit) **Created:** 12/7/2025 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `fix/mcp-oauth-protected-resource-discovery-v2` --- ### 📝 Commits (7) - [`fe6783c`](https://github.com/open-webui/open-webui/commit/fe6783c16699911c7be17392596d579333fb110c) Merge pull request #19030 from open-webui/dev - [`fc05e0a`](https://github.com/open-webui/open-webui/commit/fc05e0a6c5d39da60b603b4d520f800d6e36f748) Merge pull request #19405 from open-webui/dev - [`e3faec6`](https://github.com/open-webui/open-webui/commit/e3faec62c58e3a83d89aa3df539feacefa125e0c) Merge pull request #19416 from open-webui/dev - [`9899293`](https://github.com/open-webui/open-webui/commit/9899293f050ad50ae12024cbebee7e018acd851e) Merge pull request #19448 from open-webui/dev - [`140605e`](https://github.com/open-webui/open-webui/commit/140605e660b8186a7d5c79fb3be6ffb147a2f498) Merge pull request #19462 from open-webui/dev - [`6f1486f`](https://github.com/open-webui/open-webui/commit/6f1486ffd0cb288d0e21f41845361924e0d742b3) Merge pull request #19466 from open-webui/dev - [`b766a23`](https://github.com/open-webui/open-webui/commit/b766a23e36062e52c852a849beba64e67de9d0c8) fix: MCP OAuth discovery via Protected Resource metadata flow ### 📊 Changes **1 file changed** (+77 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/utils/oauth.py` (+77 -2) </details> ### 📄 Description ## Summary This PR fixes MCP OAuth 2.1 client registration failing when the OAuth authorization server is on a different domain than the MCP server (e.g., Todoist MCP at `ai.todoist.net` with OAuth at `todoist.com`). ## Problem Currently, Open WebUI only looks for OAuth metadata at: ``` {MCP_SERVER_URL}/.well-known/oauth-authorization-server ``` But according to the [MCP Authorization spec](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization), clients should follow the Protected Resource discovery flow when the OAuth server is on a different domain. ## Solution Implements the full MCP Protected Resource discovery flow: 1. **Make unauthenticated request** to MCP endpoint → receives 401 with `WWW-Authenticate` header 2. **Parse `resource_metadata` URL** from the header 3. **Fetch Protected Resource metadata** → contains `authorization_servers` array 4. **Use discovered servers** for OAuth metadata discovery ### Example flow for Todoist: ``` POST ai.todoist.net/mcp (no auth) ↓ 401 + WWW-Authenticate: Bearer resource_metadata="https://ai.todoist.net/.well-known/oauth-protected-resource/mcp" GET ai.todoist.net/.well-known/oauth-protected-resource/mcp ↓ { "authorization_servers": ["https://todoist.com"] } GET todoist.com/.well-known/oauth-authorization-server ↓ { "registration_endpoint": "https://todoist.com/oauth/register", ... } POST todoist.com/oauth/register (DCR) ↓ { "client_id": "...", "client_secret": "..." } ✅ ``` ## Changes - Added `discover_authorization_server_from_mcp()` function that implements the Protected Resource discovery flow - Modified `get_oauth_client_info_with_dynamic_client_registration()` to: 1. First try MCP Protected Resource discovery 2. Build discovery URLs from discovered authorization servers 3. Fall back to existing behaviour if discovery fails ## Backwards Compatibility The fix is fully backwards-compatible. If Protected Resource discovery fails (no `WWW-Authenticate` header, network error, etc.), it falls back to the existing discovery behavior. --- ## Pull Request Checklist * [x] **Target branch:** PR targets `dev` * [x] **Description:** Included above * [x] **Testing:** Manual validation performed (see below) * [x] **Code review:** Self-review done --- ## Testing ✅ **I have personally tested ALL changes in this PR** ### How I tested it: 1. Applied the patch to a running Open WebUI Docker instance 2. Navigated to Admin Settings → External Tools → Add Server 3. Set Type to "MCP (Streamable HTTP)" 4. Entered URL: `https://ai.todoist.net/mcp` 5. Set Auth to "OAuth 2.1" 6. Clicked "Register Client" 7. **Before fix:** Registration failed with 400 Bad Request 8. **After fix:** Registration succeeded, OAuth flow completed, Todoist tools available ### Verification of discovery flow: ```bash # Confirmed MCP server returns WWW-Authenticate header $ curl -s -D - https://ai.todoist.net/mcp -X POST -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}' HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://ai.todoist.net/.well-known/oauth-protected-resource/mcp" # Confirmed Protected Resource metadata returns authorization server $ curl -s https://ai.todoist.net/.well-known/oauth-protected-resource/mcp {"resource":"https://ai.todoist.net/mcp","authorization_servers":["https://todoist.com"],...} # Confirmed DCR endpoint works at discovered authorization server $ curl -s https://todoist.com/oauth/register -X POST -H "Content-Type: application/json" \ -d '{"redirect_uris":["https://example.com/callback"],"client_name":"Test"}' {"client_id":"tdd_xxx","client_secret":"xxx",...} ``` --- ## Related Issues Fixes #19794 --- ## Contributor License Agreement By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <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-04-20 05:53:54 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#25348