From 041d7ffe3df704b46cae0e8f42a7aaabf6819c75 Mon Sep 17 00:00:00 2001 From: wangxiaolei Date: Mon, 16 Mar 2026 15:44:48 +0800 Subject: [PATCH] chore: compatiable resource_metadata return without scheme (#33506) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/core/mcp/auth/auth_flow.py | 26 +++++++++++++++---- .../core/mcp/auth/test_auth_flow.py | 21 +++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/api/core/mcp/auth/auth_flow.py b/api/core/mcp/auth/auth_flow.py index aef1afb235..d015769b54 100644 --- a/api/core/mcp/auth/auth_flow.py +++ b/api/core/mcp/auth/auth_flow.py @@ -55,15 +55,31 @@ def build_protected_resource_metadata_discovery_urls( """ urls = [] + parsed_server_url = urlparse(server_url) + base_url = f"{parsed_server_url.scheme}://{parsed_server_url.netloc}" + path = parsed_server_url.path.rstrip("/") + # First priority: URL from WWW-Authenticate header if www_auth_resource_metadata_url: - urls.append(www_auth_resource_metadata_url) + parsed_metadata_url = urlparse(www_auth_resource_metadata_url) + normalized_metadata_url = None + if parsed_metadata_url.scheme and parsed_metadata_url.netloc: + normalized_metadata_url = www_auth_resource_metadata_url + elif not parsed_metadata_url.scheme and parsed_metadata_url.netloc: + normalized_metadata_url = f"{parsed_server_url.scheme}:{www_auth_resource_metadata_url}" + elif ( + not parsed_metadata_url.scheme + and not parsed_metadata_url.netloc + and parsed_metadata_url.path.startswith("/") + ): + first_segment = parsed_metadata_url.path.lstrip("/").split("/", 1)[0] + if first_segment == ".well-known" or "." not in first_segment: + normalized_metadata_url = urljoin(base_url, parsed_metadata_url.path) + + if normalized_metadata_url: + urls.append(normalized_metadata_url) # Fallback: construct from server URL - parsed = urlparse(server_url) - base_url = f"{parsed.scheme}://{parsed.netloc}" - path = parsed.path.rstrip("/") - # Priority 2: With path insertion (e.g., /.well-known/oauth-protected-resource/public/mcp) if path: path_url = f"{base_url}/.well-known/oauth-protected-resource{path}" diff --git a/api/tests/unit_tests/core/mcp/auth/test_auth_flow.py b/api/tests/unit_tests/core/mcp/auth/test_auth_flow.py index abf3c60fe0..fe533e62af 100644 --- a/api/tests/unit_tests/core/mcp/auth/test_auth_flow.py +++ b/api/tests/unit_tests/core/mcp/auth/test_auth_flow.py @@ -801,6 +801,27 @@ class TestAuthOrchestration: urls = build_protected_resource_metadata_discovery_urls(None, "https://api.example.com") assert urls == ["https://api.example.com/.well-known/oauth-protected-resource"] + def test_build_protected_resource_metadata_discovery_urls_with_relative_hint(self): + urls = build_protected_resource_metadata_discovery_urls( + "/.well-known/oauth-protected-resource/tenant/mcp", + "https://api.example.com/tenant/mcp", + ) + assert urls == [ + "https://api.example.com/.well-known/oauth-protected-resource/tenant/mcp", + "https://api.example.com/.well-known/oauth-protected-resource", + ] + + def test_build_protected_resource_metadata_discovery_urls_ignores_scheme_less_hint(self): + urls = build_protected_resource_metadata_discovery_urls( + "/openapi-mcp.cn-hangzhou.aliyuncs.com/.well-known/oauth-protected-resource/tenant/mcp", + "https://openapi-mcp.cn-hangzhou.aliyuncs.com/tenant/mcp", + ) + + assert urls == [ + "https://openapi-mcp.cn-hangzhou.aliyuncs.com/.well-known/oauth-protected-resource/tenant/mcp", + "https://openapi-mcp.cn-hangzhou.aliyuncs.com/.well-known/oauth-protected-resource", + ] + def test_build_oauth_authorization_server_metadata_discovery_urls(self): # Case 1: with auth_server_url urls = build_oauth_authorization_server_metadata_discovery_urls(