default.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "runtime"
  14. "golang.org/x/net/context"
  15. "golang.org/x/oauth2"
  16. "golang.org/x/oauth2/jwt"
  17. "google.golang.org/cloud/compute/metadata"
  18. )
  19. // DefaultClient returns an HTTP Client that uses the
  20. // DefaultTokenSource to obtain authentication credentials.
  21. //
  22. // This client should be used when developing services
  23. // that run on Google App Engine or Google Compute Engine
  24. // and use "Application Default Credentials."
  25. //
  26. // For more details, see:
  27. // https://developers.google.com/accounts/docs/application-default-credentials
  28. //
  29. func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
  30. ts, err := DefaultTokenSource(ctx, scope...)
  31. if err != nil {
  32. return nil, err
  33. }
  34. return oauth2.NewClient(ctx, ts), nil
  35. }
  36. // DefaultTokenSource is a token source that uses
  37. // "Application Default Credentials".
  38. //
  39. // It looks for credentials in the following places,
  40. // preferring the first location found:
  41. //
  42. // 1. A JSON file whose path is specified by the
  43. // GOOGLE_APPLICATION_CREDENTIALS environment variable.
  44. // 2. A JSON file in a location known to the gcloud command-line tool.
  45. // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
  46. // On other systems, $HOME/.config/gcloud/application_default_credentials.json.
  47. // 3. On Google App Engine it uses the appengine.AccessToken function.
  48. // 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
  49. // credentials from the metadata server.
  50. // (In this final case any provided scopes are ignored.)
  51. //
  52. // For more details, see:
  53. // https://developers.google.com/accounts/docs/application-default-credentials
  54. //
  55. func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
  56. // First, try the environment variable.
  57. const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
  58. if filename := os.Getenv(envVar); filename != "" {
  59. ts, err := tokenSourceFromFile(ctx, filename, scope)
  60. if err != nil {
  61. return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
  62. }
  63. return ts, nil
  64. }
  65. // Second, try a well-known file.
  66. filename := wellKnownFile()
  67. _, err := os.Stat(filename)
  68. if err == nil {
  69. ts, err2 := tokenSourceFromFile(ctx, filename, scope)
  70. if err2 == nil {
  71. return ts, nil
  72. }
  73. err = err2
  74. } else if os.IsNotExist(err) {
  75. err = nil // ignore this error
  76. }
  77. if err != nil {
  78. return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
  79. }
  80. // Third, if we're on Google App Engine use those credentials.
  81. if appengineTokenFunc != nil && !appengineVM {
  82. return AppEngineTokenSource(ctx, scope...), nil
  83. }
  84. // Fourth, if we're on Google Compute Engine use the metadata server.
  85. if metadata.OnGCE() {
  86. return ComputeTokenSource(""), nil
  87. }
  88. // None are found; return helpful error.
  89. const url = "https://developers.google.com/accounts/docs/application-default-credentials"
  90. return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
  91. }
  92. func wellKnownFile() string {
  93. const f = "application_default_credentials.json"
  94. if runtime.GOOS == "windows" {
  95. return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
  96. }
  97. return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
  98. }
  99. func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) {
  100. b, err := ioutil.ReadFile(filename)
  101. if err != nil {
  102. return nil, err
  103. }
  104. var d struct {
  105. // Common fields
  106. Type string
  107. ClientID string `json:"client_id"`
  108. // User Credential fields
  109. ClientSecret string `json:"client_secret"`
  110. RefreshToken string `json:"refresh_token"`
  111. // Service Account fields
  112. ClientEmail string `json:"client_email"`
  113. PrivateKeyID string `json:"private_key_id"`
  114. PrivateKey string `json:"private_key"`
  115. }
  116. if err := json.Unmarshal(b, &d); err != nil {
  117. return nil, err
  118. }
  119. switch d.Type {
  120. case "authorized_user":
  121. cfg := &oauth2.Config{
  122. ClientID: d.ClientID,
  123. ClientSecret: d.ClientSecret,
  124. Scopes: append([]string{}, scopes...), // copy
  125. Endpoint: Endpoint,
  126. }
  127. tok := &oauth2.Token{RefreshToken: d.RefreshToken}
  128. return cfg.TokenSource(ctx, tok), nil
  129. case "service_account":
  130. cfg := &jwt.Config{
  131. Email: d.ClientEmail,
  132. PrivateKey: []byte(d.PrivateKey),
  133. Scopes: append([]string{}, scopes...), // copy
  134. TokenURL: JWTTokenURL,
  135. }
  136. return cfg.TokenSource(ctx), nil
  137. case "":
  138. return nil, errors.New("missing 'type' field in credentials")
  139. default:
  140. return nil, fmt.Errorf("unknown credential type: %q", d.Type)
  141. }
  142. }