default.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. "sync"
  14. "time"
  15. "cloud.google.com/go/compute/metadata"
  16. "golang.org/x/oauth2"
  17. "golang.org/x/oauth2/authhandler"
  18. )
  19. const (
  20. adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc"
  21. universeDomainDefault = "googleapis.com"
  22. )
  23. // Credentials holds Google credentials, including "Application Default Credentials".
  24. // For more details, see:
  25. // https://developers.google.com/accounts/docs/application-default-credentials
  26. // Credentials from external accounts (workload identity federation) are used to
  27. // identify a particular application from an on-prem or non-Google Cloud platform
  28. // including Amazon Web Services (AWS), Microsoft Azure or any identity provider
  29. // that supports OpenID Connect (OIDC).
  30. type Credentials struct {
  31. ProjectID string // may be empty
  32. TokenSource oauth2.TokenSource
  33. // JSON contains the raw bytes from a JSON credentials file.
  34. // This field may be nil if authentication is provided by the
  35. // environment and not with a credentials file, e.g. when code is
  36. // running on Google Cloud Platform.
  37. JSON []byte
  38. udMu sync.Mutex // guards universeDomain
  39. // universeDomain is the default service domain for a given Cloud universe.
  40. universeDomain string
  41. }
  42. // UniverseDomain returns the default service domain for a given Cloud universe.
  43. //
  44. // The default value is "googleapis.com".
  45. //
  46. // Deprecated: Use instead (*Credentials).GetUniverseDomain(), which supports
  47. // obtaining the universe domain when authenticating via the GCE metadata server.
  48. // Unlike GetUniverseDomain, this method, UniverseDomain, will always return the
  49. // default value when authenticating via the GCE metadata server.
  50. // See also [The attached service account](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa).
  51. func (c *Credentials) UniverseDomain() string {
  52. if c.universeDomain == "" {
  53. return universeDomainDefault
  54. }
  55. return c.universeDomain
  56. }
  57. // GetUniverseDomain returns the default service domain for a given Cloud
  58. // universe.
  59. //
  60. // The default value is "googleapis.com".
  61. //
  62. // It obtains the universe domain from the attached service account on GCE when
  63. // authenticating via the GCE metadata server. See also [The attached service
  64. // account](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa).
  65. // If the GCE metadata server returns a 404 error, the default value is
  66. // returned. If the GCE metadata server returns an error other than 404, the
  67. // error is returned.
  68. func (c *Credentials) GetUniverseDomain() (string, error) {
  69. c.udMu.Lock()
  70. defer c.udMu.Unlock()
  71. if c.universeDomain == "" && metadata.OnGCE() {
  72. // If we're on Google Compute Engine, an App Engine standard second
  73. // generation runtime, or App Engine flexible, use the metadata server.
  74. err := c.computeUniverseDomain()
  75. if err != nil {
  76. return "", err
  77. }
  78. }
  79. // If not on Google Compute Engine, or in case of any non-error path in
  80. // computeUniverseDomain that did not set universeDomain, set the default
  81. // universe domain.
  82. if c.universeDomain == "" {
  83. c.universeDomain = universeDomainDefault
  84. }
  85. return c.universeDomain, nil
  86. }
  87. // computeUniverseDomain fetches the default service domain for a given Cloud
  88. // universe from Google Compute Engine (GCE)'s metadata server. It's only valid
  89. // to use this method if your program is running on a GCE instance.
  90. func (c *Credentials) computeUniverseDomain() error {
  91. var err error
  92. c.universeDomain, err = metadata.Get("universe/universe_domain")
  93. if err != nil {
  94. if _, ok := err.(metadata.NotDefinedError); ok {
  95. // http.StatusNotFound (404)
  96. c.universeDomain = universeDomainDefault
  97. return nil
  98. } else {
  99. return err
  100. }
  101. }
  102. return nil
  103. }
  104. // DefaultCredentials is the old name of Credentials.
  105. //
  106. // Deprecated: use Credentials instead.
  107. type DefaultCredentials = Credentials
  108. // CredentialsParams holds user supplied parameters that are used together
  109. // with a credentials file for building a Credentials object.
  110. type CredentialsParams struct {
  111. // Scopes is the list OAuth scopes. Required.
  112. // Example: https://www.googleapis.com/auth/cloud-platform
  113. Scopes []string
  114. // Subject is the user email used for domain wide delegation (see
  115. // https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority).
  116. // Optional.
  117. Subject string
  118. // AuthHandler is the AuthorizationHandler used for 3-legged OAuth flow. Required for 3LO flow.
  119. AuthHandler authhandler.AuthorizationHandler
  120. // State is a unique string used with AuthHandler. Required for 3LO flow.
  121. State string
  122. // PKCE is used to support PKCE flow. Optional for 3LO flow.
  123. PKCE *authhandler.PKCEParams
  124. // The OAuth2 TokenURL default override. This value overrides the default TokenURL,
  125. // unless explicitly specified by the credentials config file. Optional.
  126. TokenURL string
  127. // EarlyTokenRefresh is the amount of time before a token expires that a new
  128. // token will be preemptively fetched. If unset the default value is 10
  129. // seconds.
  130. //
  131. // Note: This option is currently only respected when using credentials
  132. // fetched from the GCE metadata server.
  133. EarlyTokenRefresh time.Duration
  134. // UniverseDomain is the default service domain for a given Cloud universe.
  135. // Only supported in authentication flows that support universe domains.
  136. // This value takes precedence over a universe domain explicitly specified
  137. // in a credentials config file or by the GCE metadata server. Optional.
  138. UniverseDomain string
  139. }
  140. func (params CredentialsParams) deepCopy() CredentialsParams {
  141. paramsCopy := params
  142. paramsCopy.Scopes = make([]string, len(params.Scopes))
  143. copy(paramsCopy.Scopes, params.Scopes)
  144. return paramsCopy
  145. }
  146. // DefaultClient returns an HTTP Client that uses the
  147. // DefaultTokenSource to obtain authentication credentials.
  148. func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
  149. ts, err := DefaultTokenSource(ctx, scope...)
  150. if err != nil {
  151. return nil, err
  152. }
  153. return oauth2.NewClient(ctx, ts), nil
  154. }
  155. // DefaultTokenSource returns the token source for
  156. // "Application Default Credentials".
  157. // It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource.
  158. func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
  159. creds, err := FindDefaultCredentials(ctx, scope...)
  160. if err != nil {
  161. return nil, err
  162. }
  163. return creds.TokenSource, nil
  164. }
  165. // FindDefaultCredentialsWithParams searches for "Application Default Credentials".
  166. //
  167. // It looks for credentials in the following places,
  168. // preferring the first location found:
  169. //
  170. // 1. A JSON file whose path is specified by the
  171. // GOOGLE_APPLICATION_CREDENTIALS environment variable.
  172. // For workload identity federation, refer to
  173. // https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation on
  174. // how to generate the JSON configuration file for on-prem/non-Google cloud
  175. // platforms.
  176. // 2. A JSON file in a location known to the gcloud command-line tool.
  177. // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
  178. // On other systems, $HOME/.config/gcloud/application_default_credentials.json.
  179. // 3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses
  180. // the appengine.AccessToken function.
  181. // 4. On Google Compute Engine, Google App Engine standard second generation runtimes
  182. // (>= Go 1.11), and Google App Engine flexible environment, it fetches
  183. // credentials from the metadata server.
  184. func FindDefaultCredentialsWithParams(ctx context.Context, params CredentialsParams) (*Credentials, error) {
  185. // Make defensive copy of the slices in params.
  186. params = params.deepCopy()
  187. // First, try the environment variable.
  188. const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
  189. if filename := os.Getenv(envVar); filename != "" {
  190. creds, err := readCredentialsFile(ctx, filename, params)
  191. if err != nil {
  192. return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
  193. }
  194. return creds, nil
  195. }
  196. // Second, try a well-known file.
  197. filename := wellKnownFile()
  198. if b, err := os.ReadFile(filename); err == nil {
  199. return CredentialsFromJSONWithParams(ctx, b, params)
  200. }
  201. // Third, if we're on a Google App Engine standard first generation runtime (<= Go 1.9)
  202. // use those credentials. App Engine standard second generation runtimes (>= Go 1.11)
  203. // and App Engine flexible use ComputeTokenSource and the metadata server.
  204. if appengineTokenFunc != nil {
  205. return &Credentials{
  206. ProjectID: appengineAppIDFunc(ctx),
  207. TokenSource: AppEngineTokenSource(ctx, params.Scopes...),
  208. }, nil
  209. }
  210. // Fourth, if we're on Google Compute Engine, an App Engine standard second generation runtime,
  211. // or App Engine flexible, use the metadata server.
  212. if metadata.OnGCE() {
  213. id, _ := metadata.ProjectID()
  214. return &Credentials{
  215. ProjectID: id,
  216. TokenSource: computeTokenSource("", params.EarlyTokenRefresh, params.Scopes...),
  217. universeDomain: params.UniverseDomain,
  218. }, nil
  219. }
  220. // None are found; return helpful error.
  221. return nil, fmt.Errorf("google: could not find default credentials. See %v for more information", adcSetupURL)
  222. }
  223. // FindDefaultCredentials invokes FindDefaultCredentialsWithParams with the specified scopes.
  224. func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
  225. var params CredentialsParams
  226. params.Scopes = scopes
  227. return FindDefaultCredentialsWithParams(ctx, params)
  228. }
  229. // CredentialsFromJSONWithParams obtains Google credentials from a JSON value. The JSON can
  230. // represent either a Google Developers Console client_credentials.json file (as in ConfigFromJSON),
  231. // a Google Developers service account key file, a gcloud user credentials file (a.k.a. refresh
  232. // token JSON), or the JSON configuration file for workload identity federation in non-Google cloud
  233. // platforms (see https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation).
  234. func CredentialsFromJSONWithParams(ctx context.Context, jsonData []byte, params CredentialsParams) (*Credentials, error) {
  235. // Make defensive copy of the slices in params.
  236. params = params.deepCopy()
  237. // First, attempt to parse jsonData as a Google Developers Console client_credentials.json.
  238. config, _ := ConfigFromJSON(jsonData, params.Scopes...)
  239. if config != nil {
  240. return &Credentials{
  241. ProjectID: "",
  242. TokenSource: authhandler.TokenSourceWithPKCE(ctx, config, params.State, params.AuthHandler, params.PKCE),
  243. JSON: jsonData,
  244. }, nil
  245. }
  246. // Otherwise, parse jsonData as one of the other supported credentials files.
  247. var f credentialsFile
  248. if err := json.Unmarshal(jsonData, &f); err != nil {
  249. return nil, err
  250. }
  251. universeDomain := f.UniverseDomain
  252. if params.UniverseDomain != "" {
  253. universeDomain = params.UniverseDomain
  254. }
  255. // Authorized user credentials are only supported in the googleapis.com universe.
  256. if f.Type == userCredentialsKey {
  257. universeDomain = universeDomainDefault
  258. }
  259. ts, err := f.tokenSource(ctx, params)
  260. if err != nil {
  261. return nil, err
  262. }
  263. ts = newErrWrappingTokenSource(ts)
  264. return &Credentials{
  265. ProjectID: f.ProjectID,
  266. TokenSource: ts,
  267. JSON: jsonData,
  268. universeDomain: universeDomain,
  269. }, nil
  270. }
  271. // CredentialsFromJSON invokes CredentialsFromJSONWithParams with the specified scopes.
  272. func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
  273. var params CredentialsParams
  274. params.Scopes = scopes
  275. return CredentialsFromJSONWithParams(ctx, jsonData, params)
  276. }
  277. func wellKnownFile() string {
  278. const f = "application_default_credentials.json"
  279. if runtime.GOOS == "windows" {
  280. return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
  281. }
  282. return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
  283. }
  284. func readCredentialsFile(ctx context.Context, filename string, params CredentialsParams) (*Credentials, error) {
  285. b, err := os.ReadFile(filename)
  286. if err != nil {
  287. return nil, err
  288. }
  289. return CredentialsFromJSONWithParams(ctx, b, params)
  290. }