Skip to content

Commit 50673c6

Browse files
committed
Validate grant_types on registration
1 parent 5ebbc19 commit 50673c6

File tree

2 files changed

+34
-21
lines changed

2 files changed

+34
-21
lines changed

src/mcp/server/auth/handlers/register.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ async def handle(self, request: Request) -> Response:
7575
),
7676
status_code=400,
7777
)
78+
if set(client_metadata.grant_types) != set(
79+
["authorization_code", "refresh_token"]
80+
):
81+
return PydanticJSONResponse(
82+
content=RegistrationErrorResponse(
83+
error="invalid_client_metadata",
84+
error_description="grant_types must be authorization_code "
85+
"and refresh_token",
86+
),
87+
status_code=400,
88+
)
7889

7990
client_id_issued_at = int(time.time())
8091
client_secret_expires_at = (

tests/server/fastmcp/auth/test_auth_integration.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -407,27 +407,6 @@ async def test_token_validation_error(self, test_client: httpx.AsyncClient):
407407
"error_description" in error_response
408408
) # Contains validation error messages
409409

410-
@pytest.mark.anyio
411-
@pytest.mark.parametrize(
412-
"registered_client", [{"grant_types": ["authorization_code"]}], indirect=True
413-
)
414-
async def test_token_unsupported_grant_type(self, test_client, registered_client):
415-
"""Test token endpoint error - unsupported grant type."""
416-
# Try refresh_token grant with client that only supports authorization_code
417-
response = await test_client.post(
418-
"/token",
419-
data={
420-
"grant_type": "refresh_token",
421-
"client_id": registered_client["client_id"],
422-
"client_secret": registered_client["client_secret"],
423-
"refresh_token": "some_refresh_token",
424-
},
425-
)
426-
assert response.status_code == 400
427-
error_response = response.json()
428-
assert error_response["error"] == "unsupported_grant_type"
429-
assert "supported grant types" in error_response["error_description"]
430-
431410
@pytest.mark.anyio
432411
async def test_token_invalid_auth_code(
433412
self, test_client, registered_client, pkce_challenge
@@ -1001,6 +980,29 @@ async def test_client_registration_default_scopes(
1001980
# Check that default scopes were applied
1002981
assert registered_client.scope == "read write"
1003982

983+
@pytest.mark.anyio
984+
async def test_client_registration_invalid_grant_type(
985+
self, test_client: httpx.AsyncClient
986+
):
987+
client_metadata = {
988+
"redirect_uris": ["https://client.example.com/callback"],
989+
"client_name": "Test Client",
990+
"grant_types": ["authorization_code"],
991+
}
992+
993+
response = await test_client.post(
994+
"/register",
995+
json=client_metadata,
996+
)
997+
assert response.status_code == 400
998+
error_data = response.json()
999+
assert "error" in error_data
1000+
assert error_data["error"] == "invalid_client_metadata"
1001+
assert (
1002+
error_data["error_description"]
1003+
== "grant_types must be authorization_code and refresh_token"
1004+
)
1005+
10041006

10051007
class TestFastMCPWithAuth:
10061008
"""Test FastMCP server with authentication."""

0 commit comments

Comments
 (0)