Skip to content

Commit 1445ad5

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: refresh credentials if token is missing in the common code and samples
PiperOrigin-RevId: 894473156
1 parent 19ac679 commit 1445ad5

File tree

4 files changed

+53
-5
lines changed

4 files changed

+53
-5
lines changed

contributing/samples/bigquery/agent.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from google.adk.tools.bigquery.config import BigQueryToolConfig
2222
from google.adk.tools.bigquery.config import WriteMode
2323
import google.auth
24+
import google.auth.transport.requests
2425

2526
# Define the desired credential type.
2627
# By default use Application Default Credentials (ADC) from the local
@@ -57,6 +58,8 @@
5758
# service account key file
5859
# https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys
5960
creds, _ = google.auth.load_credentials_from_file("service_account_key.json")
61+
if not creds.valid:
62+
creds.refresh(google.auth.transport.requests.Request())
6063
credentials_config = BigQueryCredentialsConfig(credentials=creds)
6164
elif CREDENTIALS_TYPE == AuthCredentialTypes.HTTP:
6265
# Initialize the tools to use the externally provided access token. One such
@@ -73,6 +76,10 @@
7376
# Initialize the tools to use the application default credentials.
7477
# https://cloud.google.com/docs/authentication/provide-credentials-adc
7578
application_default_credentials, _ = google.auth.default()
79+
if not application_default_credentials.valid:
80+
application_default_credentials.refresh(
81+
google.auth.transport.requests.Request()
82+
)
7683
credentials_config = BigQueryCredentialsConfig(
7784
credentials=application_default_credentials
7885
)

contributing/samples/data_agent/agent.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
# Initialize the tools to use the application default credentials.
5252
# https://cloud.google.com/docs/authentication/provide-credentials-adc
5353
application_default_credentials, _ = google.auth.default()
54+
if not application_default_credentials.valid:
55+
application_default_credentials.refresh(
56+
google.auth.transport.requests.Request()
57+
)
5458
credentials_config = DataAgentCredentialsConfig(
5559
credentials=application_default_credentials
5660
)

src/google/adk/tools/_google_credentials.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ async def get_valid_credentials(
191191
# If non-oauth credentials are provided then use them as is. This helps
192192
# in flows such as service account keys
193193
if creds and not isinstance(creds, google.oauth2.credentials.Credentials):
194+
if not creds.valid:
195+
try:
196+
creds.refresh(Request())
197+
except Exception: # pylint: disable=broad-except
198+
# If refresh fails, we still return the creds as they might work
199+
# for some libraries that handle refresh internally.
200+
pass
194201
return creds
195202

196203
# Check if we have valid credentials

tests/unittests/tools/test_base_google_credentials_manager.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,26 +105,56 @@ async def test_get_valid_credentials_with_valid_existing_creds(
105105
],
106106
)
107107
@pytest.mark.asyncio
108+
@patch.object(_google_credentials, "Request", autospec=True)
108109
async def test_get_valid_credentials_with_existing_non_oauth_creds(
109-
self, manager, mock_tool_context, valid
110+
self, mock_request_class, manager, mock_tool_context, valid
110111
):
111-
"""Test that existing non-oauth credentials are returned immediately.
112+
"""Test that existing non-oauth credentials handle refresh logic correctly.
112113
113-
When credentials are of non-oauth type, no refresh or OAuth flow
114-
is triggered irrespective of whether or not it is valid.
114+
When credentials are of non-oauth type, a refresh is triggered if they
115+
are invalid. No OAuth flow is ever triggered.
115116
"""
116-
# Create mock credentials that are already valid
117+
# Create mock credentials with the specified validity
117118
mock_creds = create_autospec(AuthCredentials, instance=True)
118119
mock_creds.valid = valid
119120
manager.credentials_config.credentials = mock_creds
120121

121122
result = await manager.get_valid_credentials(mock_tool_context)
122123

123124
assert result == mock_creds
125+
# Verify refresh behavior
126+
if valid:
127+
mock_creds.refresh.assert_not_called()
128+
else:
129+
mock_creds.refresh.assert_called_once_with(
130+
mock_request_class.return_value
131+
)
132+
124133
# Verify no OAuth flow was triggered
125134
mock_tool_context.get_auth_response.assert_not_called()
126135
mock_tool_context.request_credential.assert_not_called()
127136

137+
@pytest.mark.asyncio
138+
@patch.object(_google_credentials, "Request", autospec=True)
139+
async def test_get_valid_credentials_with_non_oauth_refresh_failure(
140+
self, mock_request_class, manager, mock_tool_context
141+
):
142+
"""Test that non-oauth refresh failures are caught gracefully.
143+
144+
Even if refresh fails, we should still return the credentials as they
145+
might work for some downstream libraries.
146+
"""
147+
mock_creds = create_autospec(AuthCredentials, instance=True)
148+
mock_creds.valid = False
149+
mock_creds.refresh.side_effect = Exception("Refresh failed")
150+
manager.credentials_config.credentials = mock_creds
151+
152+
result = await manager.get_valid_credentials(mock_tool_context)
153+
154+
# Credentials should still be returned
155+
assert result == mock_creds
156+
mock_creds.refresh.assert_called_once_with(mock_request_class.return_value)
157+
128158
@pytest.mark.asyncio
129159
async def test_get_credentials_from_cache_when_none_in_manager(
130160
self, manager, mock_tool_context

0 commit comments

Comments
 (0)