@@ -1299,20 +1299,27 @@ async def callback_handler() -> tuple[str, str | None]:
12991299
13001300 # Should try path-based PRM first
13011301 prm_request_1 = await auth_flow .asend (response )
1302- assert str (prm_request_1 .url ) == "https://mcp.linear.app/.well-known/oauth-protected-resource/sse "
1302+ assert str (prm_request_1 .url ) == "https://mcp.linear.app/sse/ .well-known/oauth-protected-resource"
13031303
13041304 # PRM returns 404
13051305 prm_response_1 = httpx .Response (404 , request = prm_request_1 )
13061306
1307- # Should try root -based PRM
1307+ # Should try path -based PRM first
13081308 prm_request_2 = await auth_flow .asend (prm_response_1 )
1309- assert str (prm_request_2 .url ) == "https://mcp.linear.app/.well-known/oauth-protected-resource"
1309+ assert str (prm_request_2 .url ) == "https://mcp.linear.app/.well-known/oauth-protected-resource/sse "
13101310
1311- # PRM returns 404 again - all PRM URLs failed
1311+ # PRM returns 404
13121312 prm_response_2 = httpx .Response (404 , request = prm_request_2 )
13131313
1314+ # Should try root-based PRM
1315+ prm_request_3 = await auth_flow .asend (prm_response_2 )
1316+ assert str (prm_request_3 .url ) == "https://mcp.linear.app/.well-known/oauth-protected-resource"
1317+
1318+ # PRM returns 404 again - all PRM URLs failed
1319+ prm_response_3 = httpx .Response (404 , request = prm_request_3 )
1320+
13141321 # Should fall back to root OAuth discovery (March 2025 spec behavior)
1315- oauth_metadata_request = await auth_flow .asend (prm_response_2 )
1322+ oauth_metadata_request = await auth_flow .asend (prm_response_3 )
13161323 assert str (oauth_metadata_request .url ) == "https://mcp.linear.app/.well-known/oauth-authorization-server"
13171324 assert oauth_metadata_request .method == "GET"
13181325
@@ -1407,20 +1414,27 @@ async def callback_handler() -> tuple[str, str | None]:
14071414
14081415 # Try path-based fallback
14091416 prm_request_2 = await auth_flow .asend (prm_response_1 )
1410- assert str (prm_request_2 .url ) == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp "
1417+ assert str (prm_request_2 .url ) == "https://api.example.com/v1/ .well-known/oauth-protected-resource"
14111418
14121419 # Returns 404
14131420 prm_response_2 = httpx .Response (404 , request = prm_request_2 )
14141421
1415- # Try root fallback
1422+ # Try path-based fallback
14161423 prm_request_3 = await auth_flow .asend (prm_response_2 )
1417- assert str (prm_request_3 .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
1424+ assert str (prm_request_3 .url ) == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp "
14181425
1419- # Also returns 404 - all PRM URLs failed
1426+ # Returns 404
14201427 prm_response_3 = httpx .Response (404 , request = prm_request_3 )
14211428
1429+ # Try root fallback
1430+ prm_request_4 = await auth_flow .asend (prm_response_3 )
1431+ assert str (prm_request_4 .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
1432+
1433+ # Also returns 404 - all PRM URLs failed
1434+ prm_response_4 = httpx .Response (404 , request = prm_request_4 )
1435+
14221436 # Should fall back to root OAuth discovery
1423- oauth_metadata_request = await auth_flow .asend (prm_response_3 )
1437+ oauth_metadata_request = await auth_flow .asend (prm_response_4 )
14241438 assert str (oauth_metadata_request .url ) == "https://api.example.com/.well-known/oauth-authorization-server"
14251439
14261440 # Complete the flow
@@ -1491,9 +1505,10 @@ async def callback_handler() -> tuple[str, str | None]:
14911505 )
14921506
14931507 # Should have path-based URL first, then root-based URL
1494- assert len (discovery_urls ) == 2
1495- assert discovery_urls [0 ] == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp"
1496- assert discovery_urls [1 ] == "https://api.example.com/.well-known/oauth-protected-resource"
1508+ assert len (discovery_urls ) == 3
1509+ assert discovery_urls [0 ] == "https://api.example.com/v1/.well-known/oauth-protected-resource"
1510+ assert discovery_urls [1 ] == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp"
1511+ assert discovery_urls [2 ] == "https://api.example.com/.well-known/oauth-protected-resource"
14971512
14981513 @pytest .mark .anyio
14991514 async def test_root_based_fallback_after_path_based_404 (
@@ -1539,33 +1554,41 @@ async def callback_handler() -> tuple[str, str | None]:
15391554 # Send a 401 response without WWW-Authenticate header
15401555 response = httpx .Response (401 , headers = {}, request = test_request )
15411556
1542- # Next request should be to discover protected resource metadata (path-based)
1557+ # Next request should be to discover protected resource metadata (mounted path-based)
15431558 discovery_request_1 = await auth_flow .asend (response )
1544- assert str (discovery_request_1 .url ) == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp "
1559+ assert str (discovery_request_1 .url ) == "https://api.example.com/v1/ .well-known/oauth-protected-resource"
15451560 assert discovery_request_1 .method == "GET"
15461561
15471562 # Send 404 response for path-based discovery
15481563 discovery_response_1 = httpx .Response (404 , request = discovery_request_1 )
15491564
1550- # Next request should be to root-based well-known URI
1565+ # Next request should be to discover protected resource metadata (path-based)
15511566 discovery_request_2 = await auth_flow .asend (discovery_response_1 )
1552- assert str (discovery_request_2 .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
1567+ assert str (discovery_request_2 .url ) == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp "
15531568 assert discovery_request_2 .method == "GET"
15541569
1570+ # Send 404 response for path-based discovery
1571+ discovery_response_2 = httpx .Response (404 , request = discovery_request_2 )
1572+
1573+ # Next request should be to root-based well-known URI
1574+ discovery_request_3 = await auth_flow .asend (discovery_response_2 )
1575+ assert str (discovery_request_3 .url ) == "https://api.example.com/.well-known/oauth-protected-resource"
1576+ assert discovery_request_3 .method == "GET"
1577+
15551578 # Send successful discovery response
1556- discovery_response_2 = httpx .Response (
1579+ discovery_response_3 = httpx .Response (
15571580 200 ,
15581581 content = (
15591582 b'{"resource": "https://api.example.com/v1/mcp", "authorization_servers": ["https://auth.example.com"]}'
15601583 ),
1561- request = discovery_request_2 ,
1584+ request = discovery_request_3 ,
15621585 )
15631586
15641587 # Mock the rest of the OAuth flow
15651588 provider ._perform_authorization = mock .AsyncMock (return_value = ("test_auth_code" , "test_code_verifier" ))
15661589
15671590 # Next should be OAuth metadata discovery
1568- oauth_metadata_request = await auth_flow .asend (discovery_response_2 )
1591+ oauth_metadata_request = await auth_flow .asend (discovery_response_3 )
15691592 assert oauth_metadata_request .method == "GET"
15701593
15711594 # Complete the flow
@@ -1631,10 +1654,11 @@ async def callback_handler() -> tuple[str, str | None]:
16311654 )
16321655
16331656 # Should have WWW-Authenticate URL first, then fallback URLs
1634- assert len (discovery_urls ) == 3
1657+ assert len (discovery_urls ) == 4
16351658 assert discovery_urls [0 ] == "https://custom.example.com/.well-known/oauth-protected-resource"
1636- assert discovery_urls [1 ] == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp"
1637- assert discovery_urls [2 ] == "https://api.example.com/.well-known/oauth-protected-resource"
1659+ assert discovery_urls [1 ] == "https://api.example.com/v1/.well-known/oauth-protected-resource"
1660+ assert discovery_urls [2 ] == "https://api.example.com/.well-known/oauth-protected-resource/v1/mcp"
1661+ assert discovery_urls [3 ] == "https://api.example.com/.well-known/oauth-protected-resource"
16381662
16391663
16401664class TestWWWAuthenticate :
0 commit comments