| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package google |
| |
| import ( |
| "context" |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "os" |
| "path/filepath" |
| "runtime" |
| |
| "cloud.google.com/go/compute/metadata" |
| "golang.org/x/oauth2" |
| ) |
| |
| // Credentials holds Google credentials, including "Application Default Credentials". |
| // For more details, see: |
| // https://developers.google.com/accounts/docs/application-default-credentials |
| type Credentials struct { |
| ProjectID string // may be empty |
| TokenSource oauth2.TokenSource |
| |
| // JSON contains the raw bytes from a JSON credentials file. |
| // This field may be nil if authentication is provided by the |
| // environment and not with a credentials file, e.g. when code is |
| // running on Google Cloud Platform. |
| JSON []byte |
| } |
| |
| // DefaultCredentials is the old name of Credentials. |
| // |
| // Deprecated: use Credentials instead. |
| type DefaultCredentials = Credentials |
| |
| // DefaultClient returns an HTTP Client that uses the |
| // DefaultTokenSource to obtain authentication credentials. |
| func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { |
| ts, err := DefaultTokenSource(ctx, scope...) |
| if err != nil { |
| return nil, err |
| } |
| return oauth2.NewClient(ctx, ts), nil |
| } |
| |
| // DefaultTokenSource returns the token source for |
| // "Application Default Credentials". |
| // It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource. |
| func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { |
| creds, err := FindDefaultCredentials(ctx, scope...) |
| if err != nil { |
| return nil, err |
| } |
| return creds.TokenSource, nil |
| } |
| |
| // FindDefaultCredentials searches for "Application Default Credentials". |
| // |
| // It looks for credentials in the following places, |
| // preferring the first location found: |
| // |
| // 1. A JSON file whose path is specified by the |
| // GOOGLE_APPLICATION_CREDENTIALS environment variable. |
| // 2. A JSON file in a location known to the gcloud command-line tool. |
| // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. |
| // On other systems, $HOME/.config/gcloud/application_default_credentials.json. |
| // 3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses |
| // the appengine.AccessToken function. |
| // 4. On Google Compute Engine, Google App Engine standard second generation runtimes |
| // (>= Go 1.11), and Google App Engine flexible environment, it fetches |
| // credentials from the metadata server. |
| func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) { |
| // First, try the environment variable. |
| const envVar = "GOOGLE_APPLICATION_CREDENTIALS" |
| if filename := os.Getenv(envVar); filename != "" { |
| creds, err := readCredentialsFile(ctx, filename, scopes) |
| if err != nil { |
| return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) |
| } |
| return creds, nil |
| } |
| |
| // Second, try a well-known file. |
| filename := wellKnownFile() |
| if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil { |
| return creds, nil |
| } else if !os.IsNotExist(err) { |
| return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) |
| } |
| |
| // Third, if we're on a Google App Engine standard first generation runtime (<= Go 1.9) |
| // use those credentials. App Engine standard second generation runtimes (>= Go 1.11) |
| // and App Engine flexible use ComputeTokenSource and the metadata server. |
| if appengineTokenFunc != nil { |
| return &DefaultCredentials{ |
| ProjectID: appengineAppIDFunc(ctx), |
| TokenSource: AppEngineTokenSource(ctx, scopes...), |
| }, nil |
| } |
| |
| // Fourth, if we're on Google Compute Engine, an App Engine standard second generation runtime, |
| // or App Engine flexible, use the metadata server. |
| if metadata.OnGCE() { |
| id, _ := metadata.ProjectID() |
| return &DefaultCredentials{ |
| ProjectID: id, |
| TokenSource: ComputeTokenSource("", scopes...), |
| }, nil |
| } |
| |
| // None are found; return helpful error. |
| const url = "https://developers.google.com/accounts/docs/application-default-credentials" |
| return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) |
| } |
| |
| // CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can |
| // represent either a Google Developers Console client_credentials.json file (as in |
| // ConfigFromJSON) or a Google Developers service account key file (as in |
| // JWTConfigFromJSON). |
| func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) { |
| var f credentialsFile |
| if err := json.Unmarshal(jsonData, &f); err != nil { |
| return nil, err |
| } |
| ts, err := f.tokenSource(ctx, append([]string(nil), scopes...)) |
| if err != nil { |
| return nil, err |
| } |
| return &DefaultCredentials{ |
| ProjectID: f.ProjectID, |
| TokenSource: ts, |
| JSON: jsonData, |
| }, nil |
| } |
| |
| func wellKnownFile() string { |
| const f = "application_default_credentials.json" |
| if runtime.GOOS == "windows" { |
| return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) |
| } |
| return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) |
| } |
| |
| func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) { |
| b, err := ioutil.ReadFile(filename) |
| if err != nil { |
| return nil, err |
| } |
| return CredentialsFromJSON(ctx, b, scopes...) |
| } |