google-adk-extras 0.2.6__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. google_adk_extras/__init__.py +3 -3
  2. google_adk_extras/adk_builder.py +15 -292
  3. google_adk_extras/artifacts/local_folder_artifact_service.py +0 -2
  4. google_adk_extras/artifacts/mongo_artifact_service.py +0 -1
  5. google_adk_extras/artifacts/s3_artifact_service.py +0 -1
  6. google_adk_extras/artifacts/sql_artifact_service.py +0 -1
  7. google_adk_extras/auth/__init__.py +10 -0
  8. google_adk_extras/auth/attach.py +227 -0
  9. google_adk_extras/auth/config.py +45 -0
  10. google_adk_extras/auth/jwt_utils.py +36 -0
  11. google_adk_extras/auth/sql_store.py +183 -0
  12. google_adk_extras/custom_agent_loader.py +1 -1
  13. google_adk_extras/enhanced_adk_web_server.py +0 -2
  14. google_adk_extras/enhanced_fastapi.py +6 -1
  15. google_adk_extras/memory/mongo_memory_service.py +0 -1
  16. google_adk_extras/memory/sql_memory_service.py +1 -1
  17. google_adk_extras/memory/yaml_file_memory_service.py +1 -3
  18. google_adk_extras/sessions/mongo_session_service.py +0 -1
  19. google_adk_extras/sessions/redis_session_service.py +1 -1
  20. google_adk_extras/sessions/yaml_file_session_service.py +0 -2
  21. google_adk_extras/streaming/streaming_controller.py +2 -2
  22. {google_adk_extras-0.2.6.dist-info → google_adk_extras-0.3.0.dist-info}/METADATA +84 -34
  23. google_adk_extras-0.3.0.dist-info/RECORD +37 -0
  24. google_adk_extras/credentials/__init__.py +0 -34
  25. google_adk_extras/credentials/github_oauth2_credential_service.py +0 -213
  26. google_adk_extras/credentials/google_oauth2_credential_service.py +0 -216
  27. google_adk_extras/credentials/http_basic_auth_credential_service.py +0 -388
  28. google_adk_extras/credentials/jwt_credential_service.py +0 -345
  29. google_adk_extras/credentials/microsoft_oauth2_credential_service.py +0 -250
  30. google_adk_extras/credentials/x_oauth2_credential_service.py +0 -240
  31. google_adk_extras-0.2.6.dist-info/RECORD +0 -39
  32. {google_adk_extras-0.2.6.dist-info → google_adk_extras-0.3.0.dist-info}/WHEEL +0 -0
  33. {google_adk_extras-0.2.6.dist-info → google_adk_extras-0.3.0.dist-info}/licenses/LICENSE +0 -0
  34. {google_adk_extras-0.2.6.dist-info → google_adk_extras-0.3.0.dist-info}/top_level.txt +0 -0
