diff --git a/src/google/adk/auth/oauth2_discovery.py b/src/google/adk/auth/oauth2_discovery.py index ef509102a1..e9220e23ea 100644 --- a/src/google/adk/auth/oauth2_discovery.py +++ b/src/google/adk/auth/oauth2_discovery.py @@ -89,7 +89,7 @@ async def discover_auth_server_metadata( response.raise_for_status() metadata = AuthorizationServerMetadata.model_validate(response.json()) # Validate issuer to defend against MIX-UP attacks - if metadata.issuer == issuer_url.rstrip("/"): + if metadata.issuer.rstrip("/") == issuer_url.rstrip("/"): return metadata else: logger.warning( diff --git a/tests/unittests/auth/test_oauth2_discovery.py b/tests/unittests/auth/test_oauth2_discovery.py index 48d755337d..88a87cd8c3 100644 --- a/tests/unittests/auth/test_oauth2_discovery.py +++ b/tests/unittests/auth/test_oauth2_discovery.py @@ -208,6 +208,29 @@ async def test_discover_auth_server_metadata_discard_mismatched_issuer( ), ]) + @patch("httpx.AsyncClient.get") + @pytest.mark.asyncio + async def test_discover_auth_server_metadata_issuer_trailing_slash( + self, + mock_get, + auth_server_metadata, + ): + """Test issuer match tolerates a trailing slash on the returned issuer. + + Some servers (e.g. FastMCP with GoogleProvider) return an issuer with a + trailing slash even though the requested issuer_url has none. + """ + + auth_server_metadata.issuer = "https://auth.example.com/" + mock_get.side_effect = [ + self.mock_success_response(auth_server_metadata), + ] + discovery_manager = OAuth2DiscoveryManager() + result = await discovery_manager.discover_auth_server_metadata( + "https://auth.example.com" + ) + assert result == auth_server_metadata + @patch("httpx.AsyncClient.get") @pytest.mark.asyncio async def test_discover_resource_metadata_failed(