option.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package jwk
  2. import (
  3. "crypto"
  4. "time"
  5. "github.com/lestrrat-go/backoff/v2"
  6. "github.com/lestrrat-go/jwx/internal/json"
  7. "github.com/lestrrat-go/option"
  8. )
  9. type Option = option.Interface
  10. type identHTTPClient struct{}
  11. type identThumbprintHash struct{}
  12. type identRefreshInterval struct{}
  13. type identMinRefreshInterval struct{}
  14. type identFetchBackoff struct{}
  15. type identPEM struct{}
  16. type identTypedField struct{}
  17. type identLocalRegistry struct{}
  18. type identFetchWhitelist struct{}
  19. type identIgnoreParseError struct{}
  20. // AutoRefreshOption is a type of Option that can be passed to the
  21. // AutoRefresh object.
  22. type AutoRefreshOption interface {
  23. Option
  24. autoRefreshOption()
  25. }
  26. type autoRefreshOption struct {
  27. Option
  28. }
  29. func (*autoRefreshOption) autoRefreshOption() {}
  30. // FetchOption is a type of Option that can be passed to `jwk.Fetch()`
  31. // FetchOption also implements the `AutoRefreshOption`, and thus can
  32. // safely be passed to `(*jwk.AutoRefresh).Configure()`
  33. type FetchOption interface {
  34. AutoRefreshOption
  35. fetchOption()
  36. }
  37. type fetchOption struct {
  38. Option
  39. }
  40. func (*fetchOption) autoRefreshOption() {}
  41. func (*fetchOption) fetchOption() {}
  42. // ParseOption is a type of Option that can be passed to `jwk.Parse()`
  43. // ParseOption also implmentsthe `ReadFileOPtion` and `AutoRefreshOption`,
  44. // and thus safely be passed to `jwk.ReadFile` and `(*jwk.AutoRefresh).Configure()`
  45. type ParseOption interface {
  46. ReadFileOption
  47. AutoRefreshOption
  48. parseOption()
  49. }
  50. type parseOption struct {
  51. Option
  52. }
  53. func (*parseOption) autoRefreshOption() {}
  54. func (*parseOption) parseOption() {}
  55. func (*parseOption) readFileOption() {}
  56. // WithHTTPClient allows users to specify the "net/http".Client object that
  57. // is used when fetching jwk.Set objects.
  58. func WithHTTPClient(cl HTTPClient) FetchOption {
  59. return &fetchOption{option.New(identHTTPClient{}, cl)}
  60. }
  61. // WithFetchBackoff specifies the backoff policy to use when
  62. // refreshing a JWKS from a remote server fails.
  63. //
  64. // This does not have any effect on initial `Fetch()`, or any of the `Refresh()` calls --
  65. // the backoff is applied ONLY on the background refreshing goroutine.
  66. func WithFetchBackoff(v backoff.Policy) FetchOption {
  67. return &fetchOption{option.New(identFetchBackoff{}, v)}
  68. }
  69. func WithThumbprintHash(h crypto.Hash) Option {
  70. return option.New(identThumbprintHash{}, h)
  71. }
  72. // WithRefreshInterval specifies the static interval between refreshes
  73. // of jwk.Set objects controlled by jwk.AutoRefresh.
  74. //
  75. // Providing this option overrides the adaptive token refreshing based
  76. // on Cache-Control/Expires header (and jwk.WithMinRefreshInterval),
  77. // and refreshes will *always* happen in this interval.
  78. func WithRefreshInterval(d time.Duration) AutoRefreshOption {
  79. return &autoRefreshOption{
  80. option.New(identRefreshInterval{}, d),
  81. }
  82. }
  83. // WithMinRefreshInterval specifies the minimum refresh interval to be used
  84. // when using AutoRefresh. This value is ONLY used if you did not specify
  85. // a user-supplied static refresh interval via `WithRefreshInterval`.
  86. //
  87. // This value is used as a fallback value when tokens are refreshed.
  88. //
  89. // When we fetch the key from a remote URL, we first look at the max-age
  90. // directive from Cache-Control response header. If this value is present,
  91. // we compare the max-age value and the value specified by this option
  92. // and take the larger one.
  93. //
  94. // Next we check for the Expires header, and similarly if the header is
  95. // present, we compare it against the value specified by this option,
  96. // and take the larger one.
  97. //
  98. // Finally, if neither of the above headers are present, we use the
  99. // value specified by this option as the next refresh timing
  100. //
  101. // If unspecified, the minimum refresh interval is 1 hour
  102. func WithMinRefreshInterval(d time.Duration) AutoRefreshOption {
  103. return &autoRefreshOption{
  104. option.New(identMinRefreshInterval{}, d),
  105. }
  106. }
  107. // WithPEM specifies that the input to `Parse()` is a PEM encoded key.
  108. func WithPEM(v bool) ParseOption {
  109. return &parseOption{
  110. option.New(identPEM{}, v),
  111. }
  112. }
  113. type typedFieldPair struct {
  114. Name string
  115. Value interface{}
  116. }
  117. // WithTypedField allows a private field to be parsed into the object type of
  118. // your choice. It works much like the RegisterCustomField, but the effect
  119. // is only applicable to the jwt.Parse function call which receives this option.
  120. //
  121. // While this can be extremely useful, this option should be used with caution:
  122. // There are many caveats that your entire team/user-base needs to be aware of,
  123. // and therefore in general its use is discouraged. Only use it when you know
  124. // what you are doing, and you document its use clearly for others.
  125. //
  126. // First and foremost, this is a "per-object" option. Meaning that given the same
  127. // serialized format, it is possible to generate two objects whose internal
  128. // representations may differ. That is, if you parse one _WITH_ the option,
  129. // and the other _WITHOUT_, their internal representation may completely differ.
  130. // This could potentially lead to problems.
  131. //
  132. // Second, specifying this option will slightly slow down the decoding process
  133. // as it needs to consult multiple definitions sources (global and local), so
  134. // be careful if you are decoding a large number of tokens, as the effects will stack up.
  135. func WithTypedField(name string, object interface{}) ParseOption {
  136. return &parseOption{
  137. option.New(identTypedField{},
  138. typedFieldPair{Name: name, Value: object},
  139. ),
  140. }
  141. }
  142. // This option is only available for internal code. Users don't get to play with it
  143. func withLocalRegistry(r *json.Registry) ParseOption {
  144. return &parseOption{option.New(identLocalRegistry{}, r)}
  145. }
  146. // WithFetchWhitelist specifies the Whitelist object to use when
  147. // fetching JWKs from a remote source. This option can be passed
  148. // to both `jwk.Fetch()`, `jwk.NewAutoRefresh()`, and `(*jwk.AutoRefresh).Configure()`
  149. func WithFetchWhitelist(w Whitelist) FetchOption {
  150. return &fetchOption{option.New(identFetchWhitelist{}, w)}
  151. }
  152. // WithIgnoreParseError is only applicable when used with `jwk.Parse()`
  153. // (i.e. to parse JWK sets). If passed to `jwk.ParseKey()`, the function
  154. // will return an error no matter what the input is.
  155. //
  156. // DO NOT USE WITHOUT EXHAUSTING ALL OTHER ROUTES FIRST.
  157. //
  158. // The option specifies that errors found during parsing of individual
  159. // keys are ignored. For example, if you had keys A, B, C where B is
  160. // invalid (e.g. it does not contain the required fields), then the
  161. // resulting JWKS will contain keys A and C only.
  162. //
  163. // This options exists as an escape hatch for those times when a
  164. // key in a JWKS that is irrelevant for your use case is causing
  165. // your JWKS parsing to fail, and you want to get to the rest of the
  166. // keys in the JWKS.
  167. //
  168. // Again, DO NOT USE unless you have exhausted all other routes.
  169. // When you use this option, you will not be able to tell if you are
  170. // using a faulty JWKS, except for when there are JSON syntax errors.
  171. func WithIgnoreParseError(b bool) ParseOption {
  172. return &parseOption{option.New(identIgnoreParseError{}, b)}
  173. }