default.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2015 The oauth2 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/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, it fetches credentials from the metadata server.
  49. // (In this final case any provided scopes are ignored.)
  50. //
  51. // For more details, see:
  52. // https://developers.google.com/accounts/application-default-credentials
  53. //
  54. func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
  55. // First, try the environment variable.
  56. const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
  57. if filename := os.Getenv(envVar); filename != "" {
  58. ts, err := tokenSourceFromFile(ctx, filename, scope)
  59. if err != nil {
  60. return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
  61. }
  62. return ts, nil
  63. }
  64. // Second, try a well-known file.
  65. filename := wellKnownFile()
  66. _, err := os.Stat(filename)
  67. if err == nil {
  68. ts, err2 := tokenSourceFromFile(ctx, filename, scope)
  69. if err2 == nil {
  70. return ts, nil
  71. }
  72. err = err2
  73. } else if os.IsNotExist(err) {
  74. err = nil // ignore this error
  75. }
  76. if err != nil {
  77. return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
  78. }
  79. // Third, if we're on Google App Engine use those credentials.
  80. if appengineTokenFunc != nil {
  81. return AppEngineTokenSource(ctx, scope...), nil
  82. }
  83. // Fourth, if we're on Google Compute Engine use the metadata server.
  84. if metadata.OnGCE() {
  85. return ComputeTokenSource(""), nil
  86. }
  87. // None are found; return helpful error.
  88. const url = "https://developers.google.com/accounts/application-default-credentials"
  89. return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
  90. }
  91. func wellKnownFile() string {
  92. const f = "application_default_credentials.json"
  93. if runtime.GOOS == "windows" {
  94. return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
  95. }
  96. return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
  97. }
  98. func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) {
  99. b, err := ioutil.ReadFile(filename)
  100. if err != nil {
  101. return nil, err
  102. }
  103. var d struct {
  104. // Common fields
  105. Type string
  106. ClientID string `json:"client_id"`
  107. // User Credential fields
  108. ClientSecret string `json:"client_secret"`
  109. RefreshToken string `json:"refresh_token"`
  110. // Service Account fields
  111. ClientEmail string `json:"client_email"`
  112. PrivateKeyID string `json:"private_key_id"`
  113. PrivateKey string `json:"private_key"`
  114. }
  115. if err := json.Unmarshal(b, &d); err != nil {
  116. return nil, err
  117. }
  118. switch d.Type {
  119. case "authorized_user":
  120. cfg := &oauth2.Config{
  121. ClientID: d.ClientID,
  122. ClientSecret: d.ClientSecret,
  123. Scopes: append([]string{}, scopes...), // copy
  124. Endpoint: Endpoint,
  125. }
  126. tok := &oauth2.Token{RefreshToken: d.RefreshToken}
  127. return cfg.TokenSource(ctx, tok), nil
  128. case "service_account":
  129. cfg := &jwt.Config{
  130. Email: d.ClientEmail,
  131. PrivateKey: []byte(d.PrivateKey),
  132. Scopes: append([]string{}, scopes...), // copy
  133. TokenURL: JWTTokenURL,
  134. }
  135. return cfg.TokenSource(ctx), nil
  136. case "":
  137. return nil, errors.New("missing 'type' field in credentials")
  138. default:
  139. return nil, fmt.Errorf("unknown credential type: %q", d.Type)
  140. }
  141. }