@@ -1,216 +0,0 @@
1
- """Google OAuth2 credential service implementation."""
2
-
3
- from typing import Optional, List
4
- import logging
5
-
6
- from google.adk.auth.credential_service.session_state_credential_service import SessionStateCredentialService
7
- from google.adk.auth.credential_service.base_credential_service import CallbackContext
8
- from google.adk.auth import AuthConfig, AuthCredential, AuthCredentialTypes
9
- from google.adk.auth.auth_credential import OAuth2Auth
10
- from fastapi.openapi.models import OAuth2
11
- from fastapi.openapi.models import OAuthFlowAuthorizationCode, OAuthFlows
12
-
13
- from .base_custom_credential_service import BaseCustomCredentialService
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class GoogleOAuth2CredentialService(BaseCustomCredentialService):
19
- """Google OAuth2 credential service for handling Google authentication flows.
20
-
21
- This service provides pre-configured OAuth2 flows for Google services including
22
- Gmail, Calendar, Drive, and other Google APIs.
23
-
24
- Args:
25
- client_id: The Google OAuth2 client ID from Google Cloud Console.
26
- client_secret: The Google OAuth2 client secret from Google Cloud Console.
27
- scopes: List of OAuth2 scopes to request. Common scopes include:
28
- - "openid" - OpenID Connect authentication
29
- - "email" - Access to email address
30
- - "profile" - Access to basic profile info
31
- - "https://www.googleapis.com/auth/calendar" - Google Calendar access
32
- - "https://www.googleapis.com/auth/gmail.readonly" - Gmail read access
33
- - "https://www.googleapis.com/auth/drive" - Google Drive access
34
- use_session_state: If True, stores credentials in session state. If False,
35
- uses in-memory storage. Default is True for persistence.
36
-
37
- Example:
38
- ```python
39
- credential_service = GoogleOAuth2CredentialService(
40
- client_id="your-client-id.apps.googleusercontent.com",
41
- client_secret="your-client-secret",
42
- scopes=["openid", "email", "https://www.googleapis.com/auth/calendar"]
43
- )
44
- await credential_service.initialize()
45
-
46
- # Use with Runner
47
- runner = Runner(
48
- agent=agent,
49
- session_service=session_service,
50
- credential_service=credential_service,
51
- app_name="my_app"
52
- )
53
- ```
54
- """
55
-
56
- # Google OAuth2 endpoints
57
- GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth"
58
- GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
59
-
60
- # Common Google OAuth2 scopes
61
- COMMON_SCOPES = {
62
- "openid": "OpenID Connect authentication",
63
- "email": "Access to email address",
64
- "profile": "Access to basic profile information",
65
- "calendar": "https://www.googleapis.com/auth/calendar",
66
- "calendar.readonly": "https://www.googleapis.com/auth/calendar.readonly",
67
- "gmail.readonly": "https://www.googleapis.com/auth/gmail.readonly",
68
- "gmail.modify": "https://www.googleapis.com/auth/gmail.modify",
69
- "drive": "https://www.googleapis.com/auth/drive",
70
- "drive.readonly": "https://www.googleapis.com/auth/drive.readonly",
71
- "cloud-platform": "https://www.googleapis.com/auth/cloud-platform",
72
- }
73
-
74
- def __init__(
75
- self,
76
- client_id: str,
77
- client_secret: str,
78
- scopes: Optional[List[str]] = None,
79
- use_session_state: bool = True
80
- ):
81
- """Initialize the Google OAuth2 credential service.
82
-
83
- Args:
84
- client_id: Google OAuth2 client ID.
85
- client_secret: Google OAuth2 client secret.
86
- scopes: List of OAuth2 scopes to request.
87
- use_session_state: Whether to use session state for credential storage.
88
- """
89
- super().__init__()
90
- self.client_id = client_id
91
- self.client_secret = client_secret
92
- self.scopes = scopes or ["openid", "email", "profile"]
93
- self.use_session_state = use_session_state
94
-
95
- # Resolve scope shortcuts to full URLs
96
- self._resolved_scopes = self._resolve_scopes(self.scopes)
97
-
98
- # Underlying credential service for storage
99
- if use_session_state:
100
- self._storage_service = SessionStateCredentialService()
101
- else:
102
- from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
103
- self._storage_service = InMemoryCredentialService()
104
-
105
- def _resolve_scopes(self, scopes: List[str]) -> List[str]:
106
- """Resolve scope shortcuts to full Google OAuth2 scope URLs.
107
-
108
- Args:
109
- scopes: List of scope names or URLs.
110
-
111
- Returns:
112
- List of resolved scope URLs.
113
- """
114
- resolved = []
115
- for scope in scopes:
116
- if scope in self.COMMON_SCOPES:
117
- resolved_scope = self.COMMON_SCOPES[scope]
118
- # If the value is a URL, use it; otherwise it's just a description
119
- if resolved_scope.startswith('https://'):
120
- resolved.append(resolved_scope)
121
- else:
122
- resolved.append(scope)
123
- else:
124
- resolved.append(scope)
125
- return resolved
126
-
127
- async def _initialize_impl(self) -> None:
128
- """Initialize the Google OAuth2 credential service.
129
-
130
- Validates the client credentials and sets up the OAuth2 auth scheme.
131
-
132
- Raises:
133
- ValueError: If client_id or client_secret is missing.
134
- """
135
- if not self.client_id:
136
- raise ValueError("Google OAuth2 client_id is required")
137
- if not self.client_secret:
138
- raise ValueError("Google OAuth2 client_secret is required")
139
- if not self._resolved_scopes:
140
- raise ValueError("At least one OAuth2 scope is required")
141
-
142
- logger.info(f"Initialized Google OAuth2 credential service with scopes: {self._resolved_scopes}")
143
-
144
- def create_auth_config(self) -> AuthConfig:
145
- """Create an AuthConfig for Google OAuth2 authentication.
146
-
147
- Returns:
148
- AuthConfig: Configured auth config for Google OAuth2 flow.
149
- """
150
- self._check_initialized()
151
-
152
- # Create OAuth2 auth scheme
153
- auth_scheme = OAuth2(
154
- flows=OAuthFlows(
155
- authorizationCode=OAuthFlowAuthorizationCode(
156
- authorizationUrl=self.GOOGLE_AUTH_URL,
157
- tokenUrl=self.GOOGLE_TOKEN_URL,
158
- scopes={scope: f"Access to {scope}" for scope in self._resolved_scopes}
159
- )
160
- )
161
- )
162
-
163
- # Create OAuth2 credential
164
- auth_credential = AuthCredential(
165
- auth_type=AuthCredentialTypes.OAUTH2,
166
- oauth2=OAuth2Auth(
167
- client_id=self.client_id,
168
- client_secret=self.client_secret
169
- )
170
- )
171
-
172
- return AuthConfig(
173
- auth_scheme=auth_scheme,
174
- raw_auth_credential=auth_credential
175
- )
176
-
177
- async def load_credential(
178
- self,
179
- auth_config: AuthConfig,
180
- callback_context: CallbackContext,
181
- ) -> Optional[AuthCredential]:
182
- """Load Google OAuth2 credential from storage.
183
-
184
- Args:
185
- auth_config: The auth config containing credential key information.
186
- callback_context: The current callback context.
187
-
188
- Returns:
189
- Optional[AuthCredential]: The stored credential or None if not found.
190
- """
191
- self._check_initialized()
192
- return await self._storage_service.load_credential(auth_config, callback_context)
193
-
194
- async def save_credential(
195
- self,
196
- auth_config: AuthConfig,
197
- callback_context: CallbackContext,
198
- ) -> None:
199
- """Save Google OAuth2 credential to storage.
200
-
201
- Args:
202
- auth_config: The auth config containing the credential to save.
203
- callback_context: The current callback context.
204
- """
205
- self._check_initialized()
206
- await self._storage_service.save_credential(auth_config, callback_context)
207
-
208
- logger.info(f"Saved Google OAuth2 credential for user {callback_context._invocation_context.user_id}")
209
-
210
- def get_supported_scopes(self) -> dict:
211
- """Get dictionary of supported Google OAuth2 scopes and their descriptions.
212
-
213
- Returns:
214
- dict: Mapping of scope names to descriptions.
215
- """
216
- return self.COMMON_SCOPES.copy()
@@ -1,388 +0,0 @@
1
- """HTTP Basic Auth credential service implementation."""
2
-
3
- from typing import Optional, Dict
4
- import logging
5
- import base64
6
-
7
- from google.adk.auth.credential_service.session_state_credential_service import SessionStateCredentialService
8
- from google.adk.auth.credential_service.base_credential_service import CallbackContext
9
- from google.adk.auth import AuthConfig, AuthCredential, AuthCredentialTypes
10
- from google.adk.auth.auth_credential import HttpAuth, HttpCredentials
11
- from fastapi.openapi.models import HTTPBase
12
-
13
- from .base_custom_credential_service import BaseCustomCredentialService
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class HTTPBasicAuthCredentialService(BaseCustomCredentialService):
19
- """HTTP Basic Auth credential service for username/password authentication.
20
-
21
- This service manages HTTP Basic Authentication credentials, encoding username
22
- and password combinations for API authentication.
23
-
24
- Args:
25
- username: The username for basic authentication.
26
- password: The password for basic authentication.
27
- realm: Optional realm parameter for HTTP Basic Auth.
28
- use_session_state: If True, stores credentials in session state. If False,
29
- uses in-memory storage. Default is True for persistence.
30
-
31
- Example:
32
- ```python
33
- credential_service = HTTPBasicAuthCredentialService(
34
- username="api_user",
35
- password="api_password",
36
- realm="API Access"
37
- )
38
- await credential_service.initialize()
39
-
40
- # Use with Runner
41
- runner = Runner(
42
- agent=agent,
43
- session_service=session_service,
44
- credential_service=credential_service,
45
- app_name="my_app"
46
- )
47
- ```
48
-
49
- Security Note:
50
- Basic Auth transmits credentials in base64 encoding, which is not encryption.
51
- Always use HTTPS when using Basic Auth to protect credentials in transit.
52
- """
53
-
54
- def __init__(
55
- self,
56
- username: str,
57
- password: str,
58
- realm: Optional[str] = None,
59
- use_session_state: bool = True
60
- ):
61
- """Initialize the HTTP Basic Auth credential service.
62
-
63
- Args:
64
- username: Username for basic authentication.
65
- password: Password for basic authentication.
66
- realm: Optional realm for HTTP Basic Auth.
67
- use_session_state: Whether to use session state for credential storage.
68
- """
69
- super().__init__()
70
- self.username = username
71
- self.password = password
72
- self.realm = realm
73
- self.use_session_state = use_session_state
74
-
75
- # Underlying credential service for storage
76
- if use_session_state:
77
- self._storage_service = SessionStateCredentialService()
78
- else:
79
- from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
80
- self._storage_service = InMemoryCredentialService()
81
-
82
- async def _initialize_impl(self) -> None:
83
- """Initialize the HTTP Basic Auth credential service.
84
-
85
- Validates the username and password configuration.
86
-
87
- Raises:
88
- ValueError: If username or password is missing.
89
- """
90
- if not self.username:
91
- raise ValueError("Username is required for HTTP Basic Auth")
92
- if not self.password:
93
- raise ValueError("Password is required for HTTP Basic Auth")
94
-
95
- logger.info(f"Initialized HTTP Basic Auth credential service for user: {self.username}")
96
-
97
- def encode_basic_auth(self, username: str, password: str) -> str:
98
- """Encode username and password for HTTP Basic Auth.
99
-
100
- Args:
101
- username: The username to encode.
102
- password: The password to encode.
103
-
104
- Returns:
105
- str: Base64 encoded credentials in format "Basic <encoded>".
106
- """
107
- credentials = f"{username}:{password}"
108
- encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('ascii')
109
- return f"Basic {encoded_credentials}"
110
-
111
- def decode_basic_auth(self, auth_header: str) -> tuple[str, str]:
112
- """Decode HTTP Basic Auth header to extract username and password.
113
-
114
- Args:
115
- auth_header: The Authorization header value.
116
-
117
- Returns:
118
- tuple[str, str]: Tuple of (username, password).
119
-
120
- Raises:
121
- ValueError: If the auth header is invalid.
122
- """
123
- if not auth_header.startswith("Basic "):
124
- raise ValueError("Invalid Basic Auth header format")
125
-
126
- encoded_credentials = auth_header[6:] # Remove "Basic " prefix
127
- try:
128
- credentials = base64.b64decode(encoded_credentials).decode('utf-8')
129
- username, password = credentials.split(':', 1)
130
- return username, password
131
- except Exception as e:
132
- raise ValueError(f"Failed to decode Basic Auth credentials: {e}")
133
-
134
- def create_auth_config(self) -> AuthConfig:
135
- """Create an AuthConfig for HTTP Basic Authentication.
136
-
137
- Returns:
138
- AuthConfig: Configured auth config for HTTP Basic Auth.
139
- """
140
- self._check_initialized()
141
-
142
- # Create HTTP Basic auth scheme
143
- auth_scheme = HTTPBase(scheme="basic")
144
-
145
- # Create HTTP Basic credential
146
- auth_credential = AuthCredential(
147
- auth_type=AuthCredentialTypes.HTTP,
148
- http=HttpAuth(
149
- scheme="basic",
150
- credentials=HttpCredentials(
151
- username=self.username,
152
- password=self.password
153
- )
154
- )
155
- )
156
-
157
- return AuthConfig(
158
- auth_scheme=auth_scheme,
159
- raw_auth_credential=auth_credential
160
- )
161
-
162
- async def load_credential(
163
- self,
164
- auth_config: AuthConfig,
165
- callback_context: CallbackContext,
166
- ) -> Optional[AuthCredential]:
167
- """Load HTTP Basic Auth credential from storage.
168
-
169
- Args:
170
- auth_config: The auth config containing credential key information.
171
- callback_context: The current callback context.
172
-
173
- Returns:
174
- Optional[AuthCredential]: The stored credential or None if not found.
175
- """
176
- self._check_initialized()
177
- return await self._storage_service.load_credential(auth_config, callback_context)
178
-
179
- async def save_credential(
180
- self,
181
- auth_config: AuthConfig,
182
- callback_context: CallbackContext,
183
- ) -> None:
184
- """Save HTTP Basic Auth credential to storage.
185
-
186
- Args:
187
- auth_config: The auth config containing the credential to save.
188
- callback_context: The current callback context.
189
- """
190
- self._check_initialized()
191
- await self._storage_service.save_credential(auth_config, callback_context)
192
-
193
- logger.info(f"Saved HTTP Basic Auth credential for user {callback_context._invocation_context.user_id}")
194
-
195
- def validate_credentials(self, test_username: str, test_password: str) -> bool:
196
- """Validate if provided credentials match the configured ones.
197
-
198
- Args:
199
- test_username: Username to validate.
200
- test_password: Password to validate.
201
-
202
- Returns:
203
- bool: True if credentials match, False otherwise.
204
- """
205
- self._check_initialized()
206
- return self.username == test_username and self.password == test_password
207
-
208
- def get_auth_header(self) -> str:
209
- """Get the Authorization header value for HTTP Basic Auth.
210
-
211
- Returns:
212
- str: The complete Authorization header value.
213
- """
214
- self._check_initialized()
215
- return self.encode_basic_auth(self.username, self.password)
216
-
217
- def get_credential_info(self) -> Dict[str, str]:
218
- """Get information about the configured credentials (without passwords).
219
-
220
- Returns:
221
- Dict[str, str]: Credential information (excluding sensitive data).
222
- """
223
- self._check_initialized()
224
-
225
- info = {
226
- "username": self.username,
227
- "auth_type": "HTTP Basic Auth",
228
- "password_set": bool(self.password)
229
- }
230
-
231
- if self.realm:
232
- info["realm"] = self.realm
233
-
234
- return info
235
-
236
-
237
- class HTTPBasicAuthWithCredentialsService(BaseCustomCredentialService):
238
- """HTTP Basic Auth service that accepts multiple username/password pairs.
239
-
240
- This variant allows managing multiple sets of credentials, useful for
241
- scenarios where different users or contexts require different credentials.
242
-
243
- Args:
244
- credentials: Dictionary mapping usernames to passwords.
245
- default_username: Default username to use if not specified.
246
- realm: Optional realm parameter for HTTP Basic Auth.
247
- use_session_state: If True, stores credentials in session state. If False,
248
- uses in-memory storage. Default is True for persistence.
249
-
250
- Example:
251
- ```python
252
- credential_service = HTTPBasicAuthWithCredentialsService(
253
- credentials={
254
- "admin": "admin_password",
255
- "user1": "user1_password",
256
- "api_client": "api_secret"
257
- },
258
- default_username="api_client"
259
- )
260
- await credential_service.initialize()
261
- ```
262
- """
263
-
264
- def __init__(
265
- self,
266
- credentials: Dict[str, str],
267
- default_username: Optional[str] = None,
268
- realm: Optional[str] = None,
269
- use_session_state: bool = True
270
- ):
271
- """Initialize the multi-credential HTTP Basic Auth service.
272
-
273
- Args:
274
- credentials: Dictionary of username -> password mappings.
275
- default_username: Default username to use.
276
- realm: Optional realm for HTTP Basic Auth.
277
- use_session_state: Whether to use session state for credential storage.
278
- """
279
- super().__init__()
280
- self.credentials = credentials.copy()
281
- self.default_username = default_username or (list(credentials.keys())[0] if credentials else None)
282
- self.realm = realm
283
- self.use_session_state = use_session_state
284
-
285
- # Underlying credential service for storage
286
- if use_session_state:
287
- self._storage_service = SessionStateCredentialService()
288
- else:
289
- from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
290
- self._storage_service = InMemoryCredentialService()
291
-
292
- async def _initialize_impl(self) -> None:
293
- """Initialize the multi-credential HTTP Basic Auth service.
294
-
295
- Validates that credentials are provided and default username exists.
296
-
297
- Raises:
298
- ValueError: If configuration is invalid.
299
- """
300
- if not self.credentials:
301
- raise ValueError("At least one username/password pair is required")
302
- if self.default_username and self.default_username not in self.credentials:
303
- raise ValueError(f"Default username '{self.default_username}' not found in credentials")
304
- if not self.default_username:
305
- raise ValueError("Default username is required when multiple credentials are provided")
306
-
307
- logger.info(f"Initialized HTTP Basic Auth service with {len(self.credentials)} credential sets")
308
-
309
- def create_auth_config(self, username: Optional[str] = None) -> AuthConfig:
310
- """Create an AuthConfig for HTTP Basic Authentication.
311
-
312
- Args:
313
- username: Username to use. If None, uses default_username.
314
-
315
- Returns:
316
- AuthConfig: Configured auth config for HTTP Basic Auth.
317
-
318
- Raises:
319
- ValueError: If username is not found in credentials.
320
- """
321
- self._check_initialized()
322
-
323
- username = username or self.default_username
324
- if username not in self.credentials:
325
- raise ValueError(f"Username '{username}' not found in credentials")
326
-
327
- password = self.credentials[username]
328
-
329
- # Create HTTP Basic auth scheme
330
- auth_scheme = HTTPBase(scheme="basic")
331
-
332
- # Create HTTP Basic credential
333
- auth_credential = AuthCredential(
334
- auth_type=AuthCredentialTypes.HTTP,
335
- http=HttpAuth(
336
- scheme="basic",
337
- credentials=HttpCredentials(
338
- username=username,
339
- password=password
340
- )
341
- )
342
- )
343
-
344
- return AuthConfig(
345
- auth_scheme=auth_scheme,
346
- raw_auth_credential=auth_credential
347
- )
348
-
349
- async def load_credential(
350
- self,
351
- auth_config: AuthConfig,
352
- callback_context: CallbackContext,
353
- ) -> Optional[AuthCredential]:
354
- """Load HTTP Basic Auth credential from storage.
355
-
356
- Args:
357
- auth_config: The auth config containing credential key information.
358
- callback_context: The current callback context.
359
-
360
- Returns:
361
- Optional[AuthCredential]: The stored credential or None if not found.
362
- """
363
- self._check_initialized()
364
- return await self._storage_service.load_credential(auth_config, callback_context)
365
-
366
- async def save_credential(
367
- self,
368
- auth_config: AuthConfig,
369
- callback_context: CallbackContext,
370
- ) -> None:
371
- """Save HTTP Basic Auth credential to storage.
372
-
373
- Args:
374
- auth_config: The auth config containing the credential to save.
375
- callback_context: The current callback context.
376
- """
377
- self._check_initialized()
378
- await self._storage_service.save_credential(auth_config, callback_context)
379
-
380
- logger.info(f"Saved HTTP Basic Auth credential for user {callback_context._invocation_context.user_id}")
381
-
382
- def get_available_usernames(self) -> list[str]:
383
- """Get list of available usernames.
384
-
385
- Returns:
386
- list[str]: List of configured usernames.
387
- """
388
- return list(self.credentials.keys())