fix(auth): omit scope from OAuth2 token exchange and refresh requests#5874
fix(auth): omit scope from OAuth2 token exchange and refresh requests#5874doughayden wants to merge 1 commit into
Conversation
|
Hi @doughayden , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Please fix formatting errors by running autoformat.sh |
|
Thanks @rohityan. The auto-fix changes are all in files this PR does not touch: |
- Stop setting scope on the shared create_oauth2_session helper - Drops scope from both token exchange and refresh requests - Neither needs scope (RFC 6749 4.1.3, 6); some providers reject it - Auth URL construction in auth_handler.py keeps scope, unchanged - Add body-level tests asserting refresh and exchange omit scope
8d5a4c2 to
74a609e
Compare
- Stop setting scope on the shared create_oauth2_session helper - Drops scope from both token exchange and refresh requests - Neither needs scope (RFC 6749 4.1.3, 6); some providers reject it - Auth URL construction in auth_handler.py keeps scope, unchanged - Add body-level tests asserting refresh and exchange omit scope Merge #5874 Change-Id: I78e7e1e69076afcc13460b7965ca05cae734814e
|
I've merged the PR. Thanks! |
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
Solution:
create_oauth2_sessioninoauth2_credential_util.pyis the shared helper behind both token exchange and token refresh, and it setsscopeon theOAuth2Session. authlib then carries that scope onto the refresh request (if "scope" not in kwargs and self.scope), and some providers like Salesforce reject scope on refresh, so the refresh fails.Neither token operation needs scope. The authorization code already encodes the granted scopes (RFC 6749 section 4.1.3), and on refresh scope is optional, with the server reusing the originally granted scope when it is absent (RFC 6749 section 6). This change stops setting scope in the shared helper, so it is dropped from both exchange and refresh.
Authorization URL construction uses a separate
OAuth2Sessioninauth_handler.pyand is unchanged, so scope still appears on the consent request where it is required. This is the "use scope only during authorization" outcome discussed on the issue, implemented at the token-request helper rather than by splitting scopes out of the auth scheme.Testing Plan
Unit Tests:
Added two tests in
tests/unittests/auth/test_oauth2_credential_util.pythat driverefresh_tokenandfetch_tokenthrough a stubbedOAuth2Session.postand assert the outgoing request body contains noscope. Asserting the wire body keeps the regression independent of authlib's internal scope handling.Manual End-to-End (E2E) Tests:
A runnable reproduction is at doughayden/adk-issue-examples / 02-scope_in_refresh: it stands up a token server that rejects scope on refresh and drives an ADK refresh through it. Refresh fails before this change and succeeds after. The same fix has been running as a monkey-patch against a live Salesforce integration, where token refresh succeeds.
Checklist
Additional context
This is currently carried as a monkey-patch in a downstream project; the fix lets that be removed. Fixing the shared helper rather than passing scope at each call site also covers the token exchange path, keeping the two token operations consistent.