@@ -337,6 +337,109 @@ async def test_oauth_discovery_fallback_order(self, oauth_provider):
337337 "https://api.example.com/v1/mcp/.well-known/openid-configuration" ,
338338 ]
339339
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+
340443 @pytest .mark .anyio
341444 async def test_handle_metadata_response_success (self , oauth_provider ):
342445 """Test successful metadata response handling."""
0 commit comments