Skip to content

Commit 6e0ea01

Browse files
google-genai-botcopybara-github
authored andcommitted
Added support for dynamic auth in integration connector tool
PiperOrigin-RevId: 759676602
1 parent 2f00626 commit 6e0ea01

File tree

5 files changed

+370
-10
lines changed

5 files changed

+370
-10
lines changed

src/google/adk/tools/application_integration_tool/application_integration_toolset.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import List
16-
from typing import Optional
17-
from typing import Union
15+
import logging
16+
from typing import List, Optional, Union
1817

1918
from fastapi.openapi.models import HTTPBearer
2019
from typing_extensions import override
@@ -24,6 +23,7 @@
2423
from ...auth.auth_credential import AuthCredentialTypes
2524
from ...auth.auth_credential import ServiceAccount
2625
from ...auth.auth_credential import ServiceAccountCredential
26+
from ...auth.auth_schemes import AuthScheme
2727
from ..base_toolset import BaseToolset
2828
from ..base_toolset import ToolPredicate
2929
from ..openapi_tool.auth.auth_helpers import service_account_scheme_credential
@@ -35,6 +35,9 @@
3535
from .integration_connector_tool import IntegrationConnectorTool
3636

3737

38+
logger = logging.getLogger(__name__)
39+
40+
3841
# TODO(cheliu): Apply a common toolset interface
3942
class ApplicationIntegrationToolset(BaseToolset):
4043
"""ApplicationIntegrationToolset generates tools from a given Application
@@ -93,6 +96,8 @@ def __init__(
9396
# tool/python function description.
9497
tool_instructions: Optional[str] = "",
9598
service_account_json: Optional[str] = None,
99+
auth_scheme: Optional[AuthScheme] = None,
100+
auth_credential: Optional[AuthCredential] = None,
96101
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
97102
):
98103
"""Args:
@@ -132,6 +137,8 @@ def __init__(
132137
self._tool_name_prefix = tool_name_prefix
133138
self._tool_instructions = tool_instructions
134139
self._service_account_json = service_account_json
140+
self._auth_scheme = auth_scheme
141+
self._auth_credential = auth_credential
135142
self.tool_filter = tool_filter
136143

137144
integration_client = IntegrationClient(
@@ -212,6 +219,27 @@ def _parse_spec_to_toolset(self, spec_dict, connection_details):
212219
rest_api_tool.configure_auth_scheme(auth_scheme)
213220
if auth_credential:
214221
rest_api_tool.configure_auth_credential(auth_credential)
222+
223+
auth_override_enabled = connection_details.get(
224+
"authOverrideEnabled", False
225+
)
226+
227+
if (
228+
self._auth_scheme
229+
and self._auth_credential
230+
and not auth_override_enabled
231+
):
232+
# Case: Auth provided, but override is OFF. Don't use provided auth.
233+
logger.warning(
234+
"Authentication schema and credentials are not used because"
235+
" authOverrideEnabled is not enabled in the connection."
236+
)
237+
connector_auth_scheme = None
238+
connector_auth_credential = None
239+
else:
240+
connector_auth_scheme = self._auth_scheme
241+
connector_auth_credential = self._auth_credential
242+
215243
self._tools.append(
216244
IntegrationConnectorTool(
217245
name=rest_api_tool.name,
@@ -223,6 +251,8 @@ def _parse_spec_to_toolset(self, spec_dict, connection_details):
223251
action=action,
224252
operation=operation,
225253
rest_api_tool=rest_api_tool,
254+
auth_scheme=connector_auth_scheme,
255+
auth_credential=connector_auth_credential,
226256
)
227257
)
228258

src/google/adk/tools/application_integration_tool/clients/connections_client.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,9 @@ def create_operation_request(entity: str) -> Dict[str, Any]:
554554
"serviceName": {"$ref": "#/components/schemas/serviceName"},
555555
"host": {"$ref": "#/components/schemas/host"},
556556
"entity": {"$ref": "#/components/schemas/entity"},
557+
"dynamicAuthConfig": {
558+
"$ref": "#/components/schemas/dynamicAuthConfig"
559+
},
557560
},
558561
}
559562

@@ -580,6 +583,9 @@ def update_operation_request(entity: str) -> Dict[str, Any]:
580583
"serviceName": {"$ref": "#/components/schemas/serviceName"},
581584
"host": {"$ref": "#/components/schemas/host"},
582585
"entity": {"$ref": "#/components/schemas/entity"},
586+
"dynamicAuthConfig": {
587+
"$ref": "#/components/schemas/dynamicAuthConfig"
588+
},
583589
"filterClause": {"$ref": "#/components/schemas/filterClause"},
584590
},
585591
}
@@ -603,6 +609,9 @@ def get_operation_request() -> Dict[str, Any]:
603609
"serviceName": {"$ref": "#/components/schemas/serviceName"},
604610
"host": {"$ref": "#/components/schemas/host"},
605611
"entity": {"$ref": "#/components/schemas/entity"},
612+
"dynamicAuthConfig": {
613+
"$ref": "#/components/schemas/dynamicAuthConfig"
614+
},
606615
},
607616
}
608617

