default.go 8.7 KB

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