externalaccountuser.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package externalaccountuser
  15. import (
  16. "context"
  17. "errors"
  18. "net/http"
  19. "time"
  20. "cloud.google.com/go/auth"
  21. "cloud.google.com/go/auth/credentials/internal/stsexchange"
  22. "cloud.google.com/go/auth/internal"
  23. )
  24. // Options stores the configuration for fetching tokens with external authorized
  25. // user credentials.
  26. type Options struct {
  27. // Audience is the Secure Token Service (STS) audience which contains the
  28. // resource name for the workforce pool and the provider identifier in that
  29. // pool.
  30. Audience string
  31. // RefreshToken is the OAuth 2.0 refresh token.
  32. RefreshToken string
  33. // TokenURL is the STS token exchange endpoint for refresh.
  34. TokenURL string
  35. // TokenInfoURL is the STS endpoint URL for token introspection. Optional.
  36. TokenInfoURL string
  37. // ClientID is only required in conjunction with ClientSecret, as described
  38. // below.
  39. ClientID string
  40. // ClientSecret is currently only required if token_info endpoint also needs
  41. // to be called with the generated a cloud access token. When provided, STS
  42. // will be called with additional basic authentication using client_id as
  43. // username and client_secret as password.
  44. ClientSecret string
  45. // Scopes contains the desired scopes for the returned access token.
  46. Scopes []string
  47. // Client for token request.
  48. Client *http.Client
  49. }
  50. func (c *Options) validate() bool {
  51. return c.ClientID != "" && c.ClientSecret != "" && c.RefreshToken != "" && c.TokenURL != ""
  52. }
  53. // NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider]
  54. // configured with the provided options.
  55. func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
  56. if !opts.validate() {
  57. return nil, errors.New("credentials: invalid external_account_authorized_user configuration")
  58. }
  59. tp := &tokenProvider{
  60. o: opts,
  61. }
  62. return auth.NewCachedTokenProvider(tp, nil), nil
  63. }
  64. type tokenProvider struct {
  65. o *Options
  66. }
  67. func (tp *tokenProvider) Token(ctx context.Context) (*auth.Token, error) {
  68. opts := tp.o
  69. clientAuth := stsexchange.ClientAuthentication{
  70. AuthStyle: auth.StyleInHeader,
  71. ClientID: opts.ClientID,
  72. ClientSecret: opts.ClientSecret,
  73. }
  74. headers := make(http.Header)
  75. headers.Set("Content-Type", "application/x-www-form-urlencoded")
  76. stsResponse, err := stsexchange.RefreshAccessToken(ctx, &stsexchange.Options{
  77. Client: opts.Client,
  78. Endpoint: opts.TokenURL,
  79. RefreshToken: opts.RefreshToken,
  80. Authentication: clientAuth,
  81. Headers: headers,
  82. })
  83. if err != nil {
  84. return nil, err
  85. }
  86. if stsResponse.ExpiresIn < 0 {
  87. return nil, errors.New("credentials: invalid expiry from security token service")
  88. }
  89. // guarded by the wrapping with CachedTokenProvider
  90. if stsResponse.RefreshToken != "" {
  91. opts.RefreshToken = stsResponse.RefreshToken
  92. }
  93. return &auth.Token{
  94. Value: stsResponse.AccessToken,
  95. Expiry: time.Now().UTC().Add(time.Duration(stsResponse.ExpiresIn) * time.Second),
  96. Type: internal.TokenTypeBearer,
  97. }, nil
  98. }