@@ -994,163 +994,7 @@ async def test_client_registration_invalid_grant_type(
994994 )
995995
996996
997- class TestFastMCPWithAuth :
998- """Test FastMCP server with authentication."""
999997
1000- @pytest .mark .anyio
1001- async def test_fastmcp_with_auth (
1002- self , mock_oauth_provider : MockOAuthProvider , pkce_challenge
1003- ):
1004- """Test creating a FastMCP server with authentication."""
1005- # Create FastMCP server with auth provider
1006- mcp = FastMCP (
1007- auth_server_provider = mock_oauth_provider ,
1008- require_auth = True ,
1009- auth = AuthSettings (
1010- issuer_url = AnyHttpUrl ("https://auth.example.com" ),
1011- client_registration_options = ClientRegistrationOptions (enabled = True ),
1012- revocation_options = RevocationOptions (enabled = True ),
1013- required_scopes = ["read" , "write" ],
1014- ),
1015- )
1016-
1017- # Add a test tool
1018- @mcp .tool ()
1019- def test_tool (x : int ) -> str :
1020- return f"Result: { x } "
1021-
1022- async with anyio .create_task_group () as task_group :
1023- transport = StreamingASGITransport (
1024- app = mcp .sse_app (),
1025- task_group = task_group ,
1026- )
1027- test_client = httpx .AsyncClient (
1028- transport = transport , base_url = "http://mcptest.com"
1029- )
1030-
1031- # Test metadata endpoint
1032- response = await test_client .get ("/.well-known/oauth-authorization-server" )
1033- assert response .status_code == 200
1034-
1035- # Test that auth is required for protected endpoints
1036- response = await test_client .get ("/sse" )
1037- assert response .status_code == 401
1038-
1039- response = await test_client .post ("/messages/" )
1040- assert response .status_code == 401 , response .content
1041-
1042- response = await test_client .post (
1043- "/messages/" ,
1044- headers = {"Authorization" : "invalid" },
1045- )
1046- assert response .status_code == 401
1047-
1048- response = await test_client .post (
1049- "/messages/" ,
1050- headers = {"Authorization" : "Bearer invalid" },
1051- )
1052- assert response .status_code == 401
1053-
1054- # now, become authenticated and try to go through the flow again
1055- client_metadata = {
1056- "redirect_uris" : ["https://client.example.com/callback" ],
1057- "client_name" : "Test Client" ,
1058- }
1059-
1060- response = await test_client .post (
1061- "/register" ,
1062- json = client_metadata ,
1063- )
1064- assert response .status_code == 201
1065- client_info = response .json ()
1066-
1067- # Request authorization using POST with form-encoded data
1068- response = await test_client .post (
1069- "/authorize" ,
1070- data = {
1071- "response_type" : "code" ,
1072- "client_id" : client_info ["client_id" ],
1073- "redirect_uri" : "https://client.example.com/callback" ,
1074- "code_challenge" : pkce_challenge ["code_challenge" ],
1075- "code_challenge_method" : "S256" ,
1076- "state" : "test_state" ,
1077- },
1078- )
1079- assert response .status_code == 302
1080-
1081- # Extract the authorization code from the redirect URL
1082- redirect_url = response .headers ["location" ]
1083- parsed_url = urlparse (redirect_url )
1084- query_params = parse_qs (parsed_url .query )
1085-
1086- assert "code" in query_params
1087- auth_code = query_params ["code" ][0 ]
1088-
1089- # Exchange the authorization code for tokens
1090- response = await test_client .post (
1091- "/token" ,
1092- data = {
1093- "grant_type" : "authorization_code" ,
1094- "client_id" : client_info ["client_id" ],
1095- "client_secret" : client_info ["client_secret" ],
1096- "code" : auth_code ,
1097- "code_verifier" : pkce_challenge ["code_verifier" ],
1098- "redirect_uri" : "https://client.example.com/callback" ,
1099- },
1100- )
1101- assert response .status_code == 200
1102-
1103- token_response = response .json ()
1104- assert "access_token" in token_response
1105- authorization = f"Bearer { token_response ['access_token' ]} "
1106-
1107- # Test the authenticated endpoint with valid token
1108- async with aconnect_sse (
1109- test_client , "GET" , "/sse" , headers = {"Authorization" : authorization }
1110- ) as event_source :
1111- assert event_source .response .status_code == 200
1112- events = event_source .aiter_sse ()
1113- sse = await events .__anext__ ()
1114- assert sse .event == "endpoint"
1115- assert sse .data .startswith ("/messages/?session_id=" )
1116- messages_uri = sse .data
1117-
1118- # verify that we can now post to the /messages endpoint,
1119- # and get a response on the /sse endpoint
1120- response = await test_client .post (
1121- messages_uri ,
1122- headers = {"Authorization" : authorization },
1123- content = JSONRPCRequest (
1124- jsonrpc = "2.0" ,
1125- id = "123" ,
1126- method = "initialize" ,
1127- params = {
1128- "protocolVersion" : "2024-11-05" ,
1129- "capabilities" : {
1130- "roots" : {"listChanged" : True },
1131- "sampling" : {},
1132- },
1133- "clientInfo" : {"name" : "ExampleClient" , "version" : "1.0.0" },
1134- },
1135- ).model_dump_json (),
1136- )
1137- assert response .status_code == 202
1138- assert response .content == b"Accepted"
1139-
1140- sse = await events .__anext__ ()
1141- assert sse .event == "message"
1142- sse_data = json .loads (sse .data )
1143- assert sse_data ["id" ] == "123"
1144- assert set (sse_data ["result" ]["capabilities" ].keys ()) == {
1145- "experimental" ,
1146- "prompts" ,
1147- "resources" ,
1148- "tools" ,
1149- }
1150- # the /sse endpoint will never finish; normally, the client could just
1151- # disconnect, but in tests the easiest way to do this is to cancel the
1152- # task group
1153- task_group .cancel_scope .cancel ()
1154998
1155999
11561000class TestAuthorizeEndpointErrors :
0 commit comments