Repository navigation
feat(util): migrate fetch.go to cloud.google.com/go/auth and bridge legacy functions via oauth2adapt - #198
Open
agrawalradhika-cell wants to merge 5 commits into
Conversation
…egacy functions via oauth2adapt
…h-to-cloud-google-auth
- Bind legacy token sources to the caller's ctx and honor an *http.Client stored under oauth2.HTTPClient via DetectOptions.Client. - Use strings.Fields for scopes; empty Scope yields no scopes. - Share JWT audience/scope validation; clearer error when ADC has no JSON. - Keep the API key short-circuit in one place (newCredentials). - Fix 'authentcation' typo and 'SHA256 encryption' comment. - Add tests for ctx client/cancellation, refresh JSON token_uri round trip, service account with empty scope, withRefreshToken and JSON-less ADC.
agrawalradhika-cell
marked this pull request as ready for review
October 30, 2026 21:13
Remove call to silenceStdout in TestGet3LOAuthHandlerLoopback.
Added tests for OAuth handler loopback scenarios.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 4 of migrating
oauth2lfromgolang.org/x/oauth2tocloud.google.com/go/auth. This PR moves the core auth logic inutil/fetch.goontocredentials.DetectDefault. It addscloud.google.com/go/auth-typed versions of the fetch functions. The existinggolang.org/x/oauth2-typed functions stay as thin// Deprecated:wrappers built onoauth2adapt.What Changed
New
cloud.google.com/go/authfunctions inutil/fetch.goFindAuthCredentials(ctx, settings) (*auth.Credentials, error)callscredentials.DetectDefaultthrough a new internal helper,newDetectOptions. The helper handles three gaps between the two libraries:DetectOptions.validate()rejects options that set both.main.gosets both for positional--jwtargs and for--sts --audience --scope. SoAudienceis only used for self-signed JWTs, where it wins overScope. OAuth flows use onlyScope.token_uriinauthorized_userJSON:cloud.google.com/go/authignores this field and readsDetectOptions.TokenURLinstead. The helper copiestoken_uriintoTokenURL, so--refresh(which usesBuildRefreshTokenJSON) still works against custom or mock token endpoints.AuthHandlerOptions: wired fromsettings.resolveAuthHandler(),settings.State(defaults to"state"), andGeneratePKCEOptions().*http.Clientstored inctxunderoauth2.HTTPClientis passed asDetectOptions.Client, so callers who injected a client into the context thegolang.org/x/oauth2way keep it.credentials.DetectDefaulttakes no context, soctxis used only for this lookup.strings.Fields, so repeated spaces don't produce empty scope entries. An emptyScopegives no scopes, which sends the same emptyscopeclaim as the oldstrings.Split("", " ")(covered by a service-account test).OAuthJSONTokenProvider(ctx, settings) (auth.TokenProvider, error).JWTTokenProvider(ctx, settings) (auth.TokenProvider, error)setsUseSelfSignedJWT: trueand requires an audience or a scope.cloud.google.com/go/authquietly ignoresUseSelfSignedJWTfor credentials that aren't service accounts, so this function rejects them explicitly, asgoogle.JWTAccessTokenSourceFromJSONdid before. When ADC finds credentials with no JSON (for example, GCE metadata), the error says that a service account key file is required.JWTTokenSourceandJWTTokenProvidershare one validation helper; each reports its own name in the error.FetchAuthToken(ctx, settings) (*auth.Token, error):(nil, nil)for API keys.DefaultScopewhensettings == nil.authorized_userrefresh response leaves outrefresh_token, it copies the refresh token from the credentials intotoken.Metadata["refresh_token"]. This matchesgolang.org/x/oauth2, which keeps the refresh token on refreshed tokens.Legacy functions now wrap the new ones and are marked
// Deprecated:FindJSONCredentialscallsFindAuthCredentialsand converts the result withoauth2adapt.Oauth2CredentialsFromAuthCredentials.OAuthJSONTokenSource,JWTTokenSource, andFetchTokencall the new providers.ctx, asgoogle.CredentialsFromJSONWithParams(ctx, ...)token sources were, so deadlines and cancellation still apply.oauth2adapt.TokenSourceFromTokenProviderdirectly. The reason:auth.Tokenhas noRefreshTokenfield, and theoauth2adaptversion doesn't fill inoauth2.Token.RefreshToken. The adapter:refresh_tokenfrom the metadata intooauth2.Token.RefreshTokenExtra(), soid_tokenandscopestill reachMarshalWithExtras*oauth2.RetrieveErrorto the error chain (viaoauth2adapt.AddRetrieveErrorToAuthError), so callers usingerrors.Askeep workinggolang.org/x/oauth2/googlecalls fromfetch.go:FindDefaultCredentialsWithParams,CredentialsFromJSONWithParams,DefaultTokenSource,JWTAccessTokenSourceFromJSON, andJWTAccessTokenSourceWithScope. The temporaryauthhandlerimport is removed too.util/settings.gocloud.google.com/go/auth/credentials, whichfetch.gonow imports directly. Nogo.modorvendor/changes are needed.Small cleanups: fixes the existing
Unsupported authentcation methodtypo and theSHA256 encryptionwording in theGeneratePKCEOptionscomment (now "SHA-256 hashing").TestGet3LOAuthHandlerLoopbackServerNotListeningnow callssilenceStdout(t)like the other handler tests.New
util/fetch_test.gonewDetectOptions: scope/audience precedence,token_uriforwarding (only forauthorized_user),Subject, andAuthHandlerOptions(handler precedence, default or custom state, PKCE S256).FetchAuthTokenandFetchToken, using anhttptesttoken server:authorized_userrefresh, both with a kept and a rotated refresh tokenExtra("id_token")andExtra("scope")MarshalWithExtrasround trip*auth.Errorand*oauth2.RetrieveErrorerror chainsJWTTokenProviderandJWTTokenSource: audience wins over scope; scope-only works. Each JWT'saud,scope, andissclaims are decoded and checked. Also covers the error cases (no audience or scope, non-service-account credentials).FindAuthCredentialsandFindJSONCredentialskeepJSONandProjectID.oauth2.HTTPClientstored in the context, and a canceled context makesOAuthJSONTokenSource().Token()fail withcontext.Canceled.token_uriround trip:BuildAuthRefreshTokenJSONoutput is sent to the client secret'stoken_uriwithgrant_type=refresh_token.Scopegets a token, and its JWT assertion has an empty scope.withRefreshToken: nil token, refresh token already present, credentials that aren'tauthorized_user, empty JSON, and a check that the cached token's metadata is not mutated.Backwards Compatibility
cloud.google.com/go/auth,auth/credentials, andoauth2adaptwere already added togo.modandvendor/in build(deps): add cloud.google.com/go/auth and cloud.google.com/go/auth/oauth2adapt #195.go mod verifypasses, andgo mod tidy && go mod vendorproduce no diff.util/fetch.gokeeps its exactgolang.org/x/oauth2input and output types. The CLI (main.go,util/tasks.go) still calls the deprecated wrappers, so all CLI commands now run on thecloud.google.com/go/authbackend through the bridge. Phase 5 switches the CLI to the new functions directly.cloud.google.com/go/auth(auth: cannot fetch token: ...) instead ofoauth2: cannot fetch token: ....*oauth2.RetrieveErroris still in the error chain.FetchToken(ctx, nil)used to panic. It now uses ADC withDefaultScope, which is what the oldnewTokenSourceintended.Settings.Stateis now passed as the 3LO state (default"state"). Before,"state"was hardcoded, so any other value caused a state mismatch. The CLI already passes"state".Testing
go vet ./util/go test -race ./util/...make test: all integration suites inintegration/cli_test.gopass againstintegration/golden/(2LO, 3LO, 3LO loopback, cached and refresh flows, JWT, SSO, STS, impersonation).make build