default.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. package google
  5. import (
  6. "context"
  7. "encoding/json"
  8. "fmt"
  9. "io/ioutil"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "runtime"
  14. "time"
  15. "cloud.google.com/go/compute/metadata"
  16. "golang.org/x/oauth2"
  17. "golang.org/x/oauth2/authhandler"
  18. )
  19. const adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc"
  20. // Credentials holds Google credentials, including "Application Default Credentials".
  21. // For more details, see:
  22. // https://developers.google.com/accounts/docs/application-default-credentials
  23. // Credentials from external accounts (workload identity federation) are used to
  24. // identify a particular application from an on-prem or non-Google Cloud platform
  25. // including Amazon Web Services (AWS), Microsoft Azure or any identity provider
  26. // that supports OpenID Connect (OIDC).
  27. type Credentials struct {
  28. ProjectID string // may be empty
  29. TokenSource oauth2.TokenSource
  30. // JSON contains the raw bytes from a JSON credentials file.
  31. // This field may be nil if authentication is provided by the
  32. // environment and not with a credentials file, e.g. when code is
  33. // running on Google Cloud Platform.
  34. JSON []byte
  35. }
  36. // DefaultCredentials is the old name of Credentials.
  37. //
  38. // Deprecated: use Credentials instead.
  39. type DefaultCredentials = Credentials
  40. // CredentialsParams holds user supplied parameters that are used together
  41. // with a credentials file for building a Credentials object.
  42. type CredentialsParams struct {
  43. // Scopes is the list OAuth scopes. Required.
  44. // Example: https://www.googleapis.com/auth/cloud-platform
  45. Scopes []string
  46. // Subject is the user email used for domain wide delegation (see
  47. // https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority).
  48. // Optional.
  49. Subject string
  50. // AuthHandler is the AuthorizationHandler used for 3-legged OAuth flow. Required for 3LO flow.
  51. AuthHandler authhandler.AuthorizationHandler
  52. // State is a unique string used with AuthHandler. Required for 3LO flow.
  53. State string
  54. // PKCE is used to support PKCE flow. Optional for 3LO flow.
  55. PKCE *authhandler.PKCEParams
  56. // The OAuth2 TokenURL default override. This value overrides the default TokenURL,
  57. // unless explicitly specified by the credentials config file. Optional.
  58. TokenURL string
  59. // EarlyTokenRefresh is the amount of time before a token expires that a new
  60. // token will be preemptively fetched. If unset the default value is 10
  61. // seconds.
  62. //
  63. // Note: This option is currently only respected when using credentials
  64. // fetched from the GCE metadata server.
  65. EarlyTokenRefresh time.Duration
  66. }
  67. func (params CredentialsParams) deepCopy() CredentialsParams {
  68. paramsCopy := params
  69. paramsCopy.Scopes = make([]string, len(params.Scopes))
  70. copy(paramsCopy.Scopes, params.Scopes)
  71. return paramsCopy
  72. }
  73. // DefaultClient returns an HTTP Client that uses the
  74. // DefaultTokenSource to obtain authentication credentials.
  75. func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
  76. ts, err := DefaultTokenSource(ctx, scope...)
  77. if err != nil {
  78. return nil, err
  79. }
  80. return oauth2.NewClient(ctx, ts), nil
  81. }
  82. // DefaultTokenSource returns the token source for
  83. // "Application Default Credentials".
  84. // It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource.
  85. func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
  86. creds, err := FindDefaultCredentials(ctx, scope...)
  87. if err != nil {
  88. return nil, err
  89. }
  90. return creds.TokenSource, nil
  91. }
  92. // FindDefaultCredentialsWithParams searches for "Application Default Credentials".
  93. //
  94. // It looks for credentials in the following places,
  95. // preferring the first location found:
  96. //
  97. // 1. A JSON file whose path is specified by the
  98. // GOOGLE_APPLICATION_CREDENTIALS environment variable.
  99. // For workload identity federation, refer to
  100. // https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation on
  101. // how to generate the JSON configuration file for on-prem/non-Google cloud
  102. // platforms.
  103. // 2. A JSON file in a location known to the gcloud command-line tool.
  104. // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
  105. // On other systems, $HOME/.config/gcloud/application_default_credentials.json.
  106. // 3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses
  107. // the appengine.AccessToken function.
  108. // 4. On Google Compute Engine, Google App Engine standard second generation runtimes
  109. // (>= Go 1.11), and Google App Engine flexible environment, it fetches
  110. // credentials from the metadata server.
  111. func FindDefaultCredentialsWithParams(ctx context.Context, params CredentialsParams) (*Credentials, error) {
  112. // Make defensive copy of the slices in params.
  113. params = params.deepCopy()
  114. // First, try the environment variable.
  115. const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
  116. if filename := os.Getenv(envVar); filename != "" {
  117. creds, err := readCredentialsFile(ctx, filename, params)
  118. if err != nil {
  119. return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
  120. }
  121. return creds, nil
  122. }
  123. // Second, try a well-known file.
  124. filename := wellKnownFile()
  125. if creds, err := readCredentialsFile(ctx, filename, params); err == nil {
  126. return creds, nil
  127. } else if !os.IsNotExist(err) {
  128. return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
  129. }
  130. // Third, if we're on a Google App Engine standard first generation runtime (<= Go 1.9)
  131. // use those credentials. App Engine standard second generation runtimes (>= Go 1.11)
  132. // and App Engine flexible use ComputeTokenSource and the metadata server.
  133. if appengineTokenFunc != nil {
  134. return &Credentials{
  135. ProjectID: appengineAppIDFunc(ctx),
  136. TokenSource: AppEngineTokenSource(ctx, params.Scopes...),
  137. }, nil
  138. }
  139. // Fourth, if we're on Google Compute Engine, an App Engine standard second generation runtime,
  140. // or App Engine flexible, use the metadata server.
  141. if metadata.OnGCE() {
  142. id, _ := metadata.ProjectID()
  143. return &Credentials{
  144. ProjectID: id,
  145. TokenSource: computeTokenSource("", params.EarlyTokenRefresh, params.Scopes...),
  146. }, nil
  147. }
  148. // None are found; return helpful error.
  149. return nil, fmt.Errorf("google: could not find default credentials. See %v for more information", adcSetupURL)
  150. }
  151. // FindDefaultCredentials invokes FindDefaultCredentialsWithParams with the specified scopes.
  152. func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
  153. var params CredentialsParams
  154. params.Scopes = scopes
  155. return FindDefaultCredentialsWithParams(ctx, params)
  156. }
  157. // CredentialsFromJSONWithParams obtains Google credentials from a JSON value. The JSON can
  158. // represent either a Google Developers Console client_credentials.json file (as in ConfigFromJSON),
  159. // a Google Developers service account key file, a gcloud user credentials file (a.k.a. refresh
  160. // token JSON), or the JSON configuration file for workload identity federation in non-Google cloud
  161. // platforms (see https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation).
  162. func CredentialsFromJSONWithParams(ctx context.Context, jsonData []byte, params CredentialsParams) (*Credentials, error) {
  163. // Make defensive copy of the slices in params.
  164. params = params.deepCopy()
  165. // First, attempt to parse jsonData as a Google Developers Console client_credentials.json.
  166. config, _ := ConfigFromJSON(jsonData, params.Scopes...)
  167. if config != nil {
  168. return &Credentials{
  169. ProjectID: "",
  170. TokenSource: authhandler.TokenSourceWithPKCE(ctx, config, params.State, params.AuthHandler, params.PKCE),
  171. JSON: jsonData,
  172. }, nil
  173. }
  174. // Otherwise, parse jsonData as one of the other supported credentials files.
  175. var f credentialsFile
  176. if err := json.Unmarshal(jsonData, &f); err != nil {
  177. return nil, err
  178. }
  179. ts, err := f.tokenSource(ctx, params)
  180. if err != nil {
  181. return nil, err
  182. }
  183. ts = newErrWrappingTokenSource(ts)
  184. return &Credentials{
  185. ProjectID: f.ProjectID,
  186. TokenSource: ts,
  187. JSON: jsonData,
  188. }, nil
  189. }
  190. // CredentialsFromJSON invokes CredentialsFromJSONWithParams with the specified scopes.
  191. func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
  192. var params CredentialsParams
  193. params.Scopes = scopes
  194. return CredentialsFromJSONWithParams(ctx, jsonData, params)
  195. }
  196. func wellKnownFile() string {
  197. const f = "application_default_credentials.json"
  198. if runtime.GOOS == "windows" {
  199. return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
  200. }
  201. return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
  202. }
  203. func readCredentialsFile(ctx context.Context, filename string, params CredentialsParams) (*Credentials, error) {
  204. b, err := ioutil.ReadFile(filename)
  205. if err != nil {
  206. return nil, err
  207. }
  208. return CredentialsFromJSONWithParams(ctx, b, params)
  209. }