mirror of
https://github.com/langgenius/dify.git
synced 2026-11-10 13:26:58 +08:00
fix(mcp): exclude null fields from Dynamic Client Registration payload (#34856)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Byron Wang <byron@dify.ai>
This commit is contained in:
2 files changed
+59
-22
No files matched your search
@@ -532,7 +532,7 @@ def register_client(
|
||||
|
||||
response = ssrf_proxy.post(
|
||||
registration_url,
|
||||
json=client_metadata.model_dump(),
|
||||
json=client_metadata.model_dump(exclude_none=True),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
if not response.is_success:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Unit tests for MCP OAuth authentication flow."""
|
||||
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
import httpx
|
||||
@@ -466,20 +467,32 @@ class TestAuthorizationFlow:
|
||||
},
|
||||
)
|
||||
|
||||
@patch("core.helper.ssrf_proxy.post")
|
||||
def test_register_client_success(self, mock_post):
|
||||
"""Test successful client registration."""
|
||||
mock_response = httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"client_id": "new-client-id",
|
||||
"client_secret": "new-client-secret",
|
||||
"client_name": "Dify",
|
||||
"redirect_uris": ["https://redirect.example.com"],
|
||||
},
|
||||
)
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("scope", "client_uri", "token_endpoint_auth_method", "expected_optional_fields"),
|
||||
[
|
||||
pytest.param(None, None, None, {}, id="omit-none-metadata"),
|
||||
pytest.param(
|
||||
"read write",
|
||||
"https://client.example.com",
|
||||
"none",
|
||||
{
|
||||
"scope": "read write",
|
||||
"client_uri": "https://client.example.com",
|
||||
"token_endpoint_auth_method": "none",
|
||||
},
|
||||
id="preserve-configured-metadata",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_register_client_success(
|
||||
self,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
scope: str | None,
|
||||
client_uri: str | None,
|
||||
token_endpoint_auth_method: str | None,
|
||||
expected_optional_fields: dict[str, str],
|
||||
) -> None:
|
||||
"""Registration omits None metadata and preserves configured values in the HTTP JSON body."""
|
||||
metadata = OAuthMetadata(
|
||||
authorization_endpoint="https://auth.example.com/authorize",
|
||||
token_endpoint="https://auth.example.com/token",
|
||||
@@ -491,20 +504,44 @@ class TestAuthorizationFlow:
|
||||
redirect_uris=["https://redirect.example.com"],
|
||||
grant_types=["authorization_code"],
|
||||
response_types=["code"],
|
||||
scope=scope,
|
||||
client_uri=client_uri,
|
||||
token_endpoint_auth_method=token_endpoint_auth_method,
|
||||
)
|
||||
requests: list[httpx.Request] = []
|
||||
|
||||
client_info = register_client("https://api.example.com", metadata, client_metadata)
|
||||
def handle_registration(request: httpx.Request) -> httpx.Response:
|
||||
requests.append(request)
|
||||
return httpx.Response(
|
||||
201,
|
||||
json={
|
||||
"client_id": "new-client-id",
|
||||
"client_secret": "new-client-secret",
|
||||
"client_name": "Dify",
|
||||
"redirect_uris": ["https://redirect.example.com"],
|
||||
},
|
||||
)
|
||||
|
||||
with httpx.Client(transport=httpx.MockTransport(handle_registration)) as client:
|
||||
monkeypatch.setattr(ssrf_proxy, "_get_ssrf_client", lambda _verify: client)
|
||||
client_info = register_client("https://api.example.com", metadata, client_metadata)
|
||||
|
||||
assert isinstance(client_info, OAuthClientInformationFull)
|
||||
assert client_info.client_id == "new-client-id"
|
||||
assert client_info.client_secret == "new-client-secret"
|
||||
|
||||
# Verify request
|
||||
mock_post.assert_called_once_with(
|
||||
"https://auth.example.com/register",
|
||||
json=client_metadata.model_dump(),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
assert len(requests) == 1
|
||||
request = requests[0]
|
||||
assert request.method == "POST"
|
||||
assert str(request.url) == "https://auth.example.com/register"
|
||||
assert request.headers["Content-Type"] == "application/json"
|
||||
assert json.loads(request.content) == {
|
||||
"client_name": "Dify",
|
||||
"redirect_uris": ["https://redirect.example.com"],
|
||||
"grant_types": ["authorization_code"],
|
||||
"response_types": ["code"],
|
||||
**expected_optional_fields,
|
||||
}
|
||||
|
||||
@patch("core.helper.ssrf_proxy.post")
|
||||
def test_register_client_rejected_by_server(self, mock_post):
|
||||
|
||||
Reference in new issue
Block a user