@@ -261,6 +261,109 @@ async def test_oauth_discovery_fallback_order(self, oauth_provider):
261261 "https://api.example.com/v1/mcp/.well-known/openid-configuration" ,
262262 ]
263263
264+ @pytest .mark .anyio
265+ async def test_oauth_discovery_fallback_conditions (self , oauth_provider ):
266+ """Test the conditions during which an AS metadata discovery fallback will be attempted."""
267+ # Ensure no tokens are stored
268+ oauth_provider .context .current_tokens = None
269+ oauth_provider .context .token_expiry_time = None
270+ oauth_provider ._initialized = True
271+
272+ # Mock client info to skip DCR
273+ oauth_provider .context .client_info = OAuthClientInformationFull (
274+ client_id = "existing_client" ,
275+ redirect_uris = [AnyUrl ("http://localhost:3030/callback" )],
276+ )
277+
278+ # Create a test request
279+ test_request = httpx .Request ("GET" , "https://api.example.com/v1/mcp" )
280+
281+ # Mock the auth flow
282+ auth_flow = oauth_provider .async_auth_flow (test_request )
283+
284+ # First request should be the original request without auth header
285+ request = await auth_flow .__anext__ ()
286+ assert "Authorization" not in request .headers
287+
288+ # Send a 401 response to trigger the OAuth flow
289+ response = httpx .Response (
290+ 401 ,
291+ headers = {
292+ "WWW-Authenticate" : 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"'
293+ },
294+ request = test_request ,
295+ )
296+
297+ # Next request should be to discover protected resource metadata
298+ discovery_request = await auth_flow .asend (response )
299+ assert str (discovery_request .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
300+ assert discovery_request .method == "GET"
301+
302+ # Send a successful discovery response with minimal protected resource metadata
303+ discovery_response = httpx .Response (
304+ 200 ,
305+ content = b'{"resource": "https://api.example.com/v1/mcp", "authorization_servers": ["https://auth.example.com/v1/mcp"]}' ,
306+ request = discovery_request ,
307+ )
308+
309+ # Next request should be to discover OAuth metadata
310+ oauth_metadata_request_1 = await auth_flow .asend (discovery_response )
311+ assert (
312+ str (oauth_metadata_request_1 .url )
313+ == "https://auth.example.com/.well-known/oauth-authorization-server/v1/mcp"
314+ )
315+ assert oauth_metadata_request_1 .method == "GET"
316+
317+ # Send a 404 response
318+ oauth_metadata_response_1 = httpx .Response (
319+ 404 ,
320+ content = b"Not Found" ,
321+ request = oauth_metadata_request_1 ,
322+ )
323+
324+ # Next request should be to discover OAuth metadata at the next endpoint
325+ oauth_metadata_request_2 = await auth_flow .asend (oauth_metadata_response_1 )
326+ assert str (oauth_metadata_request_2 .url ) == "https://auth.example.com/.well-known/oauth-authorization-server"
327+ assert oauth_metadata_request_2 .method == "GET"
328+
329+ # Send a 400 response
330+ oauth_metadata_response_2 = httpx .Response (
331+ 400 ,
332+ content = b"Bad Request" ,
333+ request = oauth_metadata_request_2 ,
334+ )
335+
336+ # Next request should be to discover OAuth metadata at the next endpoint
337+ oauth_metadata_request_3 = await auth_flow .asend (oauth_metadata_response_2 )
338+ assert str (oauth_metadata_request_3 .url ) == "https://auth.example.com/.well-known/openid-configuration/v1/mcp"
339+ assert oauth_metadata_request_3 .method == "GET"
340+
341+ # Send a 500 response
342+ oauth_metadata_response_3 = httpx .Response (
343+ 500 ,
344+ content = b"Internal Server Error" ,
345+ request = oauth_metadata_request_3 ,
346+ )
347+
348+ # Mock the authorization process to minimize unnecessary state in this test
349+ oauth_provider ._perform_authorization = mock .AsyncMock (return_value = ("test_auth_code" , "test_code_verifier" ))
350+
351+ # Next request should fall back to legacy behavior and auth with the RS (mocked /authorize, next is /token)
352+ token_request = await auth_flow .asend (oauth_metadata_response_3 )
353+ assert str (token_request .url ) == "https://api.example.com/token"
354+ assert token_request .method == "POST"
355+
356+ # Send a successful token response
357+ token_response = httpx .Response (
358+ 200 ,
359+ content = (
360+ b'{"access_token": "new_access_token", "token_type": "Bearer", "expires_in": 3600, '
361+ b'"refresh_token": "new_refresh_token"}'
362+ ),
363+ request = token_request ,
364+ )
365+ token_request = await auth_flow .asend (token_response )
366+
264367 @pytest .mark .anyio
265368 async def test_handle_metadata_response_success (self , oauth_provider ):
266369 """Test successful metadata response handling."""
0 commit comments