oauth2adapt.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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 oauth2adapt helps converts types used in [cloud.google.com/go/auth]
  15. // and [golang.org/x/oauth2].
  16. package oauth2adapt
  17. import (
  18. "context"
  19. "encoding/json"
  20. "errors"
  21. "cloud.google.com/go/auth"
  22. "golang.org/x/oauth2"
  23. "golang.org/x/oauth2/google"
  24. )
  25. const (
  26. oauth2TokenSourceKey = "oauth2.google.tokenSource"
  27. oauth2ServiceAccountKey = "oauth2.google.serviceAccount"
  28. authTokenSourceKey = "auth.google.tokenSource"
  29. authServiceAccountKey = "auth.google.serviceAccount"
  30. )
  31. // TokenProviderFromTokenSource converts any [golang.org/x/oauth2.TokenSource]
  32. // into a [cloud.google.com/go/auth.TokenProvider].
  33. func TokenProviderFromTokenSource(ts oauth2.TokenSource) auth.TokenProvider {
  34. return &tokenProviderAdapter{ts: ts}
  35. }
  36. type tokenProviderAdapter struct {
  37. ts oauth2.TokenSource
  38. }
  39. // Token fulfills the [cloud.google.com/go/auth.TokenProvider] interface. It
  40. // is a light wrapper around the underlying TokenSource.
  41. func (tp *tokenProviderAdapter) Token(context.Context) (*auth.Token, error) {
  42. tok, err := tp.ts.Token()
  43. if err != nil {
  44. var err2 *oauth2.RetrieveError
  45. if ok := errors.As(err, &err2); ok {
  46. return nil, AuthErrorFromRetrieveError(err2)
  47. }
  48. return nil, err
  49. }
  50. // Preserve compute token metadata, for both types of tokens.
  51. metadata := map[string]interface{}{}
  52. if val, ok := tok.Extra(oauth2TokenSourceKey).(string); ok {
  53. metadata[authTokenSourceKey] = val
  54. metadata[oauth2TokenSourceKey] = val
  55. }
  56. if val, ok := tok.Extra(oauth2ServiceAccountKey).(string); ok {
  57. metadata[authServiceAccountKey] = val
  58. metadata[oauth2ServiceAccountKey] = val
  59. }
  60. return &auth.Token{
  61. Value: tok.AccessToken,
  62. Type: tok.Type(),
  63. Expiry: tok.Expiry,
  64. Metadata: metadata,
  65. }, nil
  66. }
  67. // TokenSourceFromTokenProvider converts any
  68. // [cloud.google.com/go/auth.TokenProvider] into a
  69. // [golang.org/x/oauth2.TokenSource].
  70. func TokenSourceFromTokenProvider(tp auth.TokenProvider) oauth2.TokenSource {
  71. return &tokenSourceAdapter{tp: tp}
  72. }
  73. type tokenSourceAdapter struct {
  74. tp auth.TokenProvider
  75. }
  76. // Token fulfills the [golang.org/x/oauth2.TokenSource] interface. It
  77. // is a light wrapper around the underlying TokenProvider.
  78. func (ts *tokenSourceAdapter) Token() (*oauth2.Token, error) {
  79. tok, err := ts.tp.Token(context.Background())
  80. if err != nil {
  81. var err2 *auth.Error
  82. if ok := errors.As(err, &err2); ok {
  83. return nil, AddRetrieveErrorToAuthError(err2)
  84. }
  85. return nil, err
  86. }
  87. tok2 := &oauth2.Token{
  88. AccessToken: tok.Value,
  89. TokenType: tok.Type,
  90. Expiry: tok.Expiry,
  91. }
  92. // Preserve token metadata.
  93. metadata := tok.Metadata
  94. if metadata != nil {
  95. // Append compute token metadata in converted form.
  96. if val, ok := metadata[authTokenSourceKey].(string); ok && val != "" {
  97. metadata[oauth2TokenSourceKey] = val
  98. }
  99. if val, ok := metadata[authServiceAccountKey].(string); ok && val != "" {
  100. metadata[oauth2ServiceAccountKey] = val
  101. }
  102. tok2 = tok2.WithExtra(metadata)
  103. }
  104. return tok2, nil
  105. }
  106. // AuthCredentialsFromOauth2Credentials converts a [golang.org/x/oauth2/google.Credentials]
  107. // to a [cloud.google.com/go/auth.Credentials].
  108. func AuthCredentialsFromOauth2Credentials(creds *google.Credentials) *auth.Credentials {
  109. if creds == nil {
  110. return nil
  111. }
  112. return auth.NewCredentials(&auth.CredentialsOptions{
  113. TokenProvider: TokenProviderFromTokenSource(creds.TokenSource),
  114. JSON: creds.JSON,
  115. ProjectIDProvider: auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
  116. return creds.ProjectID, nil
  117. }),
  118. UniverseDomainProvider: auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
  119. return creds.GetUniverseDomain()
  120. }),
  121. })
  122. }
  123. // Oauth2CredentialsFromAuthCredentials converts a [cloud.google.com/go/auth.Credentials]
  124. // to a [golang.org/x/oauth2/google.Credentials].
  125. func Oauth2CredentialsFromAuthCredentials(creds *auth.Credentials) *google.Credentials {
  126. if creds == nil {
  127. return nil
  128. }
  129. // Throw away errors as old credentials are not request aware. Also, no
  130. // network requests are currently happening for this use case.
  131. projectID, _ := creds.ProjectID(context.Background())
  132. return &google.Credentials{
  133. TokenSource: TokenSourceFromTokenProvider(creds.TokenProvider),
  134. ProjectID: projectID,
  135. JSON: creds.JSON(),
  136. UniverseDomainProvider: func() (string, error) {
  137. return creds.UniverseDomain(context.Background())
  138. },
  139. }
  140. }
  141. type oauth2Error struct {
  142. ErrorCode string `json:"error"`
  143. ErrorDescription string `json:"error_description"`
  144. ErrorURI string `json:"error_uri"`
  145. }
  146. // AddRetrieveErrorToAuthError returns the same error provided and adds a
  147. // [golang.org/x/oauth2.RetrieveError] to the error chain by setting the `Err` field on the
  148. // [cloud.google.com/go/auth.Error].
  149. func AddRetrieveErrorToAuthError(err *auth.Error) *auth.Error {
  150. if err == nil {
  151. return nil
  152. }
  153. e := &oauth2.RetrieveError{
  154. Response: err.Response,
  155. Body: err.Body,
  156. }
  157. err.Err = e
  158. if len(err.Body) > 0 {
  159. var oErr oauth2Error
  160. // ignore the error as it only fills in extra details
  161. json.Unmarshal(err.Body, &oErr)
  162. e.ErrorCode = oErr.ErrorCode
  163. e.ErrorDescription = oErr.ErrorDescription
  164. e.ErrorURI = oErr.ErrorURI
  165. }
  166. return err
  167. }
  168. // AuthErrorFromRetrieveError returns an [cloud.google.com/go/auth.Error] that
  169. // wraps the provided [golang.org/x/oauth2.RetrieveError].
  170. func AuthErrorFromRetrieveError(err *oauth2.RetrieveError) *auth.Error {
  171. if err == nil {
  172. return nil
  173. }
  174. return &auth.Error{
  175. Response: err.Response,
  176. Body: err.Body,
  177. Err: err,
  178. }
  179. }