...

Source file src/github.com/lestrrat-go/jwx/jwt/options.go

Documentation: github.com/lestrrat-go/jwx/jwt

     1  package jwt
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/lestrrat-go/backoff/v2"
     9  	"github.com/lestrrat-go/jwx/jwa"
    10  	"github.com/lestrrat-go/jwx/jwe"
    11  	"github.com/lestrrat-go/jwx/jwk"
    12  	"github.com/lestrrat-go/jwx/jws"
    13  	"github.com/lestrrat-go/option"
    14  )
    15  
    16  type Option = option.Interface
    17  
    18  // GlobalOption describes an Option that can be passed to `Settings()`.
    19  type GlobalOption interface {
    20  	Option
    21  	globalOption()
    22  }
    23  
    24  type globalOption struct {
    25  	Option
    26  }
    27  
    28  func (*globalOption) globalOption() {}
    29  
    30  // ParseRequestOption describes an Option that can be passed to `ParseRequest()`.
    31  type ParseRequestOption interface {
    32  	ParseOption
    33  	httpParseOption()
    34  }
    35  
    36  type httpParseOption struct {
    37  	ParseOption
    38  }
    39  
    40  func (*httpParseOption) httpParseOption() {}
    41  
    42  // ParseOption describes an Option that can be passed to `Parse()`.
    43  // ParseOption also implements ReadFileOption, therefore it may be
    44  // safely pass them to `jwt.ReadFile()`
    45  type ParseOption interface {
    46  	ReadFileOption
    47  	parseOption()
    48  }
    49  
    50  type parseOption struct {
    51  	Option
    52  }
    53  
    54  func newParseOption(n interface{}, v interface{}) ParseOption {
    55  	return &parseOption{option.New(n, v)}
    56  }
    57  
    58  func (*parseOption) parseOption()    {}
    59  func (*parseOption) readFileOption() {}
    60  
    61  // SignOption describes an Option that can be passed to Sign() or
    62  // (jwt.Serializer).Sign
    63  type SignOption interface {
    64  	Option
    65  	signOption()
    66  }
    67  
    68  type signOption struct {
    69  	Option
    70  }
    71  
    72  func newSignOption(n interface{}, v interface{}) SignOption {
    73  	return &signOption{option.New(n, v)}
    74  }
    75  
    76  func (*signOption) signOption() {}
    77  
    78  // EncryptOption describes an Option that can be passed to Encrypt() or
    79  // (jwt.Serializer).Encrypt
    80  type EncryptOption interface {
    81  	Option
    82  	encryptOption()
    83  }
    84  
    85  type encryptOption struct {
    86  	Option
    87  }
    88  
    89  func newEncryptOption(n interface{}, v interface{}) EncryptOption {
    90  	return &encryptOption{option.New(n, v)}
    91  }
    92  
    93  func (*encryptOption) encryptOption() {}
    94  
    95  // ValidateOption describes an Option that can be passed to Validate().
    96  // ValidateOption also implements ParseOption, therefore it may be
    97  // safely passed to `Parse()` (and thus `jwt.ReadFile()`)
    98  type ValidateOption interface {
    99  	ParseOption
   100  	validateOption()
   101  }
   102  
   103  type validateOption struct {
   104  	ParseOption
   105  }
   106  
   107  func newValidateOption(n interface{}, v interface{}) ValidateOption {
   108  	return &validateOption{newParseOption(n, v)}
   109  }
   110  
   111  func (*validateOption) validateOption() {}
   112  
   113  type identAcceptableSkew struct{}
   114  type identClock struct{}
   115  type identContext struct{}
   116  type identDecrypt struct{}
   117  type identDefault struct{}
   118  type identFlattenAudience struct{}
   119  type identInferAlgorithmFromKey struct{}
   120  type identJweHeaders struct{}
   121  type identJwsHeaders struct{}
   122  type identKeySet struct{}
   123  type identKeySetProvider struct{}
   124  type identPedantic struct{}
   125  type identValidator struct{}
   126  type identToken struct{}
   127  type identTypedClaim struct{}
   128  type identValidate struct{}
   129  type identVerify struct{}
   130  type identVerifyAuto struct{}
   131  type identFetchBackoff struct{}
   132  type identFetchWhitelist struct{}
   133  type identHTTPClient struct{}
   134  type identJWKSetFetcher struct{}
   135  
   136  type identHeaderKey struct{}
   137  type identFormKey struct{}
   138  
   139  type VerifyParameters interface {
   140  	Algorithm() jwa.SignatureAlgorithm
   141  	Key() interface{}
   142  }
   143  
   144  type verifyParams struct {
   145  	alg jwa.SignatureAlgorithm
   146  	key interface{}
   147  }
   148  
   149  func (p *verifyParams) Algorithm() jwa.SignatureAlgorithm {
   150  	return p.alg
   151  }
   152  
   153  func (p *verifyParams) Key() interface{} {
   154  	return p.key
   155  }
   156  
   157  // WithVerify forces the Parse method to verify the JWT message
   158  // using the given key. XXX Should have been named something like
   159  // WithVerificationKey
   160  func WithVerify(alg jwa.SignatureAlgorithm, key interface{}) ParseOption {
   161  	return newParseOption(identVerify{}, &verifyParams{
   162  		alg: alg,
   163  		key: key,
   164  	})
   165  }
   166  
   167  // WithKeySet forces the Parse method to verify the JWT message
   168  // using one of the keys in the given key set.
   169  //
   170  // The key and the JWT MUST have a proper `kid` field set.
   171  // The key to use for signature verification is chosen by matching
   172  // the Key ID of the JWT and the ID of the given key set.
   173  //
   174  // When using this option, keys MUST have a proper 'alg' field
   175  // set. This is because we need to know the exact algorithm that
   176  // you (the user) wants to use to verify the token. We do NOT
   177  // trust the token's headers, because they can easily be tampered with.
   178  //
   179  // However, there _is_ a workaround if you do understand the risks
   180  // of allowing a library to automatically choose a signature verification strategy,
   181  // and you do not mind the verification process having to possibly
   182  // attempt using multiple times before succeeding to verify. See
   183  // `jwt.InferAlgorithmFromKey` option
   184  //
   185  // If you have only one key in the set, and are sure you want to
   186  // use that key, you can use the `jwt.WithDefaultKey` option.
   187  //
   188  // If provided with WithKeySetProvider(), this option takes precedence.
   189  func WithKeySet(set jwk.Set) ParseOption {
   190  	return newParseOption(identKeySet{}, set)
   191  }
   192  
   193  // UseDefaultKey is used in conjunction with the option WithKeySet
   194  // to instruct the Parse method to default to the single key in a key
   195  // set when no Key ID is included in the JWT. If the key set contains
   196  // multiple keys then the default behavior is unchanged -- that is,
   197  // the since we can't determine the key to use, it returns an error.
   198  func UseDefaultKey(value bool) ParseOption {
   199  	return newParseOption(identDefault{}, value)
   200  }
   201  
   202  // WithToken specifies the token instance that is used when parsing
   203  // JWT tokens.
   204  func WithToken(t Token) ParseOption {
   205  	return newParseOption(identToken{}, t)
   206  }
   207  
   208  // WithHeaders is passed to `jwt.Sign()` function, to allow specifying arbitrary
   209  // header values to be included in the header section of the jws message
   210  //
   211  // This option will be deprecated in the next major version. Use
   212  // jwt.WithJwsHeaders() instead.
   213  func WithHeaders(hdrs jws.Headers) SignOption {
   214  	return WithJwsHeaders(hdrs)
   215  }
   216  
   217  // WithJwsHeaders is passed to `jwt.Sign()` function or
   218  // "jwt.Serializer".Sign() method, to allow specifying arbitrary
   219  // header values to be included in the header section of the JWE message
   220  func WithJwsHeaders(hdrs jws.Headers) SignOption {
   221  	return newSignOption(identJwsHeaders{}, hdrs)
   222  }
   223  
   224  // WithJweHeaders is passed to "jwt.Serializer".Encrypt() method to allow
   225  // specifying arbitrary header values to be included in the protected header
   226  // of the JWE message
   227  func WithJweHeaders(hdrs jwe.Headers) EncryptOption {
   228  	return newEncryptOption(identJweHeaders{}, hdrs)
   229  }
   230  
   231  // WithValidate is passed to `Parse()` method to denote that the
   232  // validation of the JWT token should be performed after a successful
   233  // parsing of the incoming payload.
   234  func WithValidate(b bool) ParseOption {
   235  	return newParseOption(identValidate{}, b)
   236  }
   237  
   238  // WithClock specifies the `Clock` to be used when verifying
   239  // claims exp and nbf.
   240  func WithClock(c Clock) ValidateOption {
   241  	return newValidateOption(identClock{}, c)
   242  }
   243  
   244  // WithAcceptableSkew specifies the duration in which exp and nbf
   245  // claims may differ by. This value should be positive
   246  func WithAcceptableSkew(dur time.Duration) ValidateOption {
   247  	return newValidateOption(identAcceptableSkew{}, dur)
   248  }
   249  
   250  // WithIssuer specifies that expected issuer value. If not specified,
   251  // the value of issuer is not verified at all.
   252  func WithIssuer(s string) ValidateOption {
   253  	return WithValidator(ClaimValueIs(IssuerKey, s))
   254  }
   255  
   256  // WithSubject specifies that expected subject value. If not specified,
   257  // the value of subject is not verified at all.
   258  func WithSubject(s string) ValidateOption {
   259  	return WithValidator(ClaimValueIs(SubjectKey, s))
   260  }
   261  
   262  // WithJwtID specifies that expected jti value. If not specified,
   263  // the value of jti is not verified at all.
   264  func WithJwtID(s string) ValidateOption {
   265  	return WithValidator(ClaimValueIs(JwtIDKey, s))
   266  }
   267  
   268  // WithAudience specifies that expected audience value.
   269  // `Validate()` will return true if one of the values in the `aud` element
   270  // matches this value.  If not specified, the value of issuer is not
   271  // verified at all.
   272  func WithAudience(s string) ValidateOption {
   273  	return WithValidator(ClaimContainsString(AudienceKey, s))
   274  }
   275  
   276  // WithClaimValue specifies the expected value for a given claim
   277  func WithClaimValue(name string, v interface{}) ValidateOption {
   278  	return WithValidator(ClaimValueIs(name, v))
   279  }
   280  
   281  // WithHeaderKey is used to specify header keys to search for tokens.
   282  //
   283  // While the type system allows this option to be passed to jwt.Parse() directly,
   284  // doing so will have no effect. Only use it for HTTP request parsing functions
   285  func WithHeaderKey(v string) ParseRequestOption {
   286  	return &httpParseOption{newParseOption(identHeaderKey{}, v)}
   287  }
   288  
   289  // WithFormKey is used to specify header keys to search for tokens.
   290  //
   291  // While the type system allows this option to be passed to jwt.Parse() directly,
   292  // doing so will have no effect. Only use it for HTTP request parsing functions
   293  func WithFormKey(v string) ParseRequestOption {
   294  	return &httpParseOption{newParseOption(identFormKey{}, v)}
   295  }
   296  
   297  // WithFlattenAudience specifies if the "aud" claim should be flattened
   298  // to a single string upon the token being serialized to JSON.
   299  //
   300  // This is sometimes important when a JWT consumer does not understand that
   301  // the "aud" claim can actually take the form of an array of strings.
   302  //
   303  // The default value is `false`, which means that "aud" claims are always
   304  // rendered as a arrays of strings. This setting has a global effect,
   305  // and will change the behavior for all JWT serialization.
   306  func WithFlattenAudience(v bool) GlobalOption {
   307  	return &globalOption{option.New(identFlattenAudience{}, v)}
   308  }
   309  
   310  type claimPair struct {
   311  	Name  string
   312  	Value interface{}
   313  }
   314  
   315  // WithTypedClaim allows a private claim to be parsed into the object type of
   316  // your choice. It works much like the RegisterCustomField, but the effect
   317  // is only applicable to the jwt.Parse function call which receives this option.
   318  //
   319  // While this can be extremely useful, this option should be used with caution:
   320  // There are many caveats that your entire team/user-base needs to be aware of,
   321  // and therefore in general its use is discouraged. Only use it when you know
   322  // what you are doing, and you document its use clearly for others.
   323  //
   324  // First and foremost, this is a "per-object" option. Meaning that given the same
   325  // serialized format, it is possible to generate two objects whose internal
   326  // representations may differ. That is, if you parse one _WITH_ the option,
   327  // and the other _WITHOUT_, their internal representation may completely differ.
   328  // This could potentially lead to problems.
   329  //
   330  // Second, specifying this option will slightly slow down the decoding process
   331  // as it needs to consult multiple definitions sources (global and local), so
   332  // be careful if you are decoding a large number of tokens, as the effects will stack up.
   333  //
   334  // Finally, this option will also NOT work unless the tokens themselves support such
   335  // parsing mechanism. For example, while tokens obtained from `jwt.New()` and
   336  // `openid.New()` will respect this option, if you provide your own custom
   337  // token type, it will need to implement the TokenWithDecodeCtx interface.
   338  func WithTypedClaim(name string, object interface{}) ParseOption {
   339  	return newParseOption(identTypedClaim{}, claimPair{Name: name, Value: object})
   340  }
   341  
   342  // WithRequiredClaim specifies that the claim identified the given name
   343  // must exist in the token. Only the existence of the claim is checked:
   344  // the actual value associated with that field is not checked.
   345  func WithRequiredClaim(name string) ValidateOption {
   346  	return WithValidator(IsRequired(name))
   347  }
   348  
   349  // WithMaxDelta specifies that given two claims `c1` and `c2` that represent time, the difference in
   350  // time.Duration must be less than equal to the value specified by `d`. If `c1` or `c2` is the
   351  // empty string, the current time (as computed by `time.Now` or the object passed via
   352  // `WithClock()`) is used for the comparison.
   353  //
   354  // `c1` and `c2` are also assumed to be required, therefore not providing either claim in the
   355  // token will result in an error.
   356  //
   357  // Because there is no way of reliably knowing how to parse private claims, we currently only
   358  // support `iat`, `exp`, and `nbf` claims.
   359  //
   360  // If the empty string is passed to c1 or c2, then the current time (as calculated by time.Now() or
   361  // the clock object provided via WithClock()) is used.
   362  //
   363  // For example, in order to specify that `exp` - `iat` should be less than 10*time.Second, you would write
   364  //
   365  //    jwt.Validate(token, jwt.WithMaxDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey))
   366  //
   367  // If AcceptableSkew of 2 second is specified, the above will return valid for any value of
   368  // `exp` - `iat`  between 8 (10-2) and 12 (10+2).
   369  func WithMaxDelta(dur time.Duration, c1, c2 string) ValidateOption {
   370  	return WithValidator(MaxDeltaIs(c1, c2, dur))
   371  }
   372  
   373  // WithMinDelta is almost exactly the same as WithMaxDelta, but force validation to fail if
   374  // the difference between time claims are less than dur.
   375  //
   376  // For example, in order to specify that `exp` - `iat` should be greater than 10*time.Second, you would write
   377  //
   378  //    jwt.Validate(token, jwt.WithMinDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey))
   379  //
   380  // The validation would fail if the difference is less than 10 seconds.
   381  //
   382  func WithMinDelta(dur time.Duration, c1, c2 string) ValidateOption {
   383  	return WithValidator(MinDeltaIs(c1, c2, dur))
   384  }
   385  
   386  // WithValidator validates the token with the given Validator.
   387  //
   388  // For example, in order to validate tokens that are only valid during August, you would write
   389  //
   390  //    validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) error {
   391  //      if time.Now().Month() != 8 {
   392  //        return fmt.Errorf(`tokens are only valid during August!`)
   393  //      }
   394  //      return nil
   395  //    })
   396  //   err := jwt.Validate(token, jwt.WithValidator(validator))
   397  //
   398  func WithValidator(v Validator) ValidateOption {
   399  	return newValidateOption(identValidator{}, v)
   400  }
   401  
   402  type decryptParams struct {
   403  	alg jwa.KeyEncryptionAlgorithm
   404  	key interface{}
   405  }
   406  
   407  type DecryptParameters interface {
   408  	Algorithm() jwa.KeyEncryptionAlgorithm
   409  	Key() interface{}
   410  }
   411  
   412  func (dp *decryptParams) Algorithm() jwa.KeyEncryptionAlgorithm {
   413  	return dp.alg
   414  }
   415  
   416  func (dp *decryptParams) Key() interface{} {
   417  	return dp.key
   418  }
   419  
   420  // WithDecrypt allows users to specify parameters for decryption using
   421  // `jwe.Decrypt`. You must specify this if your JWT is encrypted.
   422  func WithDecrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ParseOption {
   423  	return newParseOption(identDecrypt{}, &decryptParams{
   424  		alg: alg,
   425  		key: key,
   426  	})
   427  }
   428  
   429  // WithPedantic enables pedantic mode for parsing JWTs. Currently this only
   430  // applies to checking for the correct `typ` and/or `cty` when necessary.
   431  func WithPedantic(v bool) ParseOption {
   432  	return newParseOption(identPedantic{}, v)
   433  }
   434  
   435  // InferAlgorithmFromKey allows jwt.Parse to guess the signature algorithm
   436  // passed to `jws.Verify()`, in case the key you provided does not have a proper `alg` header.
   437  //
   438  // Compared to providing explicit `alg` from the key this is slower, and in
   439  // case our heuristics are wrong or outdated, may fail to verify the token.
   440  // Also, automatic detection of signature verification methods are always
   441  // more vulnerable for potential attack vectors.
   442  //
   443  // It is highly recommended that you fix your key to contain a proper `alg`
   444  // header field instead of resorting to using this option, but sometimes
   445  // it just needs to happen.
   446  //
   447  // Your JWT still need to have an `alg` field, and it must match one of the
   448  // candidates that we produce for your key
   449  func InferAlgorithmFromKey(v bool) ParseOption {
   450  	return newParseOption(identInferAlgorithmFromKey{}, v)
   451  }
   452  
   453  // KeySetProvider is an interface for objects that can choose the appropriate
   454  // jwk.Set to be used when verifying JWTs
   455  type KeySetProvider interface {
   456  	// KeySetFrom returns the jwk.Set to be used to verify the token.
   457  	// Keep in mind that the token at the point when the method is called is NOT VERIFIED.
   458  	// DO NOT trust the contents of the Token too much. For example, do not take the
   459  	// hint as to which signature algorithm to use from the token itself.
   460  	KeySetFrom(Token) (jwk.Set, error)
   461  }
   462  
   463  // KeySetProviderFunc is an implementation of KeySetProvider that is based
   464  // on a function.
   465  type KeySetProviderFunc func(Token) (jwk.Set, error)
   466  
   467  func (fn KeySetProviderFunc) KeySetFrom(t Token) (jwk.Set, error) {
   468  	return fn(t)
   469  }
   470  
   471  // WithKeySetProvider allows users to specify an object to choose which
   472  // jwk.Set to use for verification.
   473  //
   474  // If provided with WithKeySet(), WithKeySet() option takes precedence.
   475  func WithKeySetProvider(p KeySetProvider) ParseOption {
   476  	return newParseOption(identKeySetProvider{}, p)
   477  }
   478  
   479  // WithContext allows you to specify a context.Context object to be used
   480  // with `jwt.Validate()` option.
   481  //
   482  // Please be aware that in the next major release of this library,
   483  // `jwt.Validate()`'s signature will change to include an explicit
   484  // `context.Context` object.
   485  func WithContext(ctx context.Context) ValidateOption {
   486  	return newValidateOption(identContext{}, ctx)
   487  }
   488  
   489  // WithVerifyAuto specifies that the JWS verification should be performed
   490  // using `jws.VerifyAuto()`, which in turn attempts to verify the message
   491  // using values that are stored within the JWS message.
   492  //
   493  // Only passing this option to `jwt.Parse()` will not result in a successful
   494  // verification. Please make sure to carefully read the documentation in
   495  // `jws.VerifyAuto()`, and provide the necessary Whitelist object via
   496  // `jwt.WithFetchWhitelist()`
   497  //
   498  // You might also consider using a backoff policy by using `jwt.WithFetchBackoff()`
   499  // to control the number of requests being made.
   500  func WithVerifyAuto(v bool) ParseOption {
   501  	return newParseOption(identVerifyAuto{}, v)
   502  }
   503  
   504  // WithFetchWhitelist specifies the `jwk.Whitelist` object that should be
   505  // passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
   506  //
   507  // This is a wrapper over `jws.WithFetchWhitelist()` that can be passed
   508  // to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
   509  func WithFetchWhitelist(wl jwk.Whitelist) ParseOption {
   510  	return newParseOption(identFetchWhitelist{}, wl)
   511  }
   512  
   513  // WithHTTPClient specifies the `*http.Client` object that should be
   514  // passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
   515  //
   516  // This is a wrapper over `jws.WithHTTPClient()` that can be passed
   517  // to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
   518  func WithHTTPClient(httpcl *http.Client) ParseOption {
   519  	return newParseOption(identHTTPClient{}, httpcl)
   520  }
   521  
   522  // WithFetchBackoff specifies the `backoff.Policy` object that should be
   523  // passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
   524  //
   525  // This is a wrapper over `jws.WithFetchBackoff()` that can be passed
   526  // to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
   527  func WithFetchBackoff(b backoff.Policy) ParseOption {
   528  	return newParseOption(identFetchBackoff{}, b)
   529  }
   530  
   531  // WithJWKSetFetcher specifies the `jws.JWKSetFetcher` object that should be
   532  // passed to `jws.VerifyAuto()`
   533  //
   534  // This is a wrapper over `jws.WithJWKSetFetcher()` that can be passed
   535  // to `jwt.Parse()`.
   536  func WithJWKSetFetcher(f jws.JWKSetFetcher) ParseOption {
   537  	return newParseOption(identJWKSetFetcher{}, f)
   538  }
   539  

View as plain text