1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package google 6 7import ( 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "os" 13 "path/filepath" 14 "runtime" 15 16 "cloud.google.com/go/compute/metadata" 17 "golang.org/x/net/context" 18 "golang.org/x/oauth2" 19) 20 21// DefaultCredentials holds "Application Default Credentials". 22// For more details, see: 23// https://developers.google.com/accounts/docs/application-default-credentials 24type DefaultCredentials struct { 25 ProjectID string // may be empty 26 TokenSource oauth2.TokenSource 27 28 // JSON contains the raw bytes from a JSON credentials file. 29 // This field may be nil if authentication is provided by the 30 // environment and not with a credentials file, e.g. when code is 31 // running on Google Cloud Platform. 32 JSON []byte 33} 34 35// DefaultClient returns an HTTP Client that uses the 36// DefaultTokenSource to obtain authentication credentials. 37func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { 38 ts, err := DefaultTokenSource(ctx, scope...) 39 if err != nil { 40 return nil, err 41 } 42 return oauth2.NewClient(ctx, ts), nil 43} 44 45// DefaultTokenSource returns the token source for 46// "Application Default Credentials". 47// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource. 48func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { 49 creds, err := FindDefaultCredentials(ctx, scope...) 50 if err != nil { 51 return nil, err 52 } 53 return creds.TokenSource, nil 54} 55 56// FindDefaultCredentials searches for "Application Default Credentials". 57// 58// It looks for credentials in the following places, 59// preferring the first location found: 60// 61// 1. A JSON file whose path is specified by the 62// GOOGLE_APPLICATION_CREDENTIALS environment variable. 63// 2. A JSON file in a location known to the gcloud command-line tool. 64// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. 65// On other systems, $HOME/.config/gcloud/application_default_credentials.json. 66// 3. On Google App Engine it uses the appengine.AccessToken function. 67// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches 68// credentials from the metadata server. 69// (In this final case any provided scopes are ignored.) 70func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCredentials, error) { 71 // First, try the environment variable. 72 const envVar = "GOOGLE_APPLICATION_CREDENTIALS" 73 if filename := os.Getenv(envVar); filename != "" { 74 creds, err := readCredentialsFile(ctx, filename, scope) 75 if err != nil { 76 return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) 77 } 78 return creds, nil 79 } 80 81 // Second, try a well-known file. 82 filename := wellKnownFile() 83 if creds, err := readCredentialsFile(ctx, filename, scope); err == nil { 84 return creds, nil 85 } else if !os.IsNotExist(err) { 86 return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) 87 } 88 89 // Third, if we're on Google App Engine use those credentials. 90 if appengineTokenFunc != nil && !appengineFlex { 91 return &DefaultCredentials{ 92 ProjectID: appengineAppIDFunc(ctx), 93 TokenSource: AppEngineTokenSource(ctx, scope...), 94 }, nil 95 } 96 97 // Fourth, if we're on Google Compute Engine use the metadata server. 98 if metadata.OnGCE() { 99 id, _ := metadata.ProjectID() 100 return &DefaultCredentials{ 101 ProjectID: id, 102 TokenSource: ComputeTokenSource(""), 103 }, nil 104 } 105 106 // None are found; return helpful error. 107 const url = "https://developers.google.com/accounts/docs/application-default-credentials" 108 return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) 109} 110 111func wellKnownFile() string { 112 const f = "application_default_credentials.json" 113 if runtime.GOOS == "windows" { 114 return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) 115 } 116 return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) 117} 118 119func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) { 120 b, err := ioutil.ReadFile(filename) 121 if err != nil { 122 return nil, err 123 } 124 var f credentialsFile 125 if err := json.Unmarshal(b, &f); err != nil { 126 return nil, err 127 } 128 ts, err := f.tokenSource(ctx, append([]string(nil), scopes...)) 129 if err != nil { 130 return nil, err 131 } 132 return &DefaultCredentials{ 133 ProjectID: f.ProjectID, 134 TokenSource: ts, 135 JSON: b, 136 }, nil 137} 138