Skip to content

Commit 2f716ad

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: Allow more credentials types for BigQuery tools
This change accepts the `google.auth.credentials.Credentials` type for `BigQueryCredentialsConfig`, so any subclass of that, including `google.oauth2.credentials.Credentials` would work to integrate with BigQuery service. This opens up a whole range of possibilities, such as using service account credentials to deploy an agent using these tools. PiperOrigin-RevId: 773190440
1 parent 17beb32 commit 2f716ad

File tree

9 files changed

+155
-51
lines changed

9 files changed

+155
-51
lines changed

contributing/samples/bigquery/README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,28 @@ would set:
4040
### With Application Default Credentials
4141

4242
This mode is useful for quick development when the agent builder is the only
43-
user interacting with the agent. The tools are initialized with the default
44-
credentials present on the machine running the agent.
43+
user interacting with the agent. The tools are run with these credentials.
4544

4645
1. Create application default credentials on the machine where the agent would
4746
be running by following https://cloud.google.com/docs/authentication/provide-credentials-adc.
4847

49-
1. Set `RUN_WITH_ADC=True` in `agent.py` and run the agent
48+
1. Set `CREDENTIALS_TYPE=None` in `agent.py`
49+
50+
1. Run the agent
51+
52+
### With Service Account Keys
53+
54+
This mode is useful for quick development when the agent builder wants to run
55+
the agent with service account credentials. The tools are run with these
56+
credentials.
57+
58+
1. Create service account key by following https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys.
59+
60+
1. Set `CREDENTIALS_TYPE=AuthCredentialTypes.SERVICE_ACCOUNT` in `agent.py`
61+
62+
1. Download the key file and replace `"service_account_key.json"` with the path
63+
64+
1. Run the agent
5065

5166
### With Interactive OAuth
5267

@@ -72,7 +87,7 @@ type.
7287
Note: don't create a separate .env, instead put it to the same .env file that
7388
stores your Vertex AI or Dev ML credentials
7489

75-
1. Set `RUN_WITH_ADC=False` in `agent.py` and run the agent
90+
1. Set `CREDENTIALS_TYPE=AuthCredentialTypes.OAUTH2` in `agent.py` and run the agent
7691

7792
## Sample prompts
7893

contributing/samples/bigquery/agent.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,42 @@
1515
import os
1616

1717
from google.adk.agents import llm_agent
18+
from google.adk.auth import AuthCredentialTypes
1819
from google.adk.tools.bigquery import BigQueryCredentialsConfig
1920
from google.adk.tools.bigquery import BigQueryToolset
2021
from google.adk.tools.bigquery.config import BigQueryToolConfig
2122
from google.adk.tools.bigquery.config import WriteMode
2223
import google.auth
2324

24-
RUN_WITH_ADC = False
25+
# Define an appropriate credential type
26+
CREDENTIALS_TYPE = AuthCredentialTypes.OAUTH2
2527

2628

29+
# Define BigQuery tool config
2730
tool_config = BigQueryToolConfig(write_mode=WriteMode.ALLOWED)
2831

29-
if RUN_WITH_ADC:
30-
# Initialize the tools to use the application default credentials.
31-
application_default_credentials, _ = google.auth.default()
32-
credentials_config = BigQueryCredentialsConfig(
33-
credentials=application_default_credentials
34-
)
35-
else:
32+
if CREDENTIALS_TYPE == AuthCredentialTypes.OAUTH2:
3633
# Initiaze the tools to do interactive OAuth
3734
# The environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET
3835
# must be set
3936
credentials_config = BigQueryCredentialsConfig(
4037
client_id=os.getenv("OAUTH_CLIENT_ID"),
4138
client_secret=os.getenv("OAUTH_CLIENT_SECRET"),
4239
)
40+
elif CREDENTIALS_TYPE == AuthCredentialTypes.SERVICE_ACCOUNT:
41+
# Initialize the tools to use the credentials in the service account key.
42+
# If this flow is enabled, make sure to replace the file path with your own
43+
# service account key file
44+
# https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys
45+
creds, _ = google.auth.load_credentials_from_file("service_account_key.json")
46+
credentials_config = BigQueryCredentialsConfig(credentials=creds)
47+
else:
48+
# Initialize the tools to use the application default credentials.
49+
# https://cloud.google.com/docs/authentication/provide-credentials-adc
50+
application_default_credentials, _ = google.auth.default()
51+
credentials_config = BigQueryCredentialsConfig(
52+
credentials=application_default_credentials
53+
)
4354

