forked from mirror/jwt
Compare commits
1 Commits
main
...
validation
Author | SHA1 | Date |
---|---|---|
Christian Banse | 03ffd83e84 |
85
claims.go
85
claims.go
|
@ -9,9 +9,29 @@ 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
|
||||||
|
// 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() error
|
Valid() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClaimsWithOptions must just have a Valid method that determines
|
||||||
|
// if the token is invalid for any supported reason
|
||||||
|
type ClaimsWithOptions interface {
|
||||||
|
// Valid implements claim validation. The opts are function style options that can
|
||||||
|
// 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.
|
||||||
|
ValidWithOptions(opts ...validationOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c StandardClaims) Valid() error {
|
||||||
|
return c.ValidWithOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c RegisteredClaims) Valid() error {
|
||||||
|
return c.ValidWithOptions()
|
||||||
|
}
|
||||||
|
|
||||||
// RegisteredClaims are a structured version of the JWT Claims Set,
|
// RegisteredClaims are a structured version of the JWT Claims Set,
|
||||||
// restricted to Registered Claim Names, as referenced at
|
// restricted to Registered Claim Names, as referenced at
|
||||||
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
|
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
|
||||||
|
@ -44,17 +64,17 @@ type RegisteredClaims struct {
|
||||||
ID string `json:"jti,omitempty"`
|
ID string `json:"jti,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid validates time based claims "exp, iat, nbf".
|
// ValidWithOptions validates time based claims "exp, iat, nbf".
|
||||||
// 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() error {
|
func (c RegisteredClaims) ValidWithOptions(opts ...validationOption) 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) {
|
if !c.VerifyExpiresAt(now, false, opts...) {
|
||||||
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
|
||||||
|
@ -65,7 +85,7 @@ func (c RegisteredClaims) Valid() error {
|
||||||
vErr.Errors |= ValidationErrorIssuedAt
|
vErr.Errors |= ValidationErrorIssuedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.VerifyNotBefore(now, false) {
|
if !c.VerifyNotBefore(now, false, opts...) {
|
||||||
vErr.Inner = ErrTokenNotValidYet
|
vErr.Inner = ErrTokenNotValidYet
|
||||||
vErr.Errors |= ValidationErrorNotValidYet
|
vErr.Errors |= ValidationErrorNotValidYet
|
||||||
}
|
}
|
||||||
|
@ -85,12 +105,16 @@ 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) bool {
|
func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool, opts ...validationOption) bool {
|
||||||
|
validator := validator{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&validator)
|
||||||
|
}
|
||||||
if c.ExpiresAt == nil {
|
if c.ExpiresAt == nil {
|
||||||
return verifyExp(nil, cmp, req)
|
return verifyExp(nil, cmp, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyExp(&c.ExpiresAt.Time, cmp, req)
|
return verifyExp(&c.ExpiresAt.Time, cmp, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
||||||
|
@ -105,12 +129,16 @@ 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) bool {
|
func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool, opts ...validationOption) bool {
|
||||||
|
validator := validator{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&validator)
|
||||||
|
}
|
||||||
if c.NotBefore == nil {
|
if c.NotBefore == nil {
|
||||||
return verifyNbf(nil, cmp, req)
|
return verifyNbf(nil, cmp, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyNbf(&c.NotBefore.Time, cmp, req)
|
return verifyNbf(&c.NotBefore.Time, cmp, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuer compares the iss claim against cmp.
|
// VerifyIssuer compares the iss claim against cmp.
|
||||||
|
@ -138,16 +166,16 @@ type StandardClaims struct {
|
||||||
Subject string `json:"sub,omitempty"`
|
Subject string `json:"sub,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
|
// ValidWithOptions 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() error {
|
func (c StandardClaims) ValidWithOptions(opts ...validationOption) 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) {
|
if !c.VerifyExpiresAt(now, false, opts...) {
|
||||||
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
|
||||||
|
@ -158,7 +186,7 @@ func (c StandardClaims) Valid() error {
|
||||||
vErr.Errors |= ValidationErrorIssuedAt
|
vErr.Errors |= ValidationErrorIssuedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.VerifyNotBefore(now, false) {
|
if !c.VerifyNotBefore(now, false, opts...) {
|
||||||
vErr.Inner = ErrTokenNotValidYet
|
vErr.Inner = ErrTokenNotValidYet
|
||||||
vErr.Errors |= ValidationErrorNotValidYet
|
vErr.Errors |= ValidationErrorNotValidYet
|
||||||
}
|
}
|
||||||
|
@ -178,13 +206,17 @@ 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) bool {
|
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption) 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)
|
return verifyExp(nil, time.Unix(cmp, 0), req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := time.Unix(c.ExpiresAt, 0)
|
t := time.Unix(c.ExpiresAt, 0)
|
||||||
return verifyExp(&t, time.Unix(cmp, 0), req)
|
return verifyExp(&t, time.Unix(cmp, 0), req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
||||||
|
@ -200,13 +232,17 @@ 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) bool {
|
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption) 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)
|
return verifyNbf(nil, time.Unix(cmp, 0), req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := time.Unix(c.NotBefore, 0)
|
t := time.Unix(c.NotBefore, 0)
|
||||||
return verifyNbf(&t, time.Unix(cmp, 0), req)
|
return verifyNbf(&t, time.Unix(cmp, 0), req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuer compares the iss claim against cmp.
|
// VerifyIssuer compares the iss claim against cmp.
|
||||||
|
@ -240,11 +276,11 @@ func verifyAud(aud []string, cmp string, required bool) bool {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyExp(exp *time.Time, now time.Time, required bool) bool {
|
func verifyExp(exp *time.Time, now time.Time, required bool, skew time.Duration) bool {
|
||||||
if exp == nil {
|
if exp == nil {
|
||||||
return !required
|
return !required
|
||||||
}
|
}
|
||||||
return now.Before(*exp)
|
return now.Before((*exp).Add(+skew))
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyIat(iat *time.Time, now time.Time, required bool) bool {
|
func verifyIat(iat *time.Time, now time.Time, required bool) bool {
|
||||||
|
@ -254,11 +290,12 @@ 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) bool {
|
func verifyNbf(nbf *time.Time, now time.Time, required bool, skew time.Duration) bool {
|
||||||
if nbf == nil {
|
if nbf == nil {
|
||||||
return !required
|
return !required
|
||||||
}
|
}
|
||||||
return now.After(*nbf) || now.Equal(*nbf)
|
t := (*nbf).Add(-skew)
|
||||||
|
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 {
|
||||||
|
|
|
@ -11,6 +11,10 @@ import (
|
||||||
// This is the default claims type if you don't supply one
|
// This is the default claims type if you don't supply one
|
||||||
type MapClaims map[string]interface{}
|
type MapClaims map[string]interface{}
|
||||||
|
|
||||||
|
func (m MapClaims) Valid() error {
|
||||||
|
return m.ValidWithOptions()
|
||||||
|
}
|
||||||
|
|
||||||
// VerifyAudience Compares the aud claim against cmp.
|
// VerifyAudience Compares the aud claim against cmp.
|
||||||
// If required is false, this method will return true if the value matches or is unset
|
// If required is false, this method will return true if the value matches or is unset
|
||||||
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
|
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
|
||||||
|
@ -34,7 +38,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) bool {
|
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool, opts ...validationOption) bool {
|
||||||
cmpTime := time.Unix(cmp, 0)
|
cmpTime := time.Unix(cmp, 0)
|
||||||
|
|
||||||
v, ok := m["exp"]
|
v, ok := m["exp"]
|
||||||
|
@ -42,17 +46,22 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||||
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)
|
return verifyExp(nil, cmpTime, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req)
|
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req, validator.leeway)
|
||||||
case json.Number:
|
case json.Number:
|
||||||
v, _ := exp.Float64()
|
v, _ := exp.Float64()
|
||||||
|
|
||||||
return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req)
|
return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -86,7 +95,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) bool {
|
func (m MapClaims) VerifyNotBefore(cmp int64, req bool, opts ...validationOption) bool {
|
||||||
cmpTime := time.Unix(cmp, 0)
|
cmpTime := time.Unix(cmp, 0)
|
||||||
|
|
||||||
v, ok := m["nbf"]
|
v, ok := m["nbf"]
|
||||||
|
@ -94,17 +103,22 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||||
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)
|
return verifyNbf(nil, cmpTime, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req)
|
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req, validator.leeway)
|
||||||
case json.Number:
|
case json.Number:
|
||||||
v, _ := nbf.Float64()
|
v, _ := nbf.Float64()
|
||||||
|
|
||||||
return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req)
|
return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req, validator.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -121,11 +135,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() error {
|
func (m MapClaims) ValidWithOptions(opts ...validationOption) error {
|
||||||
vErr := new(ValidationError)
|
vErr := new(ValidationError)
|
||||||
now := TimeFunc().Unix()
|
now := TimeFunc().Unix()
|
||||||
|
|
||||||
if !m.VerifyExpiresAt(now, false) {
|
if !m.VerifyExpiresAt(now, false, opts...) {
|
||||||
// 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
|
||||||
|
@ -137,7 +151,7 @@ func (m MapClaims) Valid() error {
|
||||||
vErr.Errors |= ValidationErrorIssuedAt
|
vErr.Errors |= ValidationErrorIssuedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.VerifyNotBefore(now, false) {
|
if !m.VerifyNotBefore(now, false, opts...) {
|
||||||
// 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
|
||||||
|
|
29
parser.go
29
parser.go
|
@ -22,6 +22,8 @@ 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
|
||||||
|
@ -82,14 +84,25 @@ 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(); err != nil {
|
if cwo, ok := token.Claims.(ClaimsWithOptions); ok {
|
||||||
|
if err := cwo.ValidWithOptions(p.validationOptions...); 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 {
|
||||||
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
||||||
} else {
|
} else {
|
||||||
vErr = e
|
vErr = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := token.Claims.Valid(); err != nil {
|
||||||
|
// 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 e, ok := err.(*ValidationError); !ok {
|
||||||
|
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
||||||
|
} else {
|
||||||
|
vErr = e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
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.
|
||||||
|
@ -27,3 +29,10 @@ 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -78,6 +78,28 @@ 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
|
||||||
|
@ -89,6 +111,28 @@ 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
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue