Skip to content
38 changes: 18 additions & 20 deletions claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (c RegisteredClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorExpired
}

if !c.VerifyIssuedAt(now, false) {
if !c.VerifyIssuedAt(now, false, opts...) {
vErr.Inner = ErrTokenUsedBeforeIssued
vErr.Errors |= ValidationErrorIssuedAt
}
Expand All @@ -89,10 +89,7 @@ func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool, opts ...validationOption) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)
if c.ExpiresAt == nil {
return verifyExp(nil, cmp, req, validator.leeway)
}
Expand All @@ -102,7 +99,12 @@ func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool, opts ...vali

// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool, opts ...validationOption) bool {
validator := getValidator(opts...)
if !validator.iat {
return true
}

if c.IssuedAt == nil {
return verifyIat(nil, cmp, req)
}
Expand All @@ -113,10 +115,7 @@ func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool, opts ...validationOption) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)
if c.NotBefore == nil {
return verifyNbf(nil, cmp, req, validator.leeway)
}
Expand Down Expand Up @@ -164,7 +163,7 @@ func (c StandardClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorExpired
}

if !c.VerifyIssuedAt(now, false) {
if !c.VerifyIssuedAt(now, false, opts...) {
vErr.Inner = ErrTokenUsedBeforeIssued
vErr.Errors |= ValidationErrorIssuedAt
}
Expand All @@ -190,10 +189,7 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)
if c.ExpiresAt == 0 {
return verifyExp(nil, time.Unix(cmp, 0), req, validator.leeway)
}
Expand All @@ -204,7 +200,12 @@ func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validation

// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool, opts ...validationOption) bool {
validator := getValidator(opts...)
if !validator.iat {
return true
}

if c.IssuedAt == 0 {
return verifyIat(nil, time.Unix(cmp, 0), req)
}
Expand All @@ -216,10 +217,7 @@ func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)
if c.NotBefore == 0 {
return verifyNbf(nil, time.Unix(cmp, 0), req, validator.leeway)
}
Expand Down
24 changes: 14 additions & 10 deletions map_claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption
return !req
}

validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)

switch exp := v.(type) {
case float64:
Expand All @@ -65,14 +62,24 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption

// VerifyIssuedAt compares the exp claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool, opts ...validationOption) bool {
cmpTime := time.Unix(cmp, 0)

v, ok := m["iat"]
if !ok {
return !req
}

// validate the type
switch v.(type) {
case float64, json.Number:
if !getValidator(opts...).iat {
return true
}
default:
return false
}

switch iat := v.(type) {
case float64:
if iat == 0 {
Expand All @@ -99,10 +106,7 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption
return !req
}

validator := validator{}
for _, o := range opts {
o(&validator)
}
validator := getValidator(opts...)

switch nbf := v.(type) {
case float64:
Expand Down Expand Up @@ -141,7 +145,7 @@ func (m MapClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorExpired
}

if !m.VerifyIssuedAt(now, false) {
if !m.VerifyIssuedAt(now, false, opts...) {
// TODO(oxisto): this should be replaced with ErrTokenUsedBeforeIssued
vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
Expand Down
4 changes: 2 additions & 2 deletions map_claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ func TestMapClaimsVerifyExpiresAtExpire(t *testing.T) {
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, got)
}

got = mapClaims.VerifyExpiresAt(exp + 1, true)
got = mapClaims.VerifyExpiresAt(exp+1, true)
if want != got {
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, got)
}

want = true
got = mapClaims.VerifyExpiresAt(exp - 1, true)
got = mapClaims.VerifyExpiresAt(exp-1, true)
if want != got {
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, got)
}
Expand Down
7 changes: 7 additions & 0 deletions parser_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@ func WithLeeway(d time.Duration) ParserOption {
p.validationOptions = append(p.validationOptions, withLeeway(d))
}
}

// WithIssuedAt is an option to enable the validation of the issued at (iat) claim.
func WithIssuedAt() ParserOption {
return func(p *Parser) {
p.validationOptions = append(p.validationOptions, withIssuedAt())
}
}
19 changes: 19 additions & 0 deletions validator_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type validationOption func(*validator)
// the API is more stable.
type validator struct {
leeway time.Duration // Leeway to provide when validating time values
iat bool
}

// withLeeway is an option to set the clock skew (leeway) window
Expand All @@ -27,3 +28,21 @@ func withLeeway(d time.Duration) validationOption {
v.leeway = d
}
}

// withIssuedAth is an option to enable the validation of the issued at (iat) claim
//
// Note that this function is (currently) un-exported, its naming is subject to change and will only be exported once
// the API is more stable.
func withIssuedAt() validationOption {
return func(v *validator) {
v.iat = true
}
}

func getValidator(opts ...validationOption) validator {
v := validator{}
for _, o := range opts {
o(&v)
}
return v
}