@@ -625,6 +634,9 @@ def delete_operation_request() -> Dict[str, Any]:
625634
"serviceName": {"$ref": "#/components/schemas/serviceName"},
626635
"host": {"$ref": "#/components/schemas/host"},
627636
"entity": {"$ref": "#/components/schemas/entity"},
637+
"dynamicAuthConfig": {
638+
"$ref": "#/components/schemas/dynamicAuthConfig"
639+
},
628640
"filterClause": {"$ref": "#/components/schemas/filterClause"},
629641
},
630642
}
@@ -649,6 +661,9 @@ def list_operation_request() -> Dict[str, Any]:
649661
"serviceName": {"$ref": "#/components/schemas/serviceName"},
650662
"host": {"$ref": "#/components/schemas/host"},
651663
"entity": {"$ref": "#/components/schemas/entity"},
664+
"dynamicAuthConfig": {
665+
"$ref": "#/components/schemas/dynamicAuthConfig"
666+
},
652667
},
653668
}
654669

@@ -673,6 +688,9 @@ def action_request(action: str) -> Dict[str, Any]:
673688
"connectorInputPayload": {
674689
"$ref": f"#/components/schemas/connectorInputPayload_{action}"
675690
},
691+
"dynamicAuthConfig": {
692+
"$ref": "#/components/schemas/dynamicAuthConfig"
693+
},
676694
},
677695
}
678696

src/google/adk/tools/application_integration_tool/integration_connector_tool.py

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
1615
import logging
17-
from typing import Any
18-
from typing import Dict
19-
from typing import Optional
16+
from typing import Any, Dict, Optional, Union
2017

21-
from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
22-
from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
2318
from google.genai.types import FunctionDeclaration
2419
from typing_extensions import override
2520

21+
from ...auth.auth_credential import AuthCredential
22+
from ...auth.auth_schemes import AuthScheme
2623
from .. import BaseTool
24+
from ..openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
25+
from ..openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
26+
from ..openapi_tool.openapi_spec_parser.tool_auth_handler import ToolAuthHandler
2727
from ..tool_context import ToolContext
2828

29+
2930
logger = logging.getLogger(__name__)
3031

3132

@@ -56,6 +57,7 @@ class IntegrationConnectorTool(BaseTool):
5657
'entity',
5758
'operation',
5859
'action',
60+
'dynamic_auth_config',
5961
]
6062

6163
OPTIONAL_FIELDS = [
@@ -75,6 +77,8 @@ def __init__(
7577
operation: str,
7678
action: str,
7779
rest_api_tool: RestApiTool,
80+
auth_scheme: Optional[Union[AuthScheme, str]] = None,
81+
auth_credential: Optional[Union[AuthCredential, str]] = None,
7882
):
7983
"""Initializes the ApplicationIntegrationTool.
8084
@@ -108,6 +112,8 @@ def __init__(
108112
self._operation = operation
109113
self._action = action
110114
self._rest_api_tool = rest_api_tool
115+
self._auth_scheme = auth_scheme
116+
self._auth_credential = auth_credential
111117

112118
@override
113119
def _get_declaration(self) -> FunctionDeclaration:
@@ -126,10 +132,45 @@ def _get_declaration(self) -> FunctionDeclaration:
126132
)
127133
return function_decl
128134

135+
def _prepare_dynamic_euc(self, auth_credential: AuthCredential) -> str:
136+
if (
137+
auth_credential
138+
and auth_credential.http
139+
and auth_credential.http.credentials
140+
and auth_credential.http.credentials.token
141+
):
142+
return auth_credential.http.credentials.token
143+
return None
144+
129145
@override
130146
async def run_async(
131147
self, *, args: dict[str, Any], tool_context: Optional[ToolContext]
132148
) -> Dict[str, Any]:
149+
150+
tool_auth_handler = ToolAuthHandler.from_tool_context(
151+
tool_context, self._auth_scheme, self._auth_credential
152+
)
153+
auth_result = tool_auth_handler.prepare_auth_credentials()
154+
155+
if auth_result.state == 'pending':
156+
return {
157+
'pending': True,
158+
'message': 'Needs your authorization to access your data.',
159+
}
160+
161+
# Attach parameters from auth into main parameters list
162+
if auth_result.auth_credential:
163+
# Attach parameters from auth into main parameters list
164+
auth_credential_token = self._prepare_dynamic_euc(
165+
auth_result.auth_credential
166+
)
167+
if auth_credential_token:
168+
args['dynamic_auth_config'] = {
169+
'oauth2_auth_code_flow.access_token': auth_credential_token
170+
}
171+
else:
172+
args['dynamic_auth_config'] = {'oauth2_auth_code_flow.access_token': {}}
173+
133174
args['connection_name'] = self._connection_name
134175
args['service_name'] = self._connection_service_name
135176
args['host'] = self._connection_host

0 commit comments

Comments
 (0)