diff --git a/auth/auth.go b/auth/auth.go index 9099c0b640a..e26802f1ea7 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -40,6 +40,8 @@ const ( // 3 minutes and 45 seconds before expiration. The shortest MDS cache is 4 minutes, // so we give it 15 seconds to refresh it's cache before attempting to refresh a token. defaultExpiryDelta = 215 * time.Second + + universeDomainDefault = "googleapis.com" ) var ( @@ -94,6 +96,112 @@ func (t *Token) isValidWithEarlyExpiry(earlyExpiry time.Duration) bool { return !t.Expiry.Round(0).Add(-earlyExpiry).Before(timeNow()) } +// Credentials holds Google credentials, including +// [Application Default Credentials](https://developers.google.com/accounts/docs/application-default-credentials). +type Credentials struct { + json []byte + projectID CredentialsPropertyProvider + quotaProjectID CredentialsPropertyProvider + // universeDomain is the default service domain for a given Cloud universe. + universeDomain CredentialsPropertyProvider + + TokenProvider +} + +// JSON returns the bytes associated with the the file used to source +// credentials if one was used. +func (c *Credentials) JSON() []byte { + return c.json +} + +// ProjectID returns the associated project ID from the underlying file or +// environment. +func (c *Credentials) ProjectID(ctx context.Context) (string, error) { + if c.projectID == nil { + return internal.GetProjectID(c.json, ""), nil + } + v, err := c.projectID.GetProperty(ctx) + if err != nil { + return "", err + } + return internal.GetProjectID(c.json, v), nil +} + +// QuotaProjectID returns the associated quota project ID from the underlying +// file or environment. +func (c *Credentials) QuotaProjectID(ctx context.Context) (string, error) { + if c.quotaProjectID == nil { + return internal.GetQuotaProject(c.json, ""), nil + } + v, err := c.quotaProjectID.GetProperty(ctx) + if err != nil { + return "", err + } + return internal.GetQuotaProject(c.json, v), nil +} + +// UniverseDomain returns the default service domain for a given Cloud universe. +// The default value is "googleapis.com". +func (c *Credentials) UniverseDomain(ctx context.Context) (string, error) { + if c.universeDomain == nil { + return universeDomainDefault, nil + } + v, err := c.universeDomain.GetProperty(ctx) + if err != nil { + return "", err + } + if v == "" { + return universeDomainDefault, nil + } + return v, err +} + +// CredentialsPropertyProvider provides an implementation to fetch a property +// value for [Credentials]. +type CredentialsPropertyProvider interface { + GetProperty(context.Context) (string, error) +} + +// CredentialsPropertyFunc is a type adapter to allow the use of ordinary +// functions as a [CredentialsPropertyProvider]. +type CredentialsPropertyFunc func(context.Context) (string, error) + +// GetProperty loads the properly value provided the given context. +func (p CredentialsPropertyFunc) GetProperty(ctx context.Context) (string, error) { + return p(ctx) +} + +// CredentialsOptions are used to configure [Credentials]. +type CredentialsOptions struct { + // TokenProvider is a means of sourcing a token for the credentials. Required. + TokenProvider TokenProvider + // JSON is the raw contents of the credentials file if sourced from a file. + JSON []byte + // ProjectIDProvider resolves the project ID associated with the + // credentials. + ProjectIDProvider CredentialsPropertyProvider + // QuotaProjectIDProvider resolves the quota project ID associated with the + // credentials. + QuotaProjectIDProvider CredentialsPropertyProvider + // UniverseDomainProvider resolves the universe domain with the credentials. + UniverseDomainProvider CredentialsPropertyProvider +} + +// NewCredentials returns new [Credentials] from the provided options. Most users +// will want to build this object a function from the +// [cloud.google.com/go/auth/credentials] package. +func NewCredentials(opts *CredentialsOptions) *Credentials { + creds := &Credentials{ + TokenProvider: opts.TokenProvider, + json: opts.JSON, + projectID: opts.ProjectIDProvider, + quotaProjectID: opts.QuotaProjectIDProvider, + universeDomain: opts.UniverseDomainProvider, + } + + return creds +} + // CachedTokenProviderOptions provided options for configuring a // CachedTokenProvider. type CachedTokenProviderOptions struct { diff --git a/auth/detect/compute.go b/auth/credentials/compute.go similarity index 99% rename from auth/detect/compute.go rename to auth/credentials/compute.go index 430026a98a0..74c1d52e0ea 100644 --- a/auth/detect/compute.go +++ b/auth/credentials/compute.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "context" diff --git a/auth/detect/compute_test.go b/auth/credentials/compute_test.go similarity index 98% rename from auth/detect/compute_test.go rename to auth/credentials/compute_test.go index 4a1e751fd26..0b5eca6ce41 100644 --- a/auth/detect/compute_test.go +++ b/auth/credentials/compute_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "context" diff --git a/auth/detect/detect.go b/auth/credentials/detect.go similarity index 74% rename from auth/detect/detect.go rename to auth/credentials/detect.go index 784c4d74a93..24d153bb8b9 100644 --- a/auth/detect/detect.go +++ b/auth/credentials/detect.go @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( + "context" "encoding/json" "errors" "fmt" @@ -38,8 +39,6 @@ const ( // Help on default credentials adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc" - - universeDomainDefault = "googleapis.com" ) var ( @@ -47,63 +46,14 @@ var ( allowOnGCECheck = true ) -// Credentials holds Google credentials, including -// [Application Default Credentials](https://developers.google.com/accounts/docs/application-default-credentials). -type Credentials struct { - json []byte - projectID string - quotaProjectID string - // universeDomain is the default service domain for a given Cloud universe. - universeDomain string - - auth.TokenProvider -} - -func newCredentials(tokenProvider auth.TokenProvider, json []byte, projectID string, quotaProjectID string, universeDomain string) *Credentials { - return &Credentials{ - json: json, - projectID: internal.GetProjectID(json, projectID), - quotaProjectID: internal.GetQuotaProject(json, quotaProjectID), - TokenProvider: tokenProvider, - universeDomain: universeDomain, - } -} - -// JSON returns the bytes associated with the the file used to source -// credentials if one was used. -func (c *Credentials) JSON() []byte { - return c.json -} - -// ProjectID returns the associated project ID from the underlying file or -// environment. -func (c *Credentials) ProjectID() string { - return c.projectID -} - -// QuotaProjectID returns the associated quota project ID from the underlying -// file or environment. -func (c *Credentials) QuotaProjectID() string { - return c.quotaProjectID -} - -// UniverseDomain returns the default service domain for a given Cloud universe. -// The default value is "googleapis.com". -func (c *Credentials) UniverseDomain() string { - if c.universeDomain == "" { - return universeDomainDefault - } - return c.universeDomain -} - // OnGCE reports whether this process is running in Google Cloud. func OnGCE() bool { // TODO(codyoss): once all libs use this auth lib move metadata check here return allowOnGCECheck && metadata.OnGCE() } -// DefaultCredentials searches for "Application Default Credentials" and returns -// a credential based on the [Options] provided. +// DetectDefault searches for "Application Default Credentials" and returns +// a credential based on the [DetectOptions] provided. // // It looks for credentials in the following places, preferring the first // location found: @@ -119,7 +69,7 @@ func OnGCE() bool { // - On Google Compute Engine, Google App Engine standard second generation // runtimes, and Google App Engine flexible environment, it fetches // credentials from the metadata server. -func DefaultCredentials(opts *Options) (*Credentials, error) { +func DetectDefault(opts *DetectOptions) (*auth.Credentials, error) { if err := opts.validate(); err != nil { return nil, err } @@ -138,15 +88,19 @@ func DefaultCredentials(opts *Options) (*Credentials, error) { } if OnGCE() { - id, _ := metadata.ProjectID() - return newCredentials(computeTokenProvider(opts.EarlyTokenRefresh, opts.Scopes...), nil, id, "", ""), nil + return auth.NewCredentials(&auth.CredentialsOptions{ + TokenProvider: computeTokenProvider(opts.EarlyTokenRefresh, opts.Scopes...), + ProjectIDProvider: auth.CredentialsPropertyFunc(func(context.Context) (string, error) { + return metadata.ProjectID() + }), + }), nil } return nil, fmt.Errorf("detect: could not find default credentials. See %v for more information", adcSetupURL) } -// Options provides configuration for [DefaultCredentials]. -type Options struct { +// DetectOptions provides configuration for [DetectDefault]. +type DetectOptions struct { // Scopes that credentials tokens should have. Example: // https://www.googleapis.com/auth/cloud-platform. Required if Audience is // not provided. @@ -188,7 +142,7 @@ type Options struct { Client *http.Client } -func (o *Options) validate() error { +func (o *DetectOptions) validate() error { if o == nil { return errors.New("detect: options must be provided") } @@ -201,27 +155,27 @@ func (o *Options) validate() error { return nil } -func (o *Options) tokenURL() string { +func (o *DetectOptions) tokenURL() string { if o.TokenURL != "" { return o.TokenURL } return googleTokenURL } -func (o *Options) scopes() []string { +func (o *DetectOptions) scopes() []string { scopes := make([]string, len(o.Scopes)) copy(scopes, o.Scopes) return scopes } -func (o *Options) client() *http.Client { +func (o *DetectOptions) client() *http.Client { if o.Client != nil { return o.Client } return internal.CloneDefaultClient() } -func readCredentialsFile(filename string, opts *Options) (*Credentials, error) { +func readCredentialsFile(filename string, opts *DetectOptions) (*auth.Credentials, error) { b, err := os.ReadFile(filename) if err != nil { return nil, err @@ -229,7 +183,7 @@ func readCredentialsFile(filename string, opts *Options) (*Credentials, error) { return readCredentialsFileJSON(b, opts) } -func readCredentialsFileJSON(b []byte, opts *Options) (*Credentials, error) { +func readCredentialsFileJSON(b []byte, opts *DetectOptions) (*auth.Credentials, error) { // attempt to parse jsonData as a Google Developers Console client_credentials.json. config := clientCredConfigFromJSON(b, opts) if config != nil { @@ -240,12 +194,15 @@ func readCredentialsFileJSON(b []byte, opts *Options) (*Credentials, error) { if err != nil { return nil, err } - return newCredentials(tp, b, "", "", ""), nil + return auth.NewCredentials(&auth.CredentialsOptions{ + TokenProvider: tp, + JSON: b, + }), nil } return fileCredentials(b, opts) } -func clientCredConfigFromJSON(b []byte, opts *Options) *auth.Options3LO { +func clientCredConfigFromJSON(b []byte, opts *DetectOptions) *auth.Options3LO { var creds internaldetect.ClientCredentialsFile var c *internaldetect.Config3LO if err := json.Unmarshal(b, &creds); err != nil { diff --git a/auth/detect/detect_test.go b/auth/credentials/detect_test.go similarity index 83% rename from auth/detect/detect_test.go rename to auth/credentials/detect_test.go index 57983331b33..20e77cc98f0 100644 --- a/auth/detect/detect_test.go +++ b/auth/credentials/detect_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "context" @@ -27,7 +27,7 @@ import ( "time" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect/internal/gdch" + "cloud.google.com/go/auth/credentials/internal/gdch" "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/internaldetect" "cloud.google.com/go/auth/internal/jwt" @@ -40,6 +40,7 @@ type tokResp struct { } func TestDefaultCredentials_GdchServiceAccountKey(t *testing.T) { + ctx := context.Background() aud := "http://sampele-aud.com/" b, err := os.ReadFile("../internal/testdata/gdch.json") if err != nil { @@ -112,22 +113,29 @@ func TestDefaultCredentials_GdchServiceAccountKey(t *testing.T) { t.Fatal(err) } - if _, err := DefaultCredentials(&Options{CredentialsJSON: b}); err == nil { + if _, err := DetectDefault(&DetectOptions{CredentialsJSON: b}); err == nil { t.Fatal("STSAudience should be required") } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, STSAudience: aud, }) if err != nil { t.Fatal(err) } - - if want := "fake_project"; creds.ProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + got, err := creds.ProjectID(ctx) + if err != nil { + t.Fatal(err) + } + if want := "fake_project"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -142,6 +150,7 @@ func TestDefaultCredentials_GdchServiceAccountKey(t *testing.T) { } func TestDefaultCredentials_ImpersonatedServiceAccountKey(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/imp.json") if err != nil { t.Fatal(err) @@ -168,7 +177,7 @@ func TestDefaultCredentials_ImpersonatedServiceAccountKey(t *testing.T) { t.Fatal(err) } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, UseSelfSignedJWT: true, @@ -176,8 +185,12 @@ func TestDefaultCredentials_ImpersonatedServiceAccountKey(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + got, err := creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) + } + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -192,6 +205,7 @@ func TestDefaultCredentials_ImpersonatedServiceAccountKey(t *testing.T) { } func TestDefaultCredentials_UserCredentialsKey(t *testing.T) { + ctx := context.Background() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") resp := &tokResp{ @@ -204,7 +218,7 @@ func TestDefaultCredentials_UserCredentialsKey(t *testing.T) { } })) - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsFile: "../internal/testdata/user.json", Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, TokenURL: ts.URL, @@ -212,11 +226,19 @@ func TestDefaultCredentials_UserCredentialsKey(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "fake_project2"; creds.QuotaProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + got, err := creds.QuotaProjectID(ctx) + if err != nil { + t.Fatal(err) + } + if want := "fake_project2"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -231,6 +253,7 @@ func TestDefaultCredentials_UserCredentialsKey(t *testing.T) { } func TestDefaultCredentials_UserCredentialsKey_UniverseDomain(t *testing.T) { + ctx := context.Background() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") resp := &tokResp{ @@ -243,7 +266,7 @@ func TestDefaultCredentials_UserCredentialsKey_UniverseDomain(t *testing.T) { } })) - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsFile: "../internal/testdata/user_universe_domain.json", Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, TokenURL: ts.URL, @@ -251,11 +274,19 @@ func TestDefaultCredentials_UserCredentialsKey_UniverseDomain(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "fake_project2"; creds.QuotaProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + got, err := creds.QuotaProjectID(ctx) + if err != nil { + t.Fatal(err) + } + if want := "fake_project2"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -270,6 +301,7 @@ func TestDefaultCredentials_UserCredentialsKey_UniverseDomain(t *testing.T) { } func TestDefaultCredentials_ServiceAccountKey(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/sa.json") if err != nil { t.Fatal(err) @@ -294,18 +326,26 @@ func TestDefaultCredentials_ServiceAccountKey(t *testing.T) { t.Fatal(err) } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, }) if err != nil { t.Fatal(err) } - if want := "fake_project"; creds.ProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + got, err := creds.ProjectID(ctx) + if err != nil { + t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "fake_project"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) + } + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -320,6 +360,7 @@ func TestDefaultCredentials_ServiceAccountKey(t *testing.T) { } func TestDefaultCredentials_ServiceAccountKeySelfSigned(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/sa.json") if err != nil { t.Fatal(err) @@ -329,7 +370,7 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned(t *testing.T) { defer func() { now = oldNow }() wantTok := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiY2RlZjEyMzQ1Njc4OTAifQ.eyJpc3MiOiJnb3BoZXJAZmFrZV9wcm9qZWN0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic2NvcGUiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2Nsb3VkLXBsYXRmb3JtIiwiZXhwIjo5NDk0MTE4MDAsImlhdCI6OTQ5NDA4MjAwLCJhdWQiOiIiLCJzdWIiOiJnb3BoZXJAZmFrZV9wcm9qZWN0LmlhbS5nc2VydmljZWFjY291bnQuY29tIn0.n9Hggd-1Vw4WTQiWkh7q9r5eDsz-khU5vwkZl2VmgdUF3ZxDq1ARzchCNtTifeorzbp9C0i0vCr855G7FZkVCJXPVMcnxbwfMSafUYmVsmutbQiV9eTWfWM0_Ljiwa9GEbv1bN06Lz4LrelPKEaxsDbY6tU8LJUiome_gSMLfLk" - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, UseSelfSignedJWT: true, @@ -337,11 +378,20 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "fake_project"; creds.ProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + + got, err := creds.ProjectID(ctx) + if err != nil { + t.Fatal(err) + } + if want := "fake_project"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -356,6 +406,7 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned(t *testing.T) { } func TestDefaultCredentials_ServiceAccountKeySelfSigned_UniverseDomain(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/sa_universe_domain.json") if err != nil { t.Fatal(err) @@ -365,7 +416,7 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned_UniverseDomain(t *testin defer func() { now = oldNow }() wantTok := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiY2RlZjEyMzQ1Njc4OTAifQ.eyJpc3MiOiJnb3BoZXJAZmFrZV9wcm9qZWN0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic2NvcGUiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2Nsb3VkLXBsYXRmb3JtIiwiZXhwIjo5NDk0MTE4MDAsImlhdCI6OTQ5NDA4MjAwLCJhdWQiOiIiLCJzdWIiOiJnb3BoZXJAZmFrZV9wcm9qZWN0LmlhbS5nc2VydmljZWFjY291bnQuY29tIn0.n9Hggd-1Vw4WTQiWkh7q9r5eDsz-khU5vwkZl2VmgdUF3ZxDq1ARzchCNtTifeorzbp9C0i0vCr855G7FZkVCJXPVMcnxbwfMSafUYmVsmutbQiV9eTWfWM0_Ljiwa9GEbv1bN06Lz4LrelPKEaxsDbY6tU8LJUiome_gSMLfLk" - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, UseSelfSignedJWT: true, @@ -373,11 +424,19 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned_UniverseDomain(t *testin if err != nil { t.Fatal(err) } - if want := "fake_project"; creds.ProjectID() != want { - t.Fatalf("got %q, want %q", creds.ProjectID(), want) + got, err := creds.ProjectID(ctx) + if err != nil { + t.Fatal(err) + } + if want := "fake_project"; got != want { + t.Fatalf("got %q, want %q", got, want) + } + got, err = creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) } - if want := "example.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + if want := "example.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -392,6 +451,7 @@ func TestDefaultCredentials_ServiceAccountKeySelfSigned_UniverseDomain(t *testin } func TestDefaultCredentials_ClientCredentials(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/clientcreds_installed.json") if err != nil { t.Fatal(err) @@ -418,7 +478,7 @@ func TestDefaultCredentials_ClientCredentials(t *testing.T) { t.Fatal(err) } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, TokenURL: ts.URL, @@ -437,8 +497,12 @@ func TestDefaultCredentials_ClientCredentials(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + got, err := creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) + } + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -454,6 +518,7 @@ func TestDefaultCredentials_ClientCredentials(t *testing.T) { // Better coverage of all external account features tested in the sub-package. func TestDefaultCredentials_ExternalAccountKey(t *testing.T) { + ctx := context.Background() b, err := os.ReadFile("../internal/testdata/exaccount_url.json") if err != nil { t.Fatal(err) @@ -516,7 +581,7 @@ func TestDefaultCredentials_ExternalAccountKey(t *testing.T) { t.Fatal(err) } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, UseSelfSignedJWT: true, @@ -524,8 +589,12 @@ func TestDefaultCredentials_ExternalAccountKey(t *testing.T) { if err != nil { t.Fatal(err) } - if want := "googleapis.com"; creds.UniverseDomain() != want { - t.Fatalf("got %q, want %q", creds.UniverseDomain(), want) + got, err := creds.UniverseDomain(ctx) + if err != nil { + t.Fatal(err) + } + if want := "googleapis.com"; got != want { + t.Fatalf("got %q, want %q", got, want) } tok, err := creds.Token(context.Background()) if err != nil { @@ -577,7 +646,7 @@ func TestDefaultCredentials_ExternalAccountAuthorizedUserKey(t *testing.T) { t.Fatal(err) } - creds, err := DefaultCredentials(&Options{ + creds, err := DetectDefault(&DetectOptions{ CredentialsJSON: b, Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, UseSelfSignedJWT: true, @@ -603,7 +672,7 @@ func TestDefaultCredentials_Fails(t *testing.T) { t.Setenv("APPDATA", "nothingToSeeHere") allowOnGCECheck = false defer func() { allowOnGCECheck = true }() - if _, err := DefaultCredentials(&Options{ + if _, err := DetectDefault(&DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, }); !strings.Contains(err.Error(), adcSetupURL) { t.Fatalf("got %v, wanted to contain %v", err, adcSetupURL) @@ -611,7 +680,7 @@ func TestDefaultCredentials_Fails(t *testing.T) { } func TestDefaultCredentials_BadFiletype(t *testing.T) { - if _, err := DefaultCredentials(&Options{ + if _, err := DetectDefault(&DetectOptions{ CredentialsJSON: []byte(`{"type":"42"}`), Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, }); err == nil { @@ -622,21 +691,21 @@ func TestDefaultCredentials_BadFiletype(t *testing.T) { func TestDefaultCredentials_Validate(t *testing.T) { tests := []struct { name string - opts *Options + opts *DetectOptions }{ { name: "missing options", }, { name: "scope and audience provided", - opts: &Options{ + opts: &DetectOptions{ Scopes: []string{"scope"}, Audience: "aud", }, }, { name: "file and json provided", - opts: &Options{ + opts: &DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "path", CredentialsJSON: []byte(`{"some":"json"}`), @@ -645,7 +714,7 @@ func TestDefaultCredentials_Validate(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if _, err := DefaultCredentials(tt.opts); err == nil { + if _, err := DetectDefault(tt.opts); err == nil { t.Error("got nil, want an error") } }) diff --git a/auth/detect/doc.go b/auth/credentials/doc.go similarity index 96% rename from auth/detect/doc.go rename to auth/credentials/doc.go index 027a59fb6aa..4dcc74f4848 100644 --- a/auth/detect/doc.go +++ b/auth/credentials/doc.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package detect provides support for making OAuth2 authorized and +// Package credentials provides support for making OAuth2 authorized and // authenticated HTTP requests to Google APIs. It supports the Web server flow, // client-side credentials, service accounts, Google Compute Engine service // accounts, Google App Engine service accounts and workload identity federation @@ -77,7 +77,7 @@ // The [Credentials] type represents Google credentials, including Application // Default Credentials. // -// Use [DefaultCredentials] to obtain Application Default Credentials. +// Use [DetectDefault] to obtain Application Default Credentials. // // Application Default Credentials support workload identity federation to // access Google Cloud resources from non-Google Cloud platforms including Amazon @@ -85,4 +85,4 @@ // OpenID Connect (OIDC). Workload identity federation is recommended for // non-Google Cloud environments as it avoids the need to download, manage, and // store service account private keys locally. -package detect +package credentials diff --git a/auth/detect/example_test.go b/auth/credentials/example_test.go similarity index 84% rename from auth/detect/example_test.go rename to auth/credentials/example_test.go index 1bcf64909cd..35564c723fb 100644 --- a/auth/detect/example_test.go +++ b/auth/credentials/example_test.go @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect_test +package credentials_test import ( "log" "os" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/httptransport" ) -func ExampleDefaultCredentials() { - creds, err := detect.DefaultCredentials(&detect.Options{ +func ExampleDetectDefault() { + creds, err := credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, }) if err != nil { @@ -38,7 +38,7 @@ func ExampleDefaultCredentials() { client.Get("...") } -func ExampleDefaultCredentials_withFilepath() { +func ExampleDetectDefault_withFilepath() { // Your credentials should be obtained from the Google // Developer Console (https://console.developers.google.com). // Navigate to your project, then see the "Credentials" page @@ -47,7 +47,7 @@ func ExampleDefaultCredentials_withFilepath() { // select "Service Account", and click "Create Client ID". A JSON // key file will then be downloaded to your computer. filepath := "/path/to/your-project-key.json" - creds, err := detect.DefaultCredentials(&detect.Options{ + creds, err := credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/bigquery"}, CredentialsFile: filepath, }) @@ -63,12 +63,12 @@ func ExampleDefaultCredentials_withFilepath() { client.Get("...") } -func ExampleDefaultCredentials_withJSON() { +func ExampleDetectDefault_withJSON() { data, err := os.ReadFile("/path/to/key-file.json") if err != nil { log.Fatal(err) } - creds, err := detect.DefaultCredentials(&detect.Options{ + creds, err := credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/bigquery"}, CredentialsJSON: data, }) diff --git a/auth/detect/filetypes.go b/auth/credentials/filetypes.go similarity index 81% rename from auth/detect/filetypes.go rename to auth/credentials/filetypes.go index 3d822d740a4..9bef2afe848 100644 --- a/auth/detect/filetypes.go +++ b/auth/credentials/filetypes.go @@ -12,21 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "errors" "fmt" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect/internal/externalaccount" - "cloud.google.com/go/auth/detect/internal/externalaccountuser" - "cloud.google.com/go/auth/detect/internal/gdch" - "cloud.google.com/go/auth/detect/internal/impersonate" + "cloud.google.com/go/auth/credentials/internal/externalaccount" + "cloud.google.com/go/auth/credentials/internal/externalaccountuser" + "cloud.google.com/go/auth/credentials/internal/gdch" + "cloud.google.com/go/auth/credentials/internal/impersonate" + internalauth "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/internaldetect" ) -func fileCredentials(b []byte, opts *Options) (*Credentials, error) { +func fileCredentials(b []byte, opts *DetectOptions) (*auth.Credentials, error) { fileType, err := internaldetect.ParseFileType(b) if err != nil { return nil, err @@ -100,12 +101,18 @@ func fileCredentials(b []byte, opts *Options) (*Credentials, error) { default: return nil, fmt.Errorf("detect: unsupported filetype %q", fileType) } - return newCredentials(auth.NewCachedTokenProvider(tp, &auth.CachedTokenProviderOptions{ - ExpireEarly: opts.EarlyTokenRefresh, - }), b, projectID, quotaProjectID, universeDomain), nil + return auth.NewCredentials(&auth.CredentialsOptions{ + TokenProvider: auth.NewCachedTokenProvider(tp, &auth.CachedTokenProviderOptions{ + ExpireEarly: opts.EarlyTokenRefresh, + }), + JSON: b, + ProjectIDProvider: internalauth.StaticCredentialsProperty(projectID), + QuotaProjectIDProvider: internalauth.StaticCredentialsProperty(quotaProjectID), + UniverseDomainProvider: internalauth.StaticCredentialsProperty(universeDomain), + }), nil } -func handleServiceAccount(f *internaldetect.ServiceAccountFile, opts *Options) (auth.TokenProvider, error) { +func handleServiceAccount(f *internaldetect.ServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) { if opts.UseSelfSignedJWT { return configureSelfSignedJWT(f, opts) } @@ -123,7 +130,7 @@ func handleServiceAccount(f *internaldetect.ServiceAccountFile, opts *Options) ( return auth.New2LOTokenProvider(opts2LO) } -func handleUserCredential(f *internaldetect.UserCredentialsFile, opts *Options) (auth.TokenProvider, error) { +func handleUserCredential(f *internaldetect.UserCredentialsFile, opts *DetectOptions) (auth.TokenProvider, error) { opts3LO := &auth.Options3LO{ ClientID: f.ClientID, ClientSecret: f.ClientSecret, @@ -137,7 +144,7 @@ func handleUserCredential(f *internaldetect.UserCredentialsFile, opts *Options) return auth.New3LOTokenProvider(opts3LO) } -func handleExternalAccount(f *internaldetect.ExternalAccountFile, opts *Options) (auth.TokenProvider, error) { +func handleExternalAccount(f *internaldetect.ExternalAccountFile, opts *DetectOptions) (auth.TokenProvider, error) { externalOpts := &externalaccount.Options{ Audience: f.Audience, SubjectTokenType: f.SubjectTokenType, @@ -156,7 +163,7 @@ func handleExternalAccount(f *internaldetect.ExternalAccountFile, opts *Options) return externalaccount.NewTokenProvider(externalOpts) } -func handleExternalAccountAuthorizedUser(f *internaldetect.ExternalAccountAuthorizedUserFile, opts *Options) (auth.TokenProvider, error) { +func handleExternalAccountAuthorizedUser(f *internaldetect.ExternalAccountAuthorizedUserFile, opts *DetectOptions) (auth.TokenProvider, error) { externalOpts := &externalaccountuser.Options{ Audience: f.Audience, RefreshToken: f.RefreshToken, @@ -170,7 +177,7 @@ func handleExternalAccountAuthorizedUser(f *internaldetect.ExternalAccountAuthor return externalaccountuser.NewTokenProvider(externalOpts) } -func handleImpersonatedServiceAccount(f *internaldetect.ImpersonatedServiceAccountFile, opts *Options) (auth.TokenProvider, error) { +func handleImpersonatedServiceAccount(f *internaldetect.ImpersonatedServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) { if f.ServiceAccountImpersonationURL == "" || f.CredSource == nil { return nil, errors.New("missing 'source_credentials' field or 'service_account_impersonation_url' in credentials") } @@ -188,7 +195,7 @@ func handleImpersonatedServiceAccount(f *internaldetect.ImpersonatedServiceAccou }) } -func handleGDCHServiceAccount(f *internaldetect.GDCHServiceAccountFile, opts *Options) (auth.TokenProvider, error) { +func handleGDCHServiceAccount(f *internaldetect.GDCHServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) { return gdch.NewTokenProvider(f, &gdch.Options{ STSAudience: opts.STSAudience, Client: opts.client(), diff --git a/auth/detect/internal/externalaccount/aws_provider.go b/auth/credentials/internal/externalaccount/aws_provider.go similarity index 100% rename from auth/detect/internal/externalaccount/aws_provider.go rename to auth/credentials/internal/externalaccount/aws_provider.go diff --git a/auth/detect/internal/externalaccount/aws_provider_test.go b/auth/credentials/internal/externalaccount/aws_provider_test.go similarity index 100% rename from auth/detect/internal/externalaccount/aws_provider_test.go rename to auth/credentials/internal/externalaccount/aws_provider_test.go diff --git a/auth/detect/internal/externalaccount/executable_provider.go b/auth/credentials/internal/externalaccount/executable_provider.go similarity index 100% rename from auth/detect/internal/externalaccount/executable_provider.go rename to auth/credentials/internal/externalaccount/executable_provider.go diff --git a/auth/detect/internal/externalaccount/executable_provider_test.go b/auth/credentials/internal/externalaccount/executable_provider_test.go similarity index 100% rename from auth/detect/internal/externalaccount/executable_provider_test.go rename to auth/credentials/internal/externalaccount/executable_provider_test.go diff --git a/auth/detect/internal/externalaccount/externalaccount.go b/auth/credentials/internal/externalaccount/externalaccount.go similarity index 98% rename from auth/detect/internal/externalaccount/externalaccount.go rename to auth/credentials/internal/externalaccount/externalaccount.go index 9e97f05e63f..e346e037d5f 100644 --- a/auth/detect/internal/externalaccount/externalaccount.go +++ b/auth/credentials/internal/externalaccount/externalaccount.go @@ -24,8 +24,8 @@ import ( "time" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect/internal/impersonate" - "cloud.google.com/go/auth/detect/internal/stsexchange" + "cloud.google.com/go/auth/credentials/internal/impersonate" + "cloud.google.com/go/auth/credentials/internal/stsexchange" "cloud.google.com/go/auth/internal/internaldetect" ) diff --git a/auth/detect/internal/externalaccount/externalaccount_test.go b/auth/credentials/internal/externalaccount/externalaccount_test.go similarity index 100% rename from auth/detect/internal/externalaccount/externalaccount_test.go rename to auth/credentials/internal/externalaccount/externalaccount_test.go diff --git a/auth/detect/internal/externalaccount/file_provider.go b/auth/credentials/internal/externalaccount/file_provider.go similarity index 100% rename from auth/detect/internal/externalaccount/file_provider.go rename to auth/credentials/internal/externalaccount/file_provider.go diff --git a/auth/detect/internal/externalaccount/file_provider_test.go b/auth/credentials/internal/externalaccount/file_provider_test.go similarity index 100% rename from auth/detect/internal/externalaccount/file_provider_test.go rename to auth/credentials/internal/externalaccount/file_provider_test.go diff --git a/auth/detect/internal/externalaccount/impersonate_test.go b/auth/credentials/internal/externalaccount/impersonate_test.go similarity index 100% rename from auth/detect/internal/externalaccount/impersonate_test.go rename to auth/credentials/internal/externalaccount/impersonate_test.go diff --git a/auth/detect/internal/externalaccount/info.go b/auth/credentials/internal/externalaccount/info.go similarity index 100% rename from auth/detect/internal/externalaccount/info.go rename to auth/credentials/internal/externalaccount/info.go diff --git a/auth/detect/internal/externalaccount/info_test.go b/auth/credentials/internal/externalaccount/info_test.go similarity index 100% rename from auth/detect/internal/externalaccount/info_test.go rename to auth/credentials/internal/externalaccount/info_test.go diff --git a/auth/detect/internal/externalaccount/testdata/3pi_cred.json b/auth/credentials/internal/externalaccount/testdata/3pi_cred.json similarity index 100% rename from auth/detect/internal/externalaccount/testdata/3pi_cred.json rename to auth/credentials/internal/externalaccount/testdata/3pi_cred.json diff --git a/auth/detect/internal/externalaccount/testdata/3pi_cred.txt b/auth/credentials/internal/externalaccount/testdata/3pi_cred.txt similarity index 100% rename from auth/detect/internal/externalaccount/testdata/3pi_cred.txt rename to auth/credentials/internal/externalaccount/testdata/3pi_cred.txt diff --git a/auth/detect/internal/externalaccount/url_provider.go b/auth/credentials/internal/externalaccount/url_provider.go similarity index 100% rename from auth/detect/internal/externalaccount/url_provider.go rename to auth/credentials/internal/externalaccount/url_provider.go diff --git a/auth/detect/internal/externalaccount/url_provider_test.go b/auth/credentials/internal/externalaccount/url_provider_test.go similarity index 100% rename from auth/detect/internal/externalaccount/url_provider_test.go rename to auth/credentials/internal/externalaccount/url_provider_test.go diff --git a/auth/detect/internal/externalaccountuser/externalaccountuser.go b/auth/credentials/internal/externalaccountuser/externalaccountuser.go similarity index 98% rename from auth/detect/internal/externalaccountuser/externalaccountuser.go rename to auth/credentials/internal/externalaccountuser/externalaccountuser.go index 6a94708c2e8..3d4276f8b34 100644 --- a/auth/detect/internal/externalaccountuser/externalaccountuser.go +++ b/auth/credentials/internal/externalaccountuser/externalaccountuser.go @@ -21,7 +21,7 @@ import ( "time" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect/internal/stsexchange" + "cloud.google.com/go/auth/credentials/internal/stsexchange" "cloud.google.com/go/auth/internal" ) diff --git a/auth/detect/internal/externalaccountuser/externalaccountuser_test.go b/auth/credentials/internal/externalaccountuser/externalaccountuser_test.go similarity index 98% rename from auth/detect/internal/externalaccountuser/externalaccountuser_test.go rename to auth/credentials/internal/externalaccountuser/externalaccountuser_test.go index 279e771b0b8..a54d0de87b2 100644 --- a/auth/detect/internal/externalaccountuser/externalaccountuser_test.go +++ b/auth/credentials/internal/externalaccountuser/externalaccountuser_test.go @@ -22,7 +22,7 @@ import ( "net/http/httptest" "testing" - "cloud.google.com/go/auth/detect/internal/stsexchange" + "cloud.google.com/go/auth/credentials/internal/stsexchange" "cloud.google.com/go/auth/internal" ) diff --git a/auth/detect/internal/gdch/gdch.go b/auth/credentials/internal/gdch/gdch.go similarity index 100% rename from auth/detect/internal/gdch/gdch.go rename to auth/credentials/internal/gdch/gdch.go diff --git a/auth/detect/internal/gdch/gdch_test.go b/auth/credentials/internal/gdch/gdch_test.go similarity index 100% rename from auth/detect/internal/gdch/gdch_test.go rename to auth/credentials/internal/gdch/gdch_test.go diff --git a/auth/detect/internal/impersonate/impersonate.go b/auth/credentials/internal/impersonate/impersonate.go similarity index 100% rename from auth/detect/internal/impersonate/impersonate.go rename to auth/credentials/internal/impersonate/impersonate.go diff --git a/auth/detect/internal/impersonate/impersonate_test.go b/auth/credentials/internal/impersonate/impersonate_test.go similarity index 100% rename from auth/detect/internal/impersonate/impersonate_test.go rename to auth/credentials/internal/impersonate/impersonate_test.go diff --git a/auth/detect/internal/stsexchange/sts_exchange.go b/auth/credentials/internal/stsexchange/sts_exchange.go similarity index 100% rename from auth/detect/internal/stsexchange/sts_exchange.go rename to auth/credentials/internal/stsexchange/sts_exchange.go diff --git a/auth/detect/internal/stsexchange/sts_exchange_test.go b/auth/credentials/internal/stsexchange/sts_exchange_test.go similarity index 100% rename from auth/detect/internal/stsexchange/sts_exchange_test.go rename to auth/credentials/internal/stsexchange/sts_exchange_test.go diff --git a/auth/detect/selfsignedjwt.go b/auth/credentials/selfsignedjwt.go similarity index 96% rename from auth/detect/selfsignedjwt.go rename to auth/credentials/selfsignedjwt.go index b670fd94ce1..c3ea76c1bff 100644 --- a/auth/detect/selfsignedjwt.go +++ b/auth/credentials/selfsignedjwt.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "context" @@ -34,7 +34,7 @@ var ( // configureSelfSignedJWT uses the private key in the service account to create // a JWT without making a network call. -func configureSelfSignedJWT(f *internaldetect.ServiceAccountFile, opts *Options) (auth.TokenProvider, error) { +func configureSelfSignedJWT(f *internaldetect.ServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) { pk, err := internal.ParseKey([]byte(f.PrivateKey)) if err != nil { return nil, fmt.Errorf("detect: could not parse key: %w", err) diff --git a/auth/detect/selfsignedjwt_test.go b/auth/credentials/selfsignedjwt_test.go similarity index 98% rename from auth/detect/selfsignedjwt_test.go rename to auth/credentials/selfsignedjwt_test.go index 31ef58ec26c..daa6b90c662 100644 --- a/auth/detect/selfsignedjwt_test.go +++ b/auth/credentials/selfsignedjwt_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package detect +package credentials import ( "bytes" @@ -45,7 +45,7 @@ func TestDefaultCredentials_SelfSignedJSON(t *testing.T) { if err != nil { t.Fatal(err) } - tp, err := DefaultCredentials(&Options{ + tp, err := DetectDefault(&DetectOptions{ CredentialsJSON: jsonKey, Audience: "audience", UseSelfSignedJWT: true, @@ -107,7 +107,7 @@ func TestDefaultCredentials_SelfSignedWithScope(t *testing.T) { if err != nil { t.Fatal(err) } - tp, err := DefaultCredentials(&Options{ + tp, err := DetectDefault(&DetectOptions{ CredentialsJSON: jsonKey, Scopes: []string{"scope1", "scope2"}, UseSelfSignedJWT: true, diff --git a/auth/downscope/example_test.go b/auth/downscope/example_test.go index bc12b7e9650..8a1567f96e4 100644 --- a/auth/downscope/example_test.go +++ b/auth/downscope/example_test.go @@ -18,7 +18,7 @@ import ( "context" "fmt" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/downscope" ) @@ -40,7 +40,7 @@ func ExampleNewTokenProvider() { // This Source can be initialized in multiple ways; the following example uses // Application Default Credentials. - baseProvider, err := detect.DefaultCredentials(&detect.Options{ + baseProvider, err := credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, }) tp, err := downscope.NewTokenProvider(&downscope.Options{BaseProvider: baseProvider, Rules: accessBoundary}) diff --git a/auth/downscope/integration_test.go b/auth/downscope/integration_test.go index 0535a70e004..e173282d17d 100644 --- a/auth/downscope/integration_test.go +++ b/auth/downscope/integration_test.go @@ -23,7 +23,7 @@ import ( "time" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/downscope" "cloud.google.com/go/auth/internal/testutil" "cloud.google.com/go/auth/internal/testutil/testgcs" @@ -39,7 +39,7 @@ const ( func TestDownscopedToken(t *testing.T) { testutil.IntegrationTestCheck(t) - creds, err := detect.DefaultCredentials(&detect.Options{ + creds, err := credentials.DetectDefault(&credentials.DetectOptions{ CredentialsFile: os.Getenv(envServiceAccountFile), Scopes: []string{rootTokenScope}, }) diff --git a/auth/grpctransport/directpath.go b/auth/grpctransport/directpath.go index 652d8feeeb1..b4bbdfc3f69 100644 --- a/auth/grpctransport/directpath.go +++ b/auth/grpctransport/directpath.go @@ -55,7 +55,7 @@ func checkDirectPathEndPoint(endpoint string) bool { return true } -func isTokenProviderDirectPathCompatible(tp auth.TokenProvider, opts *Options) bool { +func isTokenProviderDirectPathCompatible(tp auth.TokenProvider, _ *Options) bool { if tp == nil { return false } diff --git a/auth/grpctransport/grpctransport.go b/auth/grpctransport/grpctransport.go index 59480480ae3..a8bc1ff5cbd 100644 --- a/auth/grpctransport/grpctransport.go +++ b/auth/grpctransport/grpctransport.go @@ -21,11 +21,11 @@ import ( "net/http" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/internal/transport" "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" + grpccreds "google.golang.org/grpc/credentials" grpcinsecure "google.golang.org/grpc/credentials/insecure" ) @@ -70,7 +70,7 @@ type Options struct { TokenProvider auth.TokenProvider // DetectOpts configures settings for detect Application Default // Credentials. - DetectOpts *detect.Options + DetectOpts *credentials.DetectOptions // InternalOptions are NOT meant to be set directly by consumers of this // package, they should only be set by generated client code. @@ -99,7 +99,7 @@ func (o *Options) validate() error { return nil } -func (o *Options) resolveDetectOptions() *detect.Options { +func (o *Options) resolveDetectOptions() *credentials.DetectOptions { io := o.InternalOptions // soft-clone these so we are not updating a ref the user holds and may reuse do := transport.CloneDetectOptions(o.DetectOpts) @@ -200,7 +200,7 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er // Authentication can only be sent when communicating over a secure connection. if !opts.DisableAuthentication { metadata := opts.Metadata - creds, err := detect.DefaultCredentials(opts.resolveDetectOptions()) + creds, err := credentials.DetectDefault(opts.resolveDetectOptions()) if err != nil { return nil, err } @@ -209,7 +209,10 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er tp = opts.TokenProvider } - qp := creds.QuotaProjectID() + qp, err := creds.QuotaProjectID(ctx) + if err != nil { + return nil, err + } if qp != "" { if metadata == nil { metadata = make(map[string]string, 1) @@ -253,8 +256,8 @@ func (tp *grpcTokenProvider) GetRequestMetadata(ctx context.Context, uri ...stri return nil, err } if tp.secure { - ri, _ := credentials.RequestInfoFromContext(ctx) - if err = credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil { + ri, _ := grpccreds.RequestInfoFromContext(ctx) + if err = grpccreds.CheckSecurityLevel(ri.AuthInfo, grpccreds.PrivacyAndIntegrity); err != nil { return nil, fmt.Errorf("unable to transfer TokenProvider PerRPCCredentials: %v", err) } } diff --git a/auth/grpctransport/grpctransport_test.go b/auth/grpctransport/grpctransport_test.go index 15f0b1387a0..bcfa85d081d 100644 --- a/auth/grpctransport/grpctransport_test.go +++ b/auth/grpctransport/grpctransport_test.go @@ -21,7 +21,7 @@ import ( "testing" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" echo "cloud.google.com/go/auth/grpctransport/testdata" "github.com/google/go-cmp/cmp" "google.golang.org/grpc" @@ -88,7 +88,7 @@ func TestDial_FailsValidation(t *testing.T) { name: "has creds with disable options, cred file", opts: &Options{ DisableAuthentication: true, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "abc.123", }, }, @@ -97,7 +97,7 @@ func TestDial_FailsValidation(t *testing.T) { name: "has creds with disable options, cred json", opts: &Options{ DisableAuthentication: true, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsJSON: []byte(`{"foo":"bar"}`), }, }, @@ -117,17 +117,17 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { tests := []struct { name string in *Options - want *detect.Options + want *credentials.DetectOptions }{ { name: "base", in: &Options{ - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, @@ -138,12 +138,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { InternalOptions: &InternalOptions{ EnableJWTWithScope: true, }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -152,12 +152,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { { name: "self-signed, with aud", in: &Options{ - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: "aud", CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "aud", CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -170,11 +170,11 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"default"}, CredentialsFile: "/path/to/a/file", }, @@ -186,12 +186,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"non-default"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"non-default"}, CredentialsFile: "/path/to/a/file", }, @@ -203,12 +203,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: "non-default", CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "non-default", CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -220,11 +220,11 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { InternalOptions: &InternalOptions{ DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "default", CredentialsFile: "/path/to/a/file", }, @@ -280,7 +280,7 @@ func TestNewClient_DetectedServiceAccount(t *testing.T) { InternalOptions: &InternalOptions{ DefaultEndpoint: l.Addr().String(), }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: l.Addr().String(), CredentialsFile: "../internal/testdata/sa.json", UseSelfSignedJWT: true, diff --git a/auth/httptransport/httptransport.go b/auth/httptransport/httptransport.go index 016eb8f920a..f932b3152ff 100644 --- a/auth/httptransport/httptransport.go +++ b/auth/httptransport/httptransport.go @@ -21,7 +21,7 @@ import ( "net/http" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + detect "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/transport" ) @@ -58,7 +58,7 @@ type Options struct { ClientCertProvider ClientCertProvider // DetectOpts configures settings for detect Application Default // Credentials. - DetectOpts *detect.Options + DetectOpts *detect.DetectOptions // InternalOptions are NOT meant to be set directly by consumers of this // package, they should only be set by generated client code. @@ -88,7 +88,7 @@ func (o *Options) client() *http.Client { return nil } -func (o *Options) resolveDetectOptions() *detect.Options { +func (o *Options) resolveDetectOptions() *detect.DetectOptions { io := o.InternalOptions // soft-clone these so we are not updating a ref the user holds and may reuse do := transport.CloneDetectOptions(o.DetectOpts) diff --git a/auth/httptransport/httptransport_test.go b/auth/httptransport/httptransport_test.go index deadf0587f7..614b81f00db 100644 --- a/auth/httptransport/httptransport_test.go +++ b/auth/httptransport/httptransport_test.go @@ -22,7 +22,7 @@ import ( "testing" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/internal" "github.com/google/go-cmp/cmp" ) @@ -104,7 +104,7 @@ func TestNewClient_FailsValidation(t *testing.T) { name: "has creds with disable options, cred file", opts: &Options{ DisableAuthentication: true, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "abc.123", }, }, @@ -113,7 +113,7 @@ func TestNewClient_FailsValidation(t *testing.T) { name: "has creds with disable options, cred json", opts: &Options{ DisableAuthentication: true, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsJSON: []byte(`{"foo":"bar"}`), }, }, @@ -133,17 +133,17 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { tests := []struct { name string in *Options - want *detect.Options + want *credentials.DetectOptions }{ { name: "base", in: &Options{ - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, @@ -154,12 +154,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { InternalOptions: &InternalOptions{ EnableJWTWithScope: true, }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"scope"}, CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -168,12 +168,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { { name: "self-signed, with aud", in: &Options{ - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: "aud", CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "aud", CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -186,11 +186,11 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"default"}, CredentialsFile: "/path/to/a/file", }, @@ -202,12 +202,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Scopes: []string{"non-default"}, CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Scopes: []string{"non-default"}, CredentialsFile: "/path/to/a/file", }, @@ -219,12 +219,12 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { DefaultScopes: []string{"default"}, DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: "non-default", CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "non-default", CredentialsFile: "/path/to/a/file", UseSelfSignedJWT: true, @@ -236,11 +236,11 @@ func TestOptions_ResolveDetectOptions(t *testing.T) { InternalOptions: &InternalOptions{ DefaultAudience: "default", }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ CredentialsFile: "/path/to/a/file", }, }, - want: &detect.Options{ + want: &credentials.DetectOptions{ Audience: "default", CredentialsFile: "/path/to/a/file", }, @@ -277,7 +277,7 @@ func TestNewClient_DetectedServiceAccount(t *testing.T) { InternalOptions: &InternalOptions{ DefaultEndpoint: ts.URL, }, - DetectOpts: &detect.Options{ + DetectOpts: &credentials.DetectOptions{ Audience: ts.URL, CredentialsFile: "../internal/testdata/sa.json", UseSelfSignedJWT: true, diff --git a/auth/httptransport/transport.go b/auth/httptransport/transport.go index 13f076258ac..ad4019153b2 100644 --- a/auth/httptransport/transport.go +++ b/auth/httptransport/transport.go @@ -22,7 +22,7 @@ import ( "time" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/transport/cert" "go.opencensus.io/plugin/ochttp" @@ -57,11 +57,14 @@ func newTransport(base http.RoundTripper, opts *Options) (http.RoundTripper, err Key: opts.APIKey, } default: - creds, err := detect.DefaultCredentials(opts.resolveDetectOptions()) + creds, err := credentials.DetectDefault(opts.resolveDetectOptions()) + if err != nil { + return nil, err + } + qp, err := creds.QuotaProjectID(context.Background()) if err != nil { return nil, err } - qp := creds.QuotaProjectID() if qp != "" { if headers == nil { headers = make(map[string][]string, 1) diff --git a/auth/idtoken/file.go b/auth/idtoken/file.go index c904ba1094f..acc563f75d1 100644 --- a/auth/idtoken/file.go +++ b/auth/idtoken/file.go @@ -21,7 +21,7 @@ import ( "strings" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/impersonate" "cloud.google.com/go/auth/internal/internaldetect" ) @@ -86,7 +86,7 @@ func tokenProviderFromBytes(b []byte, opts *Options) (auth.TokenProvider, error) account := filepath.Base(accountURL.ServiceAccountImpersonationURL) account = strings.Split(account, ":")[0] - creds, err := detect.DefaultCredentials(&detect.Options{ + creds, err := credentials.DetectDefault(&credentials.DetectOptions{ Scopes: defaultScopes, CredentialsJSON: b, Client: opts.client(), diff --git a/auth/impersonate/integration_test.go b/auth/impersonate/integration_test.go index be4d240508f..dd4fc4967a4 100644 --- a/auth/impersonate/integration_test.go +++ b/auth/impersonate/integration_test.go @@ -24,7 +24,8 @@ import ( "testing" "time" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth" + "cloud.google.com/go/auth/credentials" "cloud.google.com/go/auth/idtoken" "cloud.google.com/go/auth/impersonate" "cloud.google.com/go/auth/internal/testutil" @@ -96,15 +97,15 @@ func TestCredentialsTokenSourceIntegration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - var creds *detect.Credentials + var creds *auth.Credentials if !tt.useDefaultCreds { var err error - creds, err = detect.DefaultCredentials(&detect.Options{ + creds, err = credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, CredentialsFile: tt.baseKeyFile, }) if err != nil { - t.Fatalf("detect.DefaultCredentials() = %v", err) + t.Fatalf("credentials.DetectDefault() = %v", err) } } @@ -161,15 +162,15 @@ func TestIDTokenSourceIntegration(t *testing.T) { for _, tt := range tests { name := tt.name t.Run(name, func(t *testing.T) { - var creds *detect.Credentials + var creds *auth.Credentials if !tt.useDefaultCreds { var err error - creds, err = detect.DefaultCredentials(&detect.Options{ + creds, err = credentials.DetectDefault(&credentials.DetectOptions{ Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, CredentialsFile: tt.baseKeyFile, }) if err != nil { - t.Fatalf("detect.DefaultCredentials() = %v", err) + t.Fatalf("credentials.DetectDefault() = %v", err) } } aud := "http://example.com/" diff --git a/auth/internal/internal.go b/auth/internal/internal.go index 8f0048eaf3c..66953bf9576 100644 --- a/auth/internal/internal.go +++ b/auth/internal/internal.go @@ -15,6 +15,7 @@ package internal import ( + "context" "crypto/rsa" "crypto/x509" "encoding/json" @@ -119,3 +120,17 @@ func GetProjectID(b []byte, override string) string { func ReadAll(r io.Reader) ([]byte, error) { return io.ReadAll(io.LimitReader(r, maxBodySize)) } + +// StaticCredentialsProperty is a helper for creating static credentials +// properties. +func StaticCredentialsProperty(s string) StaticProperty { + return StaticProperty(s) +} + +// StaticProperty always returns that value of the underlying string. +type StaticProperty string + +// GetProperty loads the properly value provided the given context. +func (p StaticProperty) GetProperty(context.Context) (string, error) { + return string(p), nil +} diff --git a/auth/internal/transport/transport.go b/auth/internal/transport/transport.go index e4db6b49d39..1cf75af7c31 100644 --- a/auth/internal/transport/transport.go +++ b/auth/internal/transport/transport.go @@ -16,18 +16,18 @@ // (grpctransport and httptransport). package transport -import "cloud.google.com/go/auth/detect" +import "cloud.google.com/go/auth/credentials" // CloneDetectOptions clones a user set detect option into some new memory that // we can internally manipulate before sending onto the detect package. -func CloneDetectOptions(oldDo *detect.Options) *detect.Options { +func CloneDetectOptions(oldDo *credentials.DetectOptions) *credentials.DetectOptions { if oldDo == nil { // it is valid for users not to set this, but we will need to to default // some options for them in this case so return some initialized memory // to work with. - return &detect.Options{} + return &credentials.DetectOptions{} } - newDo := &detect.Options{ + newDo := &credentials.DetectOptions{ // Simple types Audience: oldDo.Audience, Subject: oldDo.Subject, diff --git a/auth/internal/transport/transport_test.go b/auth/internal/transport/transport_test.go index 8e2e0a50a77..ecd444cc7ae 100644 --- a/auth/internal/transport/transport_test.go +++ b/auth/internal/transport/transport_test.go @@ -20,7 +20,7 @@ import ( "testing" "cloud.google.com/go/auth" - "cloud.google.com/go/auth/detect" + "cloud.google.com/go/auth/credentials" ) // TestCloneDetectOptions_FieldTest is meant to fail every time a new field is @@ -30,7 +30,7 @@ import ( // relevant fields. func TestCloneDetectOptions_FieldTest(t *testing.T) { const WantNumberOfFields = 11 - o := detect.Options{} + o := credentials.DetectOptions{} got := reflect.TypeOf(o).NumField() if got != WantNumberOfFields { t.Errorf("if this fails please read comment above the test: got %v, want %v", got, WantNumberOfFields) @@ -38,7 +38,7 @@ func TestCloneDetectOptions_FieldTest(t *testing.T) { } func TestCloneDetectOptions(t *testing.T) { - oldDo := &detect.Options{ + oldDo := &credentials.DetectOptions{ Audience: "aud", Subject: "sub", EarlyTokenRefresh: 42,