httptransport.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 httptransport provides functionality for managing HTTP client
  15. // connections to Google Cloud services.
  16. package httptransport
  17. import (
  18. "crypto/tls"
  19. "errors"
  20. "fmt"
  21. "net/http"
  22. "cloud.google.com/go/auth"
  23. detect "cloud.google.com/go/auth/credentials"
  24. "cloud.google.com/go/auth/internal"
  25. "cloud.google.com/go/auth/internal/transport"
  26. )
  27. // ClientCertProvider is a function that returns a TLS client certificate to be
  28. // used when opening TLS connections. It follows the same semantics as
  29. // [crypto/tls.Config.GetClientCertificate].
  30. type ClientCertProvider = func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
  31. // Options used to configure a [net/http.Client] from [NewClient].
  32. type Options struct {
  33. // DisableTelemetry disables default telemetry (OpenTelemetry). An example
  34. // reason to do so would be to bind custom telemetry that overrides the
  35. // defaults.
  36. DisableTelemetry bool
  37. // DisableAuthentication specifies that no authentication should be used. It
  38. // is suitable only for testing and for accessing public resources, like
  39. // public Google Cloud Storage buckets.
  40. DisableAuthentication bool
  41. // Headers are extra HTTP headers that will be appended to every outgoing
  42. // request.
  43. Headers http.Header
  44. // BaseRoundTripper overrides the base transport used for serving requests.
  45. // If specified ClientCertProvider is ignored.
  46. BaseRoundTripper http.RoundTripper
  47. // Endpoint overrides the default endpoint to be used for a service.
  48. Endpoint string
  49. // APIKey specifies an API key to be used as the basis for authentication.
  50. // If set DetectOpts are ignored.
  51. APIKey string
  52. // Credentials used to add Authorization header to all requests. If set
  53. // DetectOpts are ignored.
  54. Credentials *auth.Credentials
  55. // ClientCertProvider is a function that returns a TLS client certificate to
  56. // be used when opening TLS connections. It follows the same semantics as
  57. // crypto/tls.Config.GetClientCertificate.
  58. ClientCertProvider ClientCertProvider
  59. // DetectOpts configures settings for detect Application Default
  60. // Credentials.
  61. DetectOpts *detect.DetectOptions
  62. // UniverseDomain is the default service domain for a given Cloud universe.
  63. // The default value is "googleapis.com". This is the universe domain
  64. // configured for the client, which will be compared to the universe domain
  65. // that is separately configured for the credentials.
  66. UniverseDomain string
  67. // InternalOptions are NOT meant to be set directly by consumers of this
  68. // package, they should only be set by generated client code.
  69. InternalOptions *InternalOptions
  70. }
  71. func (o *Options) validate() error {
  72. if o == nil {
  73. return errors.New("httptransport: opts required to be non-nil")
  74. }
  75. if o.InternalOptions != nil && o.InternalOptions.SkipValidation {
  76. return nil
  77. }
  78. hasCreds := o.APIKey != "" ||
  79. o.Credentials != nil ||
  80. (o.DetectOpts != nil && len(o.DetectOpts.CredentialsJSON) > 0) ||
  81. (o.DetectOpts != nil && o.DetectOpts.CredentialsFile != "")
  82. if o.DisableAuthentication && hasCreds {
  83. return errors.New("httptransport: DisableAuthentication is incompatible with options that set or detect credentials")
  84. }
  85. return nil
  86. }
  87. // client returns the client a user set for the detect options or nil if one was
  88. // not set.
  89. func (o *Options) client() *http.Client {
  90. if o.DetectOpts != nil && o.DetectOpts.Client != nil {
  91. return o.DetectOpts.Client
  92. }
  93. return nil
  94. }
  95. func (o *Options) resolveDetectOptions() *detect.DetectOptions {
  96. io := o.InternalOptions
  97. // soft-clone these so we are not updating a ref the user holds and may reuse
  98. do := transport.CloneDetectOptions(o.DetectOpts)
  99. // If scoped JWTs are enabled user provided an aud, allow self-signed JWT.
  100. if (io != nil && io.EnableJWTWithScope) || do.Audience != "" {
  101. do.UseSelfSignedJWT = true
  102. }
  103. // Only default scopes if user did not also set an audience.
  104. if len(do.Scopes) == 0 && do.Audience == "" && io != nil && len(io.DefaultScopes) > 0 {
  105. do.Scopes = make([]string, len(io.DefaultScopes))
  106. copy(do.Scopes, io.DefaultScopes)
  107. }
  108. if len(do.Scopes) == 0 && do.Audience == "" && io != nil {
  109. do.Audience = o.InternalOptions.DefaultAudience
  110. }
  111. if o.ClientCertProvider != nil {
  112. tlsConfig := &tls.Config{
  113. GetClientCertificate: o.ClientCertProvider,
  114. }
  115. do.Client = transport.DefaultHTTPClientWithTLS(tlsConfig)
  116. do.TokenURL = detect.GoogleMTLSTokenURL
  117. }
  118. return do
  119. }
  120. // InternalOptions are only meant to be set by generated client code. These are
  121. // not meant to be set directly by consumers of this package. Configuration in
  122. // this type is considered EXPERIMENTAL and may be removed at any time in the
  123. // future without warning.
  124. type InternalOptions struct {
  125. // EnableJWTWithScope specifies if scope can be used with self-signed JWT.
  126. EnableJWTWithScope bool
  127. // DefaultAudience specifies a default audience to be used as the audience
  128. // field ("aud") for the JWT token authentication.
  129. DefaultAudience string
  130. // DefaultEndpointTemplate combined with UniverseDomain specifies the
  131. // default endpoint.
  132. DefaultEndpointTemplate string
  133. // DefaultMTLSEndpoint specifies the default mTLS endpoint.
  134. DefaultMTLSEndpoint string
  135. // DefaultScopes specifies the default OAuth2 scopes to be used for a
  136. // service.
  137. DefaultScopes []string
  138. // SkipValidation bypasses validation on Options. It should only be used
  139. // internally for clients that need more control over their transport.
  140. SkipValidation bool
  141. // SkipUniverseDomainValidation skips the verification that the universe
  142. // domain configured for the client matches the universe domain configured
  143. // for the credentials. It should only be used internally for clients that
  144. // need more control over their transport. The default is false.
  145. SkipUniverseDomainValidation bool
  146. }
  147. // AddAuthorizationMiddleware adds a middleware to the provided client's
  148. // transport that sets the Authorization header with the value produced by the
  149. // provided [cloud.google.com/go/auth.Credentials]. An error is returned only
  150. // if client or creds is nil.
  151. //
  152. // This function does not support setting a universe domain value on the client.
  153. func AddAuthorizationMiddleware(client *http.Client, creds *auth.Credentials) error {
  154. if client == nil || creds == nil {
  155. return fmt.Errorf("httptransport: client and tp must not be nil")
  156. }
  157. base := client.Transport
  158. if base == nil {
  159. if dt, ok := http.DefaultTransport.(*http.Transport); ok {
  160. base = dt.Clone()
  161. } else {
  162. // Directly reuse the DefaultTransport if the application has
  163. // replaced it with an implementation of RoundTripper other than
  164. // http.Transport.
  165. base = http.DefaultTransport
  166. }
  167. }
  168. client.Transport = &authTransport{
  169. creds: creds,
  170. base: base,
  171. }
  172. return nil
  173. }
  174. // NewClient returns a [net/http.Client] that can be used to communicate with a
  175. // Google cloud service, configured with the provided [Options]. It
  176. // automatically appends Authorization headers to all outgoing requests.
  177. func NewClient(opts *Options) (*http.Client, error) {
  178. if err := opts.validate(); err != nil {
  179. return nil, err
  180. }
  181. tOpts := &transport.Options{
  182. Endpoint: opts.Endpoint,
  183. ClientCertProvider: opts.ClientCertProvider,
  184. Client: opts.client(),
  185. UniverseDomain: opts.UniverseDomain,
  186. }
  187. if io := opts.InternalOptions; io != nil {
  188. tOpts.DefaultEndpointTemplate = io.DefaultEndpointTemplate
  189. tOpts.DefaultMTLSEndpoint = io.DefaultMTLSEndpoint
  190. }
  191. clientCertProvider, dialTLSContext, err := transport.GetHTTPTransportConfig(tOpts)
  192. if err != nil {
  193. return nil, err
  194. }
  195. baseRoundTripper := opts.BaseRoundTripper
  196. if baseRoundTripper == nil {
  197. baseRoundTripper = defaultBaseTransport(clientCertProvider, dialTLSContext)
  198. }
  199. // Ensure the token exchange transport uses the same ClientCertProvider as the API transport.
  200. opts.ClientCertProvider = clientCertProvider
  201. trans, err := newTransport(baseRoundTripper, opts)
  202. if err != nil {
  203. return nil, err
  204. }
  205. return &http.Client{
  206. Transport: trans,
  207. }, nil
  208. }
  209. // SetAuthHeader uses the provided token to set the Authorization header on a
  210. // request. If the token.Type is empty, the type is assumed to be Bearer.
  211. func SetAuthHeader(token *auth.Token, req *http.Request) {
  212. typ := token.Type
  213. if typ == "" {
  214. typ = internal.TokenTypeBearer
  215. }
  216. req.Header.Set("Authorization", typ+" "+token.Value)
  217. }