Revert "feat: port clockskew support (#139)" (#184)

This reverts commit d489c99d3e.
This commit is contained in:
Michael Fridman 2022-03-26 10:13:03 -04:00 committed by GitHub
parent 1096e506e6
commit 0972257eba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 40 additions and 149 deletions

View File

@ -9,10 +9,7 @@ import (
// Claims must just have a Valid method that determines // Claims must just have a Valid method that determines
// if the token is invalid for any supported reason // if the token is invalid for any supported reason
type Claims interface { type Claims interface {
// Valid implements claim validation. The opts are function style options that can Valid() error
// be used to fine-tune the validation. The type used for the options is intentionally
// un-exported, since its API and its naming is subject to change.
Valid(opts ...validationOption) error
} }
// RegisteredClaims are a structured version of the JWT Claims Set, // RegisteredClaims are a structured version of the JWT Claims Set,
@ -51,13 +48,13 @@ type RegisteredClaims struct {
// There is no accounting for clock skew. // There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still // As well, if any of the above claims are not in the token, it will still
// be considered a valid claim. // be considered a valid claim.
func (c RegisteredClaims) Valid(opts ...validationOption) error { func (c RegisteredClaims) Valid() error {
vErr := new(ValidationError) vErr := new(ValidationError)
now := TimeFunc() now := TimeFunc()
// The claims below are optional, by default, so if they are set to the // The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them. // default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false, opts...) { if !c.VerifyExpiresAt(now, false) {
delta := now.Sub(c.ExpiresAt.Time) delta := now.Sub(c.ExpiresAt.Time)
vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta)
vErr.Errors |= ValidationErrorExpired vErr.Errors |= ValidationErrorExpired
@ -68,7 +65,7 @@ func (c RegisteredClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorIssuedAt vErr.Errors |= ValidationErrorIssuedAt
} }
if !c.VerifyNotBefore(now, false, opts...) { if !c.VerifyNotBefore(now, false) {
vErr.Inner = ErrTokenNotValidYet vErr.Inner = ErrTokenNotValidYet
vErr.Errors |= ValidationErrorNotValidYet vErr.Errors |= ValidationErrorNotValidYet
} }
@ -88,16 +85,12 @@ func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp). // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset. // If req is false, it will return true, if exp is unset.
func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool, opts ...validationOption) bool { func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
if c.ExpiresAt == nil { if c.ExpiresAt == nil {
return verifyExp(nil, cmp, req, validator.leeway) return verifyExp(nil, cmp, req)
} }
return verifyExp(&c.ExpiresAt.Time, cmp, req, validator.leeway) return verifyExp(&c.ExpiresAt.Time, cmp, req)
} }
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@ -112,16 +105,12 @@ func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset. // If req is false, it will return true, if nbf is unset.
func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool, opts ...validationOption) bool { func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
if c.NotBefore == nil { if c.NotBefore == nil {
return verifyNbf(nil, cmp, req, validator.leeway) return verifyNbf(nil, cmp, req)
} }
return verifyNbf(&c.NotBefore.Time, cmp, req, validator.leeway) return verifyNbf(&c.NotBefore.Time, cmp, req)
} }
// VerifyIssuer compares the iss claim against cmp. // VerifyIssuer compares the iss claim against cmp.
@ -152,13 +141,13 @@ type StandardClaims struct {
// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew. // Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still // As well, if any of the above claims are not in the token, it will still
// be considered a valid claim. // be considered a valid claim.
func (c StandardClaims) Valid(opts ...validationOption) error { func (c StandardClaims) Valid() error {
vErr := new(ValidationError) vErr := new(ValidationError)
now := TimeFunc().Unix() now := TimeFunc().Unix()
// The claims below are optional, by default, so if they are set to the // The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them. // default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false, opts...) { if !c.VerifyExpiresAt(now, false) {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta)
vErr.Errors |= ValidationErrorExpired vErr.Errors |= ValidationErrorExpired
@ -169,7 +158,7 @@ func (c StandardClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorIssuedAt vErr.Errors |= ValidationErrorIssuedAt
} }
if !c.VerifyNotBefore(now, false, opts...) { if !c.VerifyNotBefore(now, false) {
vErr.Inner = ErrTokenNotValidYet vErr.Inner = ErrTokenNotValidYet
vErr.Errors |= ValidationErrorNotValidYet vErr.Errors |= ValidationErrorNotValidYet
} }
@ -189,17 +178,13 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp). // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset. // If req is false, it will return true, if exp is unset.
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption) bool { func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
if c.ExpiresAt == 0 { if c.ExpiresAt == 0 {
return verifyExp(nil, time.Unix(cmp, 0), req, validator.leeway) return verifyExp(nil, time.Unix(cmp, 0), req)
} }
t := time.Unix(c.ExpiresAt, 0) t := time.Unix(c.ExpiresAt, 0)
return verifyExp(&t, time.Unix(cmp, 0), req, validator.leeway) return verifyExp(&t, time.Unix(cmp, 0), req)
} }
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@ -215,17 +200,13 @@ func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset. // If req is false, it will return true, if nbf is unset.
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption) bool { func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
validator := validator{}
for _, o := range opts {
o(&validator)
}
if c.NotBefore == 0 { if c.NotBefore == 0 {
return verifyNbf(nil, time.Unix(cmp, 0), req, validator.leeway) return verifyNbf(nil, time.Unix(cmp, 0), req)
} }
t := time.Unix(c.NotBefore, 0) t := time.Unix(c.NotBefore, 0)
return verifyNbf(&t, time.Unix(cmp, 0), req, validator.leeway) return verifyNbf(&t, time.Unix(cmp, 0), req)
} }
// VerifyIssuer compares the iss claim against cmp. // VerifyIssuer compares the iss claim against cmp.
@ -259,11 +240,11 @@ func verifyAud(aud []string, cmp string, required bool) bool {
return result return result
} }
func verifyExp(exp *time.Time, now time.Time, required bool, skew time.Duration) bool { func verifyExp(exp *time.Time, now time.Time, required bool) bool {
if exp == nil { if exp == nil {
return !required return !required
} }
return now.Before((*exp).Add(+skew)) return now.Before(*exp)
} }
func verifyIat(iat *time.Time, now time.Time, required bool) bool { func verifyIat(iat *time.Time, now time.Time, required bool) bool {
@ -273,12 +254,11 @@ func verifyIat(iat *time.Time, now time.Time, required bool) bool {
return now.After(*iat) || now.Equal(*iat) return now.After(*iat) || now.Equal(*iat)
} }
func verifyNbf(nbf *time.Time, now time.Time, required bool, skew time.Duration) bool { func verifyNbf(nbf *time.Time, now time.Time, required bool) bool {
if nbf == nil { if nbf == nil {
return !required return !required
} }
t := (*nbf).Add(-skew) return now.After(*nbf) || now.Equal(*nbf)
return now.After(t) || now.Equal(t)
} }
func verifyIss(iss string, cmp string, required bool) bool { func verifyIss(iss string, cmp string, required bool) bool {

4
go.mod
View File

@ -1,3 +1,7 @@
module github.com/golang-jwt/jwt/v4 module github.com/golang-jwt/jwt/v4
go 1.16 go 1.16
retract (
v4.4.0 // Contains a backwards incompatible change to the Claims interface.
)

View File

@ -34,7 +34,7 @@ func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
// VerifyExpiresAt compares the exp claim against cmp (cmp <= exp). // VerifyExpiresAt compares the exp claim against cmp (cmp <= exp).
// If req is false, it will return true, if exp is unset. // If req is false, it will return true, if exp is unset.
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption) bool { func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0) cmpTime := time.Unix(cmp, 0)
v, ok := m["exp"] v, ok := m["exp"]
@ -42,22 +42,17 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption
return !req return !req
} }
validator := validator{}
for _, o := range opts {
o(&validator)
}
switch exp := v.(type) { switch exp := v.(type) {
case float64: case float64:
if exp == 0 { if exp == 0 {
return verifyExp(nil, cmpTime, req, validator.leeway) return verifyExp(nil, cmpTime, req)
} }
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req, validator.leeway) return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req)
case json.Number: case json.Number:
v, _ := exp.Float64() v, _ := exp.Float64()
return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req, validator.leeway) return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req)
} }
return false return false
@ -91,7 +86,7 @@ func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset. // If req is false, it will return true, if nbf is unset.
func (m MapClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption) bool { func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0) cmpTime := time.Unix(cmp, 0)
v, ok := m["nbf"] v, ok := m["nbf"]
@ -99,22 +94,17 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption
return !req return !req
} }
validator := validator{}
for _, o := range opts {
o(&validator)
}
switch nbf := v.(type) { switch nbf := v.(type) {
case float64: case float64:
if nbf == 0 { if nbf == 0 {
return verifyNbf(nil, cmpTime, req, validator.leeway) return verifyNbf(nil, cmpTime, req)
} }
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req, validator.leeway) return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req)
case json.Number: case json.Number:
v, _ := nbf.Float64() v, _ := nbf.Float64()
return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req, validator.leeway) return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req)
} }
return false return false
@ -131,11 +121,11 @@ func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
// There is no accounting for clock skew. // There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still // As well, if any of the above claims are not in the token, it will still
// be considered a valid claim. // be considered a valid claim.
func (m MapClaims) Valid(opts ...validationOption) error { func (m MapClaims) Valid() error {
vErr := new(ValidationError) vErr := new(ValidationError)
now := TimeFunc().Unix() now := TimeFunc().Unix()
if !m.VerifyExpiresAt(now, false, opts...) { if !m.VerifyExpiresAt(now, false) {
// TODO(oxisto): this should be replaced with ErrTokenExpired // TODO(oxisto): this should be replaced with ErrTokenExpired
vErr.Inner = errors.New("Token is expired") vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired vErr.Errors |= ValidationErrorExpired
@ -147,7 +137,7 @@ func (m MapClaims) Valid(opts ...validationOption) error {
vErr.Errors |= ValidationErrorIssuedAt vErr.Errors |= ValidationErrorIssuedAt
} }
if !m.VerifyNotBefore(now, false, opts...) { if !m.VerifyNotBefore(now, false) {
// TODO(oxisto): this should be replaced with ErrTokenNotValidYet // TODO(oxisto): this should be replaced with ErrTokenNotValidYet
vErr.Inner = errors.New("Token is not valid yet") vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet vErr.Errors |= ValidationErrorNotValidYet

View File

@ -22,8 +22,6 @@ type Parser struct {
// //
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead. // Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
SkipClaimsValidation bool SkipClaimsValidation bool
validationOptions []validationOption
} }
// NewParser creates a new Parser with the specified options // NewParser creates a new Parser with the specified options
@ -84,7 +82,8 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
// Validate Claims // Validate Claims
if !p.SkipClaimsValidation { if !p.SkipClaimsValidation {
if err := token.Claims.Valid(p.validationOptions...); err != nil { if err := token.Claims.Valid(); err != nil {
// If the Claims Valid returned an error, check if it is a validation error, // If the Claims Valid returned an error, check if it is a validation error,
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
if e, ok := err.(*ValidationError); !ok { if e, ok := err.(*ValidationError); !ok {

View File

@ -1,7 +1,5 @@
package jwt package jwt
import "time"
// ParserOption is used to implement functional-style options that modify the behavior of the parser. To add // ParserOption is used to implement functional-style options that modify the behavior of the parser. To add
// new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that // new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that
// takes a *Parser type as input and manipulates its configuration accordingly. // takes a *Parser type as input and manipulates its configuration accordingly.
@ -29,10 +27,3 @@ func WithoutClaimsValidation() ParserOption {
p.SkipClaimsValidation = true p.SkipClaimsValidation = true
} }
} }
// WithLeeway returns the ParserOption for specifying the leeway window.
func WithLeeway(d time.Duration) ParserOption {
return func(p *Parser) {
p.validationOptions = append(p.validationOptions, withLeeway(d))
}
}

View File

@ -78,28 +78,6 @@ var jwtTestData = []struct {
nil, nil,
jwt.SigningMethodRS256, jwt.SigningMethodRS256,
}, },
{
"basic expired with 60s skew",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
false,
jwt.ValidationErrorExpired,
[]error{jwt.ErrTokenExpired},
jwt.NewParser(jwt.WithLeeway(time.Minute)),
jwt.SigningMethodRS256,
},
{
"basic expired with 120s skew",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
true,
0,
nil,
jwt.NewParser(jwt.WithLeeway(2 * time.Minute)),
jwt.SigningMethodRS256,
},
{ {
"basic nbf", "basic nbf",
"", // autogen "", // autogen
@ -111,28 +89,6 @@ var jwtTestData = []struct {
nil, nil,
jwt.SigningMethodRS256, jwt.SigningMethodRS256,
}, },
{
"basic nbf with 60s skew",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
false,
jwt.ValidationErrorNotValidYet,
[]error{jwt.ErrTokenNotValidYet},
jwt.NewParser(jwt.WithLeeway(time.Minute)),
jwt.SigningMethodRS256,
},
{
"basic nbf with 120s skew",
"", // autogen
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
true,
0,
nil,
jwt.NewParser(jwt.WithLeeway(2 * time.Minute)),
jwt.SigningMethodRS256,
},
{ {
"expired and nbf", "expired and nbf",
"", // autogen "", // autogen

View File

@ -1,29 +0,0 @@
package jwt
import "time"
// validationOption is used to implement functional-style options that modify the behavior of the parser. To add
// new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that
// takes a *validator type as input and manipulates its configuration accordingly.
//
// Note that this struct is (currently) un-exported, its naming is subject to change and will only be exported once
// the API is more stable.
type validationOption func(*validator)
// validator represents options that can be used for claims validation
//
// Note that this struct is (currently) un-exported, its naming is subject to change and will only be exported once
// the API is more stable.
type validator struct {
leeway time.Duration // Leeway to provide when validating time values
}
// withLeeway is an option to set the clock skew (leeway) window
//
// 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 withLeeway(d time.Duration) validationOption {
return func(v *validator) {
v.leeway = d
}
}