forked from mirror/jwt
Validation Options: Experiment 1
This commit is contained in:
parent
cf43decf7c
commit
9322ee9417
31
claims.go
31
claims.go
|
@ -22,6 +22,8 @@ type Claims interface {
|
||||||
//
|
//
|
||||||
// See examples for how to use this with your own claim types.
|
// See examples for how to use this with your own claim types.
|
||||||
type RegisteredClaims struct {
|
type RegisteredClaims struct {
|
||||||
|
v validator
|
||||||
|
|
||||||
// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
|
// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
|
||||||
Issuer string `json:"iss,omitempty"`
|
Issuer string `json:"iss,omitempty"`
|
||||||
|
|
||||||
|
@ -87,10 +89,10 @@ func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
|
||||||
// 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) bool {
|
||||||
if c.ExpiresAt == nil {
|
if c.ExpiresAt == nil {
|
||||||
return verifyExp(nil, cmp, req)
|
return verifyExp(nil, cmp, req, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyExp(&c.ExpiresAt.Time, cmp, req)
|
return verifyExp(&c.ExpiresAt.Time, cmp, req, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
||||||
|
@ -107,10 +109,10 @@ func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
|
||||||
// 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) bool {
|
||||||
if c.NotBefore == nil {
|
if c.NotBefore == nil {
|
||||||
return verifyNbf(nil, cmp, req)
|
return verifyNbf(nil, cmp, req, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyNbf(&c.NotBefore.Time, cmp, req)
|
return verifyNbf(&c.NotBefore.Time, cmp, req, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuer compares the iss claim against cmp.
|
// VerifyIssuer compares the iss claim against cmp.
|
||||||
|
@ -129,6 +131,8 @@ func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||||
//
|
//
|
||||||
// Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
|
// Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
|
||||||
type StandardClaims struct {
|
type StandardClaims struct {
|
||||||
|
v validator
|
||||||
|
|
||||||
Audience string `json:"aud,omitempty"`
|
Audience string `json:"aud,omitempty"`
|
||||||
ExpiresAt int64 `json:"exp,omitempty"`
|
ExpiresAt int64 `json:"exp,omitempty"`
|
||||||
Id string `json:"jti,omitempty"`
|
Id string `json:"jti,omitempty"`
|
||||||
|
@ -180,11 +184,11 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
|
||||||
// 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) bool {
|
||||||
if c.ExpiresAt == 0 {
|
if c.ExpiresAt == 0 {
|
||||||
return verifyExp(nil, time.Unix(cmp, 0), req)
|
return verifyExp(nil, time.Unix(cmp, 0), req, c.v.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, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
|
||||||
|
@ -202,11 +206,11 @@ func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||||
// 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) bool {
|
||||||
if c.NotBefore == 0 {
|
if c.NotBefore == 0 {
|
||||||
return verifyNbf(nil, time.Unix(cmp, 0), req)
|
return verifyNbf(nil, time.Unix(cmp, 0), req, c.v.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, c.v.leeway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyIssuer compares the iss claim against cmp.
|
// VerifyIssuer compares the iss claim against cmp.
|
||||||
|
@ -240,11 +244,12 @@ 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 +259,13 @@ 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 {
|
||||||
|
|
|
@ -45,14 +45,14 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||||
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, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req)
|
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req, 0)
|
||||||
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, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -97,14 +97,14 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||||
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, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req)
|
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req, 0)
|
||||||
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, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
14
parser.go
14
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
|
||||||
|
|
||||||
|
v validator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParser creates a new Parser with the specified options
|
// NewParser creates a new Parser with the specified options
|
||||||
|
@ -80,6 +82,18 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
||||||
|
|
||||||
vErr := &ValidationError{}
|
vErr := &ValidationError{}
|
||||||
|
|
||||||
|
// Experimental: inject the validation options of the parser into the
|
||||||
|
// claims. This provides a backwards compatible way to have validation
|
||||||
|
// options, but it unfortunately only works for jwt.RegisteredClaims and
|
||||||
|
// jwt.StandardClaims
|
||||||
|
if rclaims, ok := claims.(*RegisteredClaims); ok {
|
||||||
|
rclaims.v = p.v
|
||||||
|
}
|
||||||
|
|
||||||
|
if sclaims, ok := claims.(*StandardClaims); ok {
|
||||||
|
sclaims.v = p.v
|
||||||
|
}
|
||||||
|
|
||||||
// Validate Claims
|
// Validate Claims
|
||||||
if !p.SkipClaimsValidation {
|
if !p.SkipClaimsValidation {
|
||||||
if err := token.Claims.Valid(); err != nil {
|
if err := token.Claims.Valid(); err != nil {
|
||||||
|
|
|
@ -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.v.leeway = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -321,6 +321,19 @@ var jwtTestData = []struct {
|
||||||
&jwt.Parser{UseJSONNumber: true},
|
&jwt.Parser{UseJSONNumber: true},
|
||||||
jwt.SigningMethodRS256,
|
jwt.SigningMethodRS256,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"RFC7519 Claims - expired by 100s with 120s skew",
|
||||||
|
"", // autogen
|
||||||
|
defaultKeyFunc,
|
||||||
|
&jwt.RegisteredClaims{
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * 100)),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
jwt.NewParser(jwt.WithLeeway(2 * time.Minute)),
|
||||||
|
jwt.SigningMethodRS256,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// signToken creates and returns a signed JWT token using signingMethod.
|
// signToken creates and returns a signed JWT token using signingMethod.
|
||||||
|
|
Loading…
Reference in New Issue