Skip to content

Commit 08db89d

Browse files
committed
chore(ve_identity): Make OAuth2 flow and scopes parameters optional
Updated OAuth2-related classes and functions to make 'auth_flow' and 'scopes' parameters optional, allowing control plane defaults to be used if not provided.
1 parent f0d5941 commit 08db89d

File tree

8 files changed

+65
-89
lines changed

8 files changed

+65
-89
lines changed

tests/test_ve_identity_auth_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def on_auth_url_callback(url: str):
103103

104104
def test_oauth2_auth_empty_scopes(self):
105105
"""Test that empty scopes raises ValueError."""
106-
with pytest.raises(ValueError, match="scopes cannot be empty"):
106+
with pytest.raises(ValueError, match="scopes cannot be an empty list"):
107107
oauth2_auth(
108108
provider_name="github",
109109
scopes=[],

veadk/agent.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,13 @@ async def run(
298298
final_output = ""
299299
for _prompt in prompt:
300300
message = types.Content(role="user", parts=[types.Part(text=_prompt)])
301-
final_output = await self._run(
302-
runner, user_id, session_id, message, stream, auth_request_processor
303-
)
301+
final_output = await self._run(runner, user_id, session_id, message, stream, auth_request_processor)
304302

305303
# VeADK features
306304
if save_session_to_memory:
307-
assert (
308-
self.long_term_memory is not None
309-
), "Long-term memory is not initialized in agent"
305+
assert self.long_term_memory is not None, (
306+
"Long-term memory is not initialized in agent"
307+
)
310308
session = await session_service.get_session(
311309
app_name=app_name,
312310
user_id=user_id,

veadk/integrations/ve_identity/auth_config.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from abc import ABC, abstractmethod
2222
from typing import Any, Callable, List, Literal, Optional, Union
2323

24-
from pydantic import BaseModel, model_validator, field_validator
24+
from pydantic import BaseModel, field_validator
2525

2626
from veadk.integrations.ve_identity.models import OAuth2AuthPoller
2727
from veadk.integrations.ve_identity.identity_client import IdentityClient
@@ -35,6 +35,7 @@ def _get_default_region() -> str:
3535
"""
3636
try:
3737
from veadk.config import settings
38+
3839
return settings.veidentity.region
3940
except Exception:
4041
# Fallback to default if config loading fails
@@ -52,8 +53,8 @@ class AuthConfig(BaseModel, ABC):
5253

5354
def __init__(self, **data):
5455
"""Initialize AuthConfig with default region from VeADK config if not provided."""
55-
if 'region' not in data or data['region'] is None:
56-
data['region'] = _get_default_region()
56+
if "region" not in data or data["region"] is None:
57+
data["region"] = _get_default_region()
5758
super().__init__(**data)
5859

5960
@field_validator("provider_name")
@@ -82,10 +83,10 @@ def auth_type(self) -> str:
8283
class OAuth2AuthConfig(AuthConfig):
8384
"""OAuth2 authentication configuration."""
8485

85-
# Required fields
86-
scopes: List[str]
87-
auth_flow: Literal["M2M", "USER_FEDERATION"]
88-
# Optional fields
86+
# Optional fields - control plane will use defaults if not provided
87+
scopes: Optional[List[str]] = None
88+
auth_flow: Optional[Literal["M2M", "USER_FEDERATION"]] = None
89+
# Additional optional fields
8990
callback_url: Optional[str] = None
9091
force_authentication: bool = False
9192
response_for_auth_required: Optional[Union[dict, str]] = None
@@ -94,10 +95,18 @@ class OAuth2AuthConfig(AuthConfig):
9495

9596
@field_validator("scopes")
9697
@classmethod
97-
def validate_scopes_not_empty(cls, v: List[str]) -> List[str]:
98-
"""Validate that scopes list is not empty and contains valid scope strings."""
98+
def validate_scopes_not_empty(cls, v: Optional[List[str]]) -> Optional[List[str]]:
99+
"""Validate that scopes list is not empty and contains valid scope strings.
100+
101+
If scopes is None, the control plane will use default scopes.
102+
"""
103+
if v is None:
104+
return None
105+
99106
if not v:
100-
raise ValueError("scopes cannot be empty")
107+
raise ValueError(
108+
"scopes cannot be an empty list; use None to use control plane defaults"
109+
)
101110

102111
# Validate each scope is not empty
103112
for scope in v:
@@ -128,15 +137,6 @@ def validate_callback_url(cls, v: Optional[str]) -> Optional[str]:
128137
raise ValueError("callback_url must be a valid HTTP/HTTPS URL")
129138
return v
130139

131-
@model_validator(mode="after")
132-
def _validate_required_fields(self):
133-
"""Validate required fields."""
134-
if not self.scopes:
135-
raise ValueError("scopes is required for OAuth2AuthConfig")
136-
if not self.auth_flow:
137-
raise ValueError("auth_flow is required for OAuth2AuthConfig")
138-
return self
139-
140140
@property
141141
def auth_type(self) -> str:
142142
return "oauth2"
@@ -201,8 +201,8 @@ def workload_auth(
201201

202202
def oauth2_auth(
203203
provider_name: str,
204-
scopes: List[str],
205-
auth_flow: Literal["M2M", "USER_FEDERATION"],
204+
scopes: Optional[List[str]] = None,
205+
auth_flow: Optional[Literal["M2M", "USER_FEDERATION"]] = None,
206206
callback_url: Optional[str] = None,
207207
force_authentication: bool = False,
208208
response_for_auth_required: Optional[Union[dict, str]] = None,
@@ -215,8 +215,10 @@ def oauth2_auth(
215215
216216
Args:
217217
provider_name: Name of the credential provider.
218-
scopes: List of OAuth2 scopes.
219-
auth_flow: Authentication flow type ("M2M" or "USER_FEDERATION").
218+
scopes: Optional list of OAuth2 scopes. If not provided, the control plane
219+
will use the default configured scopes for the provider.
220+
auth_flow: Optional authentication flow type ("M2M" or "USER_FEDERATION").
221+
If not provided, the control plane will use the default configured flow.
220222
callback_url: Optional callback URL for OAuth2.
221223
force_authentication: Whether to force authentication.
222224
response_for_auth_required: Response to return when auth is required.

veadk/integrations/ve_identity/auth_mixins.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ class OAuth2AuthMixin(BaseAuthMixin):
343343
def __init__(
344344
self,
345345
*,
346-
scopes: List[str],
347-
auth_flow: Literal["M2M", "USER_FEDERATION"],
346+
scopes: Optional[List[str]] = None,
347+
auth_flow: Optional[Literal["M2M", "USER_FEDERATION"]] = None,
348348
callback_url: Optional[str] = None,
349349
force_authentication: bool = False,
350350
response_for_auth_required: Optional[str] = None,
@@ -356,9 +356,11 @@ def __init__(
356356
"""Initialize the OAuth2 authentication mixin.
357357
358358
Args:
359-
scopes: List of OAuth2 scopes to request.
360-
auth_flow: Authentication flow type - "M2M" for machine-to-machine or
361-
"USER_FEDERATION" for user-delegated access.
359+
scopes: Optional list of OAuth2 scopes to request. If not provided,
360+
the control plane will use the default configured scopes.
361+
auth_flow: Optional authentication flow type - "M2M" for machine-to-machine or
362+
"USER_FEDERATION" for user-delegated access. If not provided,
363+
the control plane will use the default configured flow.
362364
callback_url: OAuth2 redirect URL (must be pre-registered).
363365
force_authentication: If True, forces re-authentication even if cached token exists.
364366
response_for_auth_required: Custom response to return when user authorization is needed.

veadk/integrations/ve_identity/auth_processor.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -230,21 +230,19 @@ async def process_auth_request(
230230
on_auth_url(auth_uri)
231231

232232
# Use custom poller or default poller
233-
# Create async token fetcher for default poller
234-
async def async_token_fetcher():
235-
response = self._identity_client.get_oauth2_token_or_auth_url(
236-
**request_dict
237-
)
238-
return (
239-
OAuth2Auth(access_token=response.access_token)
240-
if response.access_token and response.access_token.strip()
241-
else None
242-
)
243-
244233
active_poller = (
245234
self.config.oauth2_auth_poller(auth_uri, request_dict)
246235
if self.config.oauth2_auth_poller
247-
else _DefaultOauth2AuthPoller(auth_uri, async_token_fetcher)
236+
else _DefaultOauth2AuthPoller(
237+
auth_uri,
238+
lambda: (
239+
lambda response: (
240+
OAuth2Auth(access_token=response.access_token)
241+
if response.access_token and response.access_token.strip()
242+
else None
243+
)
244+
)(self._identity_client.get_oauth2_token_or_auth_url(**request_dict)),
245+
)
248246
)
249247

250248
# Poll for the oauth2 auth

veadk/integrations/ve_identity/identity_client.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ def get_oauth2_token_or_auth_url(
276276
*,
277277
provider_name: str,
278278
agent_identity_token: str,
279-
auth_flow: Literal["M2M", "USER_FEDERATION"],
279+
auth_flow: Optional[Literal["M2M", "USER_FEDERATION"]] = None,
280280
scopes: Optional[List[str]] = None,
281281
callback_url: Optional[str] = None,
282282
force_authentication: bool = False,
@@ -291,9 +291,11 @@ def get_oauth2_token_or_auth_url(
291291
Args:
292292
provider_name: Name of the credential provider configured in the identity service.
293293
agent_identity_token: Agent's workload access token for authentication.
294-
auth_flow: OAuth2 flow type - "M2M" for machine-to-machine or
295-
"USER_FEDERATION" for user-delegated access.
296-
scopes: Optional list of OAuth2 scopes to request.
294+
auth_flow: Optional OAuth2 flow type - "M2M" for machine-to-machine or
295+
"USER_FEDERATION" for user-delegated access. If not provided,
296+
the control plane will use the default configured value.
297+
scopes: Optional list of OAuth2 scopes to request. If not provided,
298+
the control plane will use the default configured scopes.
297299
callback_url: OAuth2 redirect URL (must be pre-registered with the provider).
298300
force_authentication: If True, forces re-authentication even if a valid
299301
token exists in the token vault.

veadk/integrations/ve_identity/mcp_tool.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,14 @@
1515
from __future__ import annotations
1616

1717
from typing import Any
18-
1918
from typing_extensions import override
2019

20+
from mcp.types import Tool as McpBaseTool
2121
from google.genai.types import FunctionDeclaration
2222
from google.adk.auth.auth_credential import AuthCredential
2323
from google.adk.tools.base_tool import BaseTool
2424
from google.adk.tools.tool_context import ToolContext
2525
from google.adk.tools._gemini_schema_util import _to_gemini_schema
26-
27-
# Attempt to import MCP Tool from the MCP library
28-
try:
29-
from mcp.types import Tool as McpBaseTool
30-
except ImportError as e:
31-
import sys
32-
33-
if sys.version_info < (3, 10):
34-
raise ImportError(
35-
"MCP Tool requires Python 3.10 or above. Please upgrade your Python"
36-
" version."
37-
) from e
38-
else:
39-
raise e
40-
4126
from google.adk.tools.mcp_tool.mcp_session_manager import MCPSessionManager
4227
from google.adk.tools.mcp_tool.mcp_session_manager import retry_on_closed_resource
4328

@@ -47,7 +32,6 @@
4732
AuthRequiredException,
4833
)
4934
from veadk.integrations.ve_identity.utils import generate_headers
50-
5135
from veadk.utils.logger import get_logger
5236

5337
logger = get_logger(__name__)

veadk/integrations/ve_identity/mcp_toolset.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,8 @@
2525
from pydantic import model_validator, field_validator
2626
from typing_extensions import override
2727

28-
from veadk.integrations.ve_identity.auth_config import VeIdentityAuthConfig
29-
from veadk.integrations.ve_identity.auth_mixins import VeIdentityAuthMixin
30-
from veadk.integrations.ve_identity.mcp_tool import VeIdentityMcpTool
31-
from veadk.integrations.ve_identity.utils import generate_headers
32-
33-
# Attempt to import MCP Tool from the MCP library, and hints user to upgrade
34-
# their Python version to 3.10 if it fails.
35-
try:
36-
from mcp import StdioServerParameters, ClientSession
37-
from mcp.types import ListToolsResult
38-
except ImportError as e:
39-
import sys
40-
41-
if sys.version_info < (3, 10):
42-
raise ImportError(
43-
"MCP Tool requires Python 3.10 or above. Please upgrade your Python"
44-
" version."
45-
) from e
46-
else:
47-
raise e
28+
from mcp import StdioServerParameters, ClientSession
29+
from mcp.types import ListToolsResult
4830

4931
from google.adk.tools.mcp_tool.mcp_session_manager import (
5032
retry_on_closed_resource,
@@ -58,6 +40,11 @@
5840
from google.adk.tools.tool_configs import ToolArgsConfig, BaseToolConfig
5941
from google.adk.agents.readonly_context import ReadonlyContext
6042

43+
from veadk.integrations.ve_identity.auth_config import VeIdentityAuthConfig
44+
from veadk.integrations.ve_identity.auth_mixins import VeIdentityAuthMixin
45+
from veadk.integrations.ve_identity.mcp_tool import VeIdentityMcpTool
46+
from veadk.integrations.ve_identity.utils import generate_headers
47+
6148
logger = logging.getLogger(__name__)
6249

6350

@@ -388,7 +375,10 @@ class VeIdentityMcpToolsetConfig(BaseToolConfig):
388375
def _validate_auth_config(cls, v):
389376
"""Convert dict to proper auth config object."""
390377
if isinstance(v, dict):
391-
from veadk.integrations.ve_identity.auth_config import api_key_auth, oauth2_auth
378+
from veadk.integrations.ve_identity.auth_config import (
379+
api_key_auth,
380+
oauth2_auth,
381+
)
392382

393383
provider_name = v.get("provider_name")
394384
if not provider_name:

0 commit comments

Comments
 (0)