-
Notifications
You must be signed in to change notification settings - Fork 2.6k
fix: oauth refresh not triggered on token expiry #3767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: oauth refresh not triggered on token expiry #3767
Conversation
Summary of ChangesHello @MarlzRana, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses an issue where OAuth token refreshes were not being correctly triggered upon token expiry. The solution involves a significant refactoring of the credential exchange mechanism to provide more explicit feedback on whether a token exchange actually occurred. By modifying the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
b09d045 to
245c307
Compare
|
Response from ADK Triaging Agent Hello @MarlzRana, thank you for creating this PR! Could you please fill out the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request addresses a bug where OAuth tokens were not being refreshed upon expiry. The fix involves changing the exchange method in credential exchangers to return a tuple containing the credential and a boolean indicating whether an exchange actually occurred. This information is then used in CredentialManager to decide whether to attempt a token refresh.
The changes are logical and correctly fix the identified issue. The refactoring of the exchange method signature is consistently applied across BaseCredentialExchanger, OAuth2CredentialExchanger, and the corresponding tests.
I have provided a couple of comments with suggestions for improving code clarity and maintainability.
src/google/adk/auth/auth_handler.py
Outdated
| return (await exchanger.exchange( | ||
| self.auth_config.exchanged_auth_credential, self.auth_config.auth_scheme | ||
| ) | ||
| ))[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - addressed in 2dae391.
| if isinstance(exchanger, ServiceAccountCredentialExchanger): | ||
| exchanged_credential = exchanger.exchange_credential( | ||
| self._auth_config.auth_scheme, credential | ||
| ) | ||
| else: | ||
| exchanged_credential = await exchanger.exchange( | ||
| credential, self._auth_config.auth_scheme | ||
| return ( | ||
| exchanger.exchange_credential(self._auth_config.auth_scheme, credential), | ||
| True | ||
| ) | ||
|
|
||
| return exchanged_credential, True | ||
| return await exchanger.exchange(credential, self._auth_config.auth_scheme) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this change correctly handles the different interfaces of credential exchangers, the isinstance check points to a larger design issue. ServiceAccountCredentialExchanger does not conform to the BaseCredentialExchanger interface (it's not async, has a different method name exchange_credential, and a different return contract is being created for it here).
For better long-term maintainability, consider refactoring ServiceAccountCredentialExchanger to properly implement the BaseCredentialExchanger interface. This would remove the need for this special handling and make the credential exchange logic more uniform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For better long-term maintainability, consider refactoring ServiceAccountCredentialExchanger to properly implement the BaseCredentialExchanger interface. This would remove the need for this special handling and make the credential exchange logic more uniform.
Agreed. There are currently two BaseCredentialExchanger's (A) (B), B should be deprecated, and as part of that process ServiceAccountCredentialExchanger should inherit from A instead of B, and be moved to src/google/adk/auth/exchanger/service_account_exchanger.py instead of being in its current location (src/google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py).
|
Response from ADK Triaging Agent Hello @MarlzRana, thank you for creating this PR! Could you please fill out the |
|
Hi @MarlzRana ,Thank you for your contribution! We appreciate you taking the time to submit this pull request. Your PR has been received by the team and is currently under review. We will provide feedback as soon as we have an update to share. |
|
Hi @ankursharmas , can you please review this. |
|
Thank you @ryanaiagent for the triaging! I have started the review process since #3761 is assigned to me. |
Link to Issue or Description of Change
Link to an existing issue:
Problem:
The method below does not return the credential and whether the credential was exchanged. It returns the credential and whether an exchanger was found. Two different things, with the latter always true if one was able to exchange to get a credential initially, as an exchanger existed for that credential type.
adk-python/src/google/adk/auth/credential_manager.py
Lines 208 to 225 in 0094eea
Which means in the aforementioned (modal) case, that in the below code
was_exchangedis always true, and we never hit the code to refresh the credential if needed - which means credential refresh never happens.adk-python/src/google/adk/auth/credential_manager.py
Lines 161 to 167 in 0094eea
Solution:
Let's get the credential exchangers to indicate whether they performed token exchange and pass that back correctly in
_exchange_credentialin the credential manager, so thatwas_exchangedis correctly set to False (and not always True) when we already have a token in the credential, and hence, we do check if the credential needs refreshing, and refresh if necessary (instead of always skipping over this logic whenwas_exchangedwas always True prior to this PR).Testing Plan
Unit Tests:
Manual End-to-End (E2E) Tests:
Tested this locally with the ADK web and a long-running session that surpassed the duration of the token validity, and observed token refresh occur, and no longer obtained a 401.
Checklist