From 908d356713b02037b936a3b007ecf98ea8978a92 Mon Sep 17 00:00:00 2001 From: Tarek Sharafi Date: Mon, 9 Oct 2023 20:58:20 +0300 Subject: [PATCH] feat: allow making exp claim required (#351) --- parser_option.go | 8 ++++++++ parser_test.go | 10 ++++++++++ validator.go | 8 ++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/parser_option.go b/parser_option.go index 1b5af97..88a780f 100644 --- a/parser_option.go +++ b/parser_option.go @@ -58,6 +58,14 @@ func WithIssuedAt() ParserOption { } } +// WithExpirationRequired returns the ParserOption to make exp claim required. +// By default exp claim is optional. +func WithExpirationRequired() ParserOption { + return func(p *Parser) { + p.validator.requireExp = true + } +} + // WithAudience configures the validator to require the specified audience in // the `aud` claim. Validation will fail if the audience is not listed in the // token or the `aud` claim is missing. diff --git a/parser_test.go b/parser_test.go index 1825dfc..c0f8171 100644 --- a/parser_test.go +++ b/parser_test.go @@ -423,6 +423,16 @@ var jwtTestData = []struct { jwt.NewParser(jwt.WithLeeway(2 * time.Minute)), jwt.SigningMethodRS256, }, + { + "rejects if exp is required but missing", + "", // autogen + defaultKeyFunc, + &jwt.RegisteredClaims{}, + false, + []error{jwt.ErrTokenInvalidClaims}, + jwt.NewParser(jwt.WithExpirationRequired()), + jwt.SigningMethodRS256, + }, } // signToken creates and returns a signed JWT token using signingMethod. diff --git a/validator.go b/validator.go index 3850438..3082c8c 100644 --- a/validator.go +++ b/validator.go @@ -42,6 +42,9 @@ type validator struct { // validation. If unspecified, this defaults to time.Now. timeFunc func() time.Time + // requireExp specifies whether the exp claim is required + requireExp bool + // verifyIat specifies whether the iat (Issued At) claim will be verified. // According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this // only specifies the age of the token, but no validation check is @@ -86,8 +89,9 @@ func (v *validator) Validate(claims Claims) error { } // We always need to check the expiration time, but usage of the claim - // itself is OPTIONAL. - if err = v.verifyExpiresAt(claims, now, false); err != nil { + // itself is OPTIONAL by default. requireExp overrides this behavior + // and makes the exp claim mandatory. + if err = v.verifyExpiresAt(claims, now, v.requireExp); err != nil { errs = append(errs, err) }