@@ -337,6 +337,109 @@ async def test_oauth_discovery_fallback_order(self, oauth_provider):
337
337
"https://api.example.com/v1/mcp/.well-known/openid-configuration" ,
338
338
]
339
339
340
+ @pytest .mark .anyio
341
+ async def test_oauth_discovery_fallback_conditions (self , oauth_provider ):
342
+ """Test the conditions during which an AS metadata discovery fallback will be attempted."""
343
+ # Ensure no tokens are stored
344
+ oauth_provider .context .current_tokens = None
345
+ oauth_provider .context .token_expiry_time = None
346
+ oauth_provider ._initialized = True
347
+
348
+ # Mock client info to skip DCR
349
+ oauth_provider .context .client_info = OAuthClientInformationFull (
350
+ client_id = "existing_client" ,
351
+ redirect_uris = [AnyUrl ("http://localhost:3030/callback" )],
352
+ )
353
+
354
+ # Create a test request
355
+ test_request = httpx .Request ("GET" , "https://api.example.com/v1/mcp" )
356
+
357
+ # Mock the auth flow
358
+ auth_flow = oauth_provider .async_auth_flow (test_request )
359
+
360
+ # First request should be the original request without auth header
361
+ request = await auth_flow .__anext__ ()
362
+ assert "Authorization" not in request .headers
363
+
364
+ # Send a 401 response to trigger the OAuth flow
365
+ response = httpx .Response (
366
+ 401 ,
367
+ headers = {
368
+ "WWW-Authenticate" : 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"'
369
+ },
370
+ request = test_request ,
371
+ )
372
+
373
+ # Next request should be to discover protected resource metadata
374
+ discovery_request = await auth_flow .asend (response )
375
+ assert str (discovery_request .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
376
+ assert discovery_request .method == "GET"
377
+
378
+ # Send a successful discovery response with minimal protected resource metadata
379
+ discovery_response = httpx .Response (
380
+ 200 ,
381
+ content = b'{"resource": "https://api.example.com/v1/mcp", "authorization_servers": ["https://auth.example.com/v1/mcp"]}' ,
382
+ request = discovery_request ,
383
+ )
384
+
385
+ # Next request should be to discover OAuth metadata
386
+ oauth_metadata_request_1 = await auth_flow .asend (discovery_response )
387
+ assert (
388
+ str (oauth_metadata_request_1 .url )
389
+ == "https://auth.example.com/.well-known/oauth-authorization-server/v1/mcp"
390
+ )
391
+ assert oauth_metadata_request_1 .method == "GET"
392
+
393
+ # Send a 404 response
394
+ oauth_metadata_response_1 = httpx .Response (
395
+ 404 ,
396
+ content = b"Not Found" ,
397
+ request = oauth_metadata_request_1 ,
398
+ )
399
+
400
+ # Next request should be to discover OAuth metadata at the next endpoint
401
+ oauth_metadata_request_2 = await auth_flow .asend (oauth_metadata_response_1 )
402
+ assert str (oauth_metadata_request_2 .url ) == "https://auth.example.com/.well-known/oauth-authorization-server"
403
+ assert oauth_metadata_request_2 .method == "GET"
404
+
405
+ # Send a 400 response
406
+ oauth_metadata_response_2 = httpx .Response (
407
+ 400 ,
408
+ content = b"Bad Request" ,
409
+ request = oauth_metadata_request_2 ,
410
+ )
411
+
412
+ # Next request should be to discover OAuth metadata at the next endpoint
413
+ oauth_metadata_request_3 = await auth_flow .asend (oauth_metadata_response_2 )
414
+ assert str (oauth_metadata_request_3 .url ) == "https://auth.example.com/.well-known/openid-configuration/v1/mcp"
415
+ assert oauth_metadata_request_3 .method == "GET"
416
+
417
+ # Send a 500 response
418
+ oauth_metadata_response_3 = httpx .Response (
419
+ 500 ,
420
+ content = b"Internal Server Error" ,
421
+ request = oauth_metadata_request_3 ,
422
+ )
423
+
424
+ # Mock the authorization process to minimize unnecessary state in this test
425
+ oauth_provider ._perform_authorization = mock .AsyncMock (return_value = ("test_auth_code" , "test_code_verifier" ))
426
+
427
+ # Next request should fall back to legacy behavior and auth with the RS (mocked /authorize, next is /token)
428
+ token_request = await auth_flow .asend (oauth_metadata_response_3 )
429
+ assert str (token_request .url ) == "https://api.example.com/token"
430
+ assert token_request .method == "POST"
431
+
432
+ # Send a successful token response
433
+ token_response = httpx .Response (
434
+ 200 ,
435
+ content = (
436
+ b'{"access_token": "new_access_token", "token_type": "Bearer", "expires_in": 3600, '
437
+ b'"refresh_token": "new_refresh_token"}'
438
+ ),
439
+ request = token_request ,
440
+ )
441
+ token_request = await auth_flow .asend (token_response )
442
+
340
443
@pytest .mark .anyio
341
444
async def test_handle_metadata_response_success (self , oauth_provider ):
342
445
"""Test successful metadata response handling."""
0 commit comments