4455
bigquery_toolset = BigQueryToolset(
4556
credentials_config=credentials_config, bigquery_tool_config=tool_config

src/google/adk/tools/bigquery/bigquery_credentials.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
from fastapi.openapi.models import OAuth2
2222
from fastapi.openapi.models import OAuthFlowAuthorizationCode
2323
from fastapi.openapi.models import OAuthFlows
24+
import google.auth.credentials
2425
from google.auth.exceptions import RefreshError
2526
from google.auth.transport.requests import Request
26-
from google.oauth2.credentials import Credentials
27+
import google.oauth2.credentials
2728
from pydantic import BaseModel
2829
from pydantic import model_validator
2930

@@ -40,26 +41,35 @@
4041

4142
@experimental
4243
class BigQueryCredentialsConfig(BaseModel):
43-
"""Configuration for Google API tools. (Experimental)"""
44+
"""Configuration for Google API tools (Experimental).
45+
46+
Please do not use this in production, as it may be deprecated later.
47+
"""
4448

4549
# Configure the model to allow arbitrary types like Credentials
4650
model_config = {"arbitrary_types_allowed": True}
4751

48-
credentials: Optional[Credentials] = None
49-
"""the existing oauth credentials to use. If set,this credential will be used
52+
credentials: Optional[google.auth.credentials.Credentials] = None
53+
"""The existing auth credentials to use. If set, this credential will be used
5054
for every end user, end users don't need to be involved in the oauthflow. This
5155
field is mutually exclusive with client_id, client_secret and scopes.
5256
Don't set this field unless you are sure this credential has the permission to
5357
access every end user's data.
5458
55-
Example usage: when the agent is deployed in Google Cloud environment and
59+
Example usage 1: When the agent is deployed in Google Cloud environment and
5660
the service account (used as application default credentials) has access to
5761
all the required BigQuery resource. Setting this credential to allow user to
5862
access the BigQuery resource without end users going through oauth flow.
5963
60-
To get application default credential: `google.auth.default(...)`. See more
64+
To get application default credential, use: `google.auth.default(...)`. See more
6165
details in https://cloud.google.com/docs/authentication/application-default-credentials.
6266
67+
Example usage 2: When the agent wants to access the user's BigQuery resources
68+
using the service account key credentials.
69+
70+
To load service account key credentials, use: `google.auth.load_credentials_from_file(...)`.
71+
See more details in https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys.
72+
6373
When the deployed environment cannot provide a pre-existing credential,
6474
consider setting below client_id, client_secret and scope for end users to go
6575
through oauth flow, so that agent can access the user data.
@@ -86,7 +96,9 @@ def __post_init__(self) -> BigQueryCredentialsConfig:
8696
" client_id/client_secret/scopes."
8797
)
8898

89-
if self.credentials:
99+
if self.credentials and isinstance(
100+
self.credentials, google.oauth2.credentials.Credentials
101+
):
90102
self.client_id = self.credentials.client_id
91103
self.client_secret = self.credentials.client_secret
92104
self.scopes = self.credentials.scopes
@@ -115,7 +127,7 @@ def __init__(self, credentials_config: BigQueryCredentialsConfig):
115127

116128
async def get_valid_credentials(
117129
self, tool_context: ToolContext
118-
) -> Optional[Credentials]:
130+
) -> Optional[google.auth.credentials.Credentials]:
119131
"""Get valid credentials, handling refresh and OAuth flow as needed.
120132
121133
Args:
@@ -127,7 +139,7 @@ async def get_valid_credentials(
127139
# First, try to get credentials from the tool context
128140
creds_json = tool_context.state.get(BIGQUERY_TOKEN_CACHE_KEY, None)
129141
creds = (
130-
Credentials.from_authorized_user_info(
142+
google.oauth2.credentials.Credentials.from_authorized_user_info(
131143
json.loads(creds_json), self.credentials_config.scopes
132144
)
133145
if creds_json
@@ -138,6 +150,11 @@ async def get_valid_credentials(
138150
if not creds:
139151
creds = self.credentials_config.credentials
140152

153+
# If non-oauth credentials are provided then use them as is. This helps
154+
# in flows such as service account keys
155+
if creds and not isinstance(creds, google.oauth2.credentials.Credentials):
156+
return creds
157+
141158
# Check if we have valid credentials
142159
if creds and creds.valid:
143160
return creds
@@ -159,7 +176,7 @@ async def get_valid_credentials(
159176

160177
async def _perform_oauth_flow(
161178
self, tool_context: ToolContext
162-
) -> Optional[Credentials]:
179+
) -> Optional[google.oauth2.credentials.Credentials]:
163180
"""Perform OAuth flow to get new credentials.
164181
165182
Args:
@@ -199,7 +216,7 @@ async def _perform_oauth_flow(
199216

200217
if auth_response:
201218
# OAuth flow completed, create credentials
202-
creds = Credentials(
219+
creds = google.oauth2.credentials.Credentials(
203220
token=auth_response.oauth2.access_token,
204221
refresh_token=auth_response.oauth2.refresh_token,
205222
token_uri=auth_scheme.flows.authorizationCode.tokenUrl,

src/google/adk/tools/bigquery/bigquery_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from typing import Callable
2020
from typing import Optional
2121

22-
from google.oauth2.credentials import Credentials
22+
from google.auth.credentials import Credentials
2323
from typing_extensions import override
2424

2525
from ...utils.feature_decorator import experimental

src/google/adk/tools/bigquery/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
from __future__ import annotations
1616

1717
import google.api_core.client_info
18+
from google.auth.credentials import Credentials
1819
from google.cloud import bigquery
19-
from google.oauth2.credentials import Credentials
2020

2121
from ... import version
2222

src/google/adk/tools/bigquery/metadata_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from google.auth.credentials import Credentials
1516
from google.cloud import bigquery
16-
from google.oauth2.credentials import Credentials
1717

1818
from . import client
1919

src/google/adk/tools/bigquery/query_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
import types
1717
from typing import Callable
1818

19+
from google.auth.credentials import Credentials
1920
from google.cloud import bigquery
20-
from google.oauth2.credentials import Credentials
2121

2222
from . import client
2323
from .config import BigQueryToolConfig

tests/unittests/tools/bigquery/test_bigquery_credentials.py

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

15-
from unittest.mock import Mock
15+
from unittest import mock
1616

1717
from google.adk.tools.bigquery.bigquery_credentials import BigQueryCredentialsConfig
1818
# Mock the Google OAuth and API dependencies
19-
from google.oauth2.credentials import Credentials
19+
import google.auth.credentials
20+
import google.oauth2.credentials
2021
import pytest
2122

2223

@@ -27,22 +28,46 @@ class TestBigQueryCredentials:
2728
either existing credentials or client ID/secret pairs are provided.
2829
"""
2930

30-
def test_valid_credentials_object(self):
31-
"""Test that providing valid Credentials object works correctly.
31+
def test_valid_credentials_object_auth_credentials(self):
32+
"""Test that providing valid Credentials object works correctly with
33+
google.auth.credentials.Credentials.
3234
3335
When a user already has valid OAuth credentials, they should be able
3436
to pass them directly without needing to provide client ID/secret.
3537
"""
36-
# Create a mock credentials object with the expected attributes
37-
mock_creds = Mock(spec=Credentials)
38-
mock_creds.client_id = "test_client_id"
39-
mock_creds.client_secret = "test_client_secret"
40-
mock_creds.scopes = ["https://www.googleapis.com/auth/calendar"]
38+
# Create a mock auth credentials object
39+
# auth_creds = google.auth.credentials.Credentials()
40+
auth_creds = mock.create_autospec(
41+
google.auth.credentials.Credentials, instance=True
42+
)
43+
44+
config = BigQueryCredentialsConfig(credentials=auth_creds)
45+
46+
# Verify that the credentials are properly stored and attributes are extracted
47+
assert config.credentials == auth_creds
48+
assert config.client_id is None
49+
assert config.client_secret is None
50+
assert config.scopes == ["https://www.googleapis.com/auth/bigquery"]
51+
52+
def test_valid_credentials_object_oauth2_credentials(self):
53+
"""Test that providing valid Credentials object works correctly with
54+
google.oauth2.credentials.Credentials.
55+
56+
When a user already has valid OAuth credentials, they should be able
57+
to pass them directly without needing to provide client ID/secret.
58+
"""
59+
# Create a mock oauth2 credentials object
60+
oauth2_creds = google.oauth2.credentials.Credentials(
61+
"test_token",
62+
client_id="test_client_id",
63+
client_secret="test_client_secret",
64+
scopes=["https://www.googleapis.com/auth/calendar"],
65+
)
4166

42-
config = BigQueryCredentialsConfig(credentials=mock_creds)
67+
config = BigQueryCredentialsConfig(credentials=oauth2_creds)
4368

4469
# Verify that the credentials are properly stored and attributes are extracted
45-
assert config.credentials == mock_creds
70+
assert config.credentials == oauth2_creds
4671
assert config.client_id == "test_client_id"
4772
assert config.client_secret == "test_client_secret"
4873
assert config.scopes == ["https://www.googleapis.com/auth/calendar"]

0 commit comments

Comments
 (0)