Skip to content

Commit 83f9817

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: Raise a clear actionable error when CustomAuthScheme lacks a registered AuthProvider
PiperOrigin-RevId: 911115744
1 parent e5fa990 commit 83f9817

2 files changed

Lines changed: 54 additions & 20 deletions

File tree

src/google/adk/auth/credential_manager.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -183,26 +183,34 @@ async def get_auth_credential(
183183
) -> Optional[AuthCredential]:
184184
"""Load and prepare authentication credential through a structured workflow."""
185185

186-
# Pydantic may have deserialized an unknown scheme into a generic
187-
# CustomAuthScheme. If so, rehydrate it first into a specific subclass.
188-
# Note: Custom authentication scheme classes must have been imported into
189-
# the Python runtime before get_auth_credential is called for their
190-
# subclasses to be registered. This is fine as developer will anyway import
191-
# them while registering the auth providers.
192-
# Note: `__subclasses__()` only returns immediate subclasses, if there is a
193-
# subclass of a subclass of CustomAuthScheme then it will not be returned.
194-
# pylint: disable=unidiomatic-typecheck Needs exact class matching.
195-
if type(self._auth_config.auth_scheme) is CustomAuthScheme:
196-
self._auth_config.auth_scheme = _rehydrate_custom_scheme(
197-
self._auth_config.auth_scheme,
198-
CustomAuthScheme.__subclasses__(),
186+
# Step 0: Handle CustomAuthScheme if present
187+
if isinstance(self._auth_config.auth_scheme, CustomAuthScheme):
188+
# Pydantic may have deserialized an unknown scheme into a generic
189+
# CustomAuthScheme. If so, rehydrate it first into a specific subclass.
190+
# Note: Custom authentication scheme classes must have been imported into
191+
# the Python runtime before get_auth_credential is called for their
192+
# subclasses to be registered. This is fine as developer will anyway
193+
# import them while registering the auth providers.
194+
# Note: `__subclasses__()` only returns immediate subclasses, if there is
195+
# a subclass of a subclass of CustomAuthScheme then it will not be
196+
# returned.
197+
# pylint: disable=unidiomatic-typecheck Needs exact class matching.
198+
if type(self._auth_config.auth_scheme) is CustomAuthScheme:
199+
self._auth_config.auth_scheme = _rehydrate_custom_scheme(
200+
self._auth_config.auth_scheme,
201+
CustomAuthScheme.__subclasses__(),
202+
)
203+
204+
provider = self._auth_provider_registry.get_provider(
205+
self._auth_config.auth_scheme
199206
)
200-
# First, check if a registered auth provider is available before attempting
201-
# to retrieve tokens natively.
202-
provider = self._auth_provider_registry.get_provider(
203-
self._auth_config.auth_scheme
204-
)
205-
if provider:
207+
if provider is None:
208+
raise ValueError(
209+
"No auth provider registered for custom auth scheme "
210+
f"{self._auth_config.auth_scheme.type_!r}. "
211+
"Register it using `CredentialManager.register_auth_provider("
212+
"<YourAuthProviderInstance>)`."
213+
)
206214
provided_credential = await provider.get_auth_credential(
207215
self._auth_config, context
208216
)

tests/unittests/auth/test_credential_manager.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from .. import testing_utils
4949

5050

51-
class DummyAuthScheme(SecurityBase):
51+
class DummyAuthScheme(CustomAuthScheme):
5252
"""A custom auth scheme for testing pluggable auth providers."""
5353

5454
type_: str = "dummy_auth_scheme"
@@ -1122,3 +1122,29 @@ def test_rehydrate_custom_scheme_failure(self):
11221122
_rehydrate_custom_scheme(
11231123
scheme=custom_scheme, supported_schemes=[DummyAuthScheme]
11241124
)
1125+
1126+
@pytest.mark.asyncio
1127+
async def test_get_auth_credential_raises_error_when_no_provider_registered(
1128+
self, mocker
1129+
):
1130+
"""Test that a ValueError is raised when no provider is registered for a CustomAuthScheme."""
1131+
1132+
class DummyCustomScheme(CustomAuthScheme):
1133+
type_: str = "dummy_custom_auth_scheme"
1134+
1135+
auth_config = mocker.Mock(spec=AuthConfig, instance=True)
1136+
auth_config.auth_scheme = DummyCustomScheme()
1137+
1138+
manager = CredentialManager(auth_config)
1139+
1140+
with pytest.raises(
1141+
ValueError,
1142+
match=(
1143+
r"No auth provider registered for custom auth scheme "
1144+
r"'dummy_custom_auth_scheme'\. "
1145+
r"Register it using `CredentialManager\.register_auth_provider\("
1146+
),
1147+
):
1148+
await manager.get_auth_credential(
1149+
mocker.Mock(spec=CallbackContext, instance=True)
1150+
)

0 commit comments

Comments
 (0)