Skip to content

Add WithNotBeforeRequired parser option and add test coverage #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions parser_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ func WithExpirationRequired() ParserOption {
}
}

// WithNotBeforeRequired returns the ParserOption to make nbf claim required.
// By default nbf claim is optional.
func WithNotBeforeRequired() ParserOption {
return func(p *Parser) {
p.validator.requireNbf = true
}
}

// WithAudience configures the validator to require any of the specified
// audiences in the `aud` claim. Validation will fail if the audience is not
// listed in the token or the `aud` claim is missing.
Expand Down
8 changes: 6 additions & 2 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type Validator struct {
// requireExp specifies whether the exp claim is required
requireExp bool

// requireNbf specifies whether the nbf claim is required
requireNbf 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
Expand Down Expand Up @@ -111,8 +114,9 @@ func (v *Validator) Validate(claims Claims) error {
}

// We always need to check not-before, but usage of the claim itself is
// OPTIONAL.
if err = v.verifyNotBefore(claims, now, false); err != nil {
// OPTIONAL by default. requireNbf overrides this behavior and makes
// the nbf claim mandatory.
if err = v.verifyNotBefore(claims, now, v.requireNbf); err != nil {
errs = append(errs, err)
}

Expand Down
67 changes: 67 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,73 @@ func Test_Validator_verifyIssuedAt(t *testing.T) {
}
}

func Test_Validator_requireNotBefore(t *testing.T) {
type fields struct {
leeway time.Duration
timeFunc func() time.Time
requireNbf bool
}
type args struct {
claims Claims
cmp time.Time
required bool
}
tests := []struct {
name string
fields fields
args args
wantErr error
}{
{
name: "good claim without nbf",
fields: fields{requireNbf: false},
args: args{claims: MapClaims{}, required: false},
wantErr: nil,
},
{
name: "good claim with nbf",
fields: fields{requireNbf: true},
args: args{
claims: RegisteredClaims{NotBefore: NewNumericDate(time.Now().Add(time.Minute * -10))},
cmp: time.Now().Add(10 * time.Minute),
required: true,
},
wantErr: nil,
},
{
name: "token nbf time is in future",
fields: fields{requireNbf: true, timeFunc: time.Now},
args: args{
claims: RegisteredClaims{NotBefore: NewNumericDate(time.Now().Add(time.Minute * +10))},
cmp: time.Now().Add(10 * time.Minute),
required: true,
},
wantErr: ErrTokenNotValidYet,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts := []ParserOption{
WithLeeway(tt.fields.leeway),
}

if tt.fields.requireNbf {
opts = append(opts, WithNotBeforeRequired())
}

if tt.fields.timeFunc != nil {
opts = append(opts, WithTimeFunc(tt.fields.timeFunc))
}

v := NewValidator(opts...)

if err := v.verifyNotBefore(tt.args.claims, tt.args.cmp, tt.args.required); (err != nil) && !errors.Is(err, tt.wantErr) {
t.Errorf("validator.requireNotBefore() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func Test_Validator_verifyAudience(t *testing.T) {
type fields struct {
expectedAud []string
Expand Down