diff --git a/internal/creds.go b/internal/creds.go index 855604b75df..b067a179b7a 100644 --- a/internal/creds.go +++ b/internal/creds.go @@ -31,6 +31,9 @@ func Creds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) { } func baseCreds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) { + if ds.InternalCredentials != nil { + return ds.InternalCredentials, nil + } if ds.Credentials != nil { return ds.Credentials, nil } diff --git a/internal/creds_test.go b/internal/creds_test.go index ecdae563187..34b052dcb29 100644 --- a/internal/creds_test.go +++ b/internal/creds_test.go @@ -236,3 +236,62 @@ func TestQuotaProjectFromCreds(t *testing.T) { t.Errorf("QuotaProjectFromCreds(quotaProjectJSON): want %q, got %q", want, got) } } + +func TestCredsWithCredentials(t *testing.T) { + tests := []struct { + name string + ds *DialSettings + want string + }{ + { + name: "only normal opt", + ds: &DialSettings{ + Credentials: &google.Credentials{ + TokenSource: oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: "normal", + }), + }, + }, + want: "normal", + }, + { + name: "normal and internal creds opt", + ds: &DialSettings{ + Credentials: &google.Credentials{ + TokenSource: oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: "normal", + }), + }, + InternalCredentials: &google.Credentials{ + TokenSource: oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: "internal", + }), + }, + }, + want: "internal", + }, + { + name: "only internal creds opt", + ds: &DialSettings{ + InternalCredentials: &google.Credentials{ + TokenSource: oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: "internal", + }), + }, + }, + want: "internal", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + creds, err := Creds(context.Background(), tc.ds) + if err != nil { + t.Fatalf("got %v, want nil error", err) + } + if tok, _ := creds.TokenSource.Token(); tok.AccessToken != tc.want { + t.Fatalf("tok.AccessToken = %q, want %q", tok.AccessToken, tc.want) + } + }) + } +} diff --git a/internal/settings.go b/internal/settings.go index 278e2b34cc3..76efdb22772 100644 --- a/internal/settings.go +++ b/internal/settings.go @@ -29,6 +29,7 @@ type DialSettings struct { Credentials *google.Credentials CredentialsFile string // if set, Token Source is ignored. CredentialsJSON []byte + InternalCredentials *google.Credentials UserAgent string APIKey string Audiences []string diff --git a/option/internaloption/internaloption.go b/option/internaloption/internaloption.go index 4c33916165a..343a5a965eb 100644 --- a/option/internaloption/internaloption.go +++ b/option/internaloption/internaloption.go @@ -6,6 +6,7 @@ package internaloption import ( + "golang.org/x/oauth2/google" "google.golang.org/api/internal" "google.golang.org/api/option" ) @@ -121,3 +122,15 @@ type enableJwtWithScope bool func (w enableJwtWithScope) Apply(o *internal.DialSettings) { o.EnableJwtWithScope = bool(w) } + +// WithCredentials returns a client option to specify credentials which will be used to authenticate API calls. +// This credential takes precedence over all other credential options. +func WithCredentials(creds *google.Credentials) option.ClientOption { + return (*withCreds)(creds) +} + +type withCreds google.Credentials + +func (w *withCreds) Apply(o *internal.DialSettings) { + o.InternalCredentials = (*google.Credentials)(w) +} diff --git a/option/internaloption/internaloption_test.go b/option/internaloption/internaloption_test.go new file mode 100644 index 00000000000..0ad09f8c236 --- /dev/null +++ b/option/internaloption/internaloption_test.go @@ -0,0 +1,29 @@ +// Copyright 2021 Google LLC. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internaloption + +import ( + "testing" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "google.golang.org/api/internal" +) + +func TestWithCredentials(t *testing.T) { + want := "fakeToken" + fakeCreds := &google.Credentials{ + TokenSource: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: want}), + } + opt := WithCredentials(fakeCreds) + ds := &internal.DialSettings{} + opt.Apply(ds) + if ds.InternalCredentials == nil || ds.InternalCredentials.TokenSource == nil { + t.Errorf("ds.InternalCredentials should be initialized") + } + if tok, err := ds.InternalCredentials.TokenSource.Token(); err != nil || tok.AccessToken != "fakeToken" { + t.Errorf("ts.Token() = %q, want %q", tok.AccessToken, "") + } +}