mirror of https://github.com/golang-jwt/jwt.git
Exported `NewValidator` (#349)
* Exported `NewValidator` Previously, we had `newValidator` as a private function. This PR exports this function so that validation can be done independently of parsing the claim.
This commit is contained in:
parent
c776b83291
commit
a49fa5d91d
|
@ -17,7 +17,7 @@ and corresponding updates for existing programs.
|
||||||
|
|
||||||
## Parsing and Validation Options
|
## Parsing and Validation Options
|
||||||
|
|
||||||
Under the hood, a new `validator` struct takes care of validating the claims. A
|
Under the hood, a new `Validator` struct takes care of validating the claims. A
|
||||||
long awaited feature has been the option to fine-tune the validation of tokens.
|
long awaited feature has been the option to fine-tune the validation of tokens.
|
||||||
This is now possible with several `ParserOption` functions that can be appended
|
This is now possible with several `ParserOption` functions that can be appended
|
||||||
to most `Parse` functions, such as `ParseWithClaims`. The most important options
|
to most `Parse` functions, such as `ParseWithClaims`. The most important options
|
||||||
|
@ -68,6 +68,16 @@ type Claims interface {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Users that previously directly called the `Valid` function on their claims,
|
||||||
|
e.g., to perform validation independently of parsing/verifying a token, can now
|
||||||
|
use the `jwt.NewValidator` function to create a `Validator` independently of the
|
||||||
|
`Parser`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
var v = jwt.NewValidator(jwt.WithLeeway(5*time.Second))
|
||||||
|
v.Validate(myClaims)
|
||||||
|
```
|
||||||
|
|
||||||
### Supported Claim Types and Removal of `StandardClaims`
|
### Supported Claim Types and Removal of `StandardClaims`
|
||||||
|
|
||||||
The two standard claim types supported by this library, `MapClaims` and
|
The two standard claim types supported by this library, `MapClaims` and
|
||||||
|
|
|
@ -62,7 +62,7 @@ func TestVerifyAud(t *testing.T) {
|
||||||
opts = append(opts, WithAudience(test.Comparison))
|
opts = append(opts, WithAudience(test.Comparison))
|
||||||
}
|
}
|
||||||
|
|
||||||
validator := newValidator(opts...)
|
validator := NewValidator(opts...)
|
||||||
got := validator.Validate(test.MapClaims)
|
got := validator.Validate(test.MapClaims)
|
||||||
|
|
||||||
if (got == nil) != test.Expected {
|
if (got == nil) != test.Expected {
|
||||||
|
@ -77,7 +77,7 @@ func TestMapclaimsVerifyIssuedAtInvalidTypeString(t *testing.T) {
|
||||||
"iat": "foo",
|
"iat": "foo",
|
||||||
}
|
}
|
||||||
want := false
|
want := false
|
||||||
got := newValidator(WithIssuedAt()).Validate(mapClaims)
|
got := NewValidator(WithIssuedAt()).Validate(mapClaims)
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func TestMapclaimsVerifyNotBeforeInvalidTypeString(t *testing.T) {
|
||||||
"nbf": "foo",
|
"nbf": "foo",
|
||||||
}
|
}
|
||||||
want := false
|
want := false
|
||||||
got := newValidator().Validate(mapClaims)
|
got := NewValidator().Validate(mapClaims)
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ func TestMapclaimsVerifyExpiresAtInvalidTypeString(t *testing.T) {
|
||||||
"exp": "foo",
|
"exp": "foo",
|
||||||
}
|
}
|
||||||
want := false
|
want := false
|
||||||
got := newValidator().Validate(mapClaims)
|
got := NewValidator().Validate(mapClaims)
|
||||||
|
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
||||||
|
@ -112,14 +112,14 @@ func TestMapClaimsVerifyExpiresAtExpire(t *testing.T) {
|
||||||
"exp": float64(exp.Unix()),
|
"exp": float64(exp.Unix()),
|
||||||
}
|
}
|
||||||
want := false
|
want := false
|
||||||
got := newValidator(WithTimeFunc(func() time.Time {
|
got := NewValidator(WithTimeFunc(func() time.Time {
|
||||||
return exp
|
return exp
|
||||||
})).Validate(mapClaims)
|
})).Validate(mapClaims)
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
got = newValidator(WithTimeFunc(func() time.Time {
|
got = NewValidator(WithTimeFunc(func() time.Time {
|
||||||
return exp.Add(1 * time.Second)
|
return exp.Add(1 * time.Second)
|
||||||
})).Validate(mapClaims)
|
})).Validate(mapClaims)
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
|
@ -127,7 +127,7 @@ func TestMapClaimsVerifyExpiresAtExpire(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
want = true
|
want = true
|
||||||
got = newValidator(WithTimeFunc(func() time.Time {
|
got = NewValidator(WithTimeFunc(func() time.Time {
|
||||||
return exp.Add(-1 * time.Second)
|
return exp.Add(-1 * time.Second)
|
||||||
})).Validate(mapClaims)
|
})).Validate(mapClaims)
|
||||||
if want != (got == nil) {
|
if want != (got == nil) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Parser struct {
|
||||||
// Skip claims validation during token parsing.
|
// Skip claims validation during token parsing.
|
||||||
skipClaimsValidation bool
|
skipClaimsValidation bool
|
||||||
|
|
||||||
validator *validator
|
validator *Validator
|
||||||
|
|
||||||
decodeStrict bool
|
decodeStrict bool
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ type Parser struct {
|
||||||
// NewParser creates a new Parser with the specified options
|
// NewParser creates a new Parser with the specified options
|
||||||
func NewParser(options ...ParserOption) *Parser {
|
func NewParser(options ...ParserOption) *Parser {
|
||||||
p := &Parser{
|
p := &Parser{
|
||||||
validator: &validator{},
|
validator: &Validator{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through our parsing options and apply them
|
// Loop through our parsing options and apply them
|
||||||
|
@ -115,7 +115,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
||||||
if !p.skipClaimsValidation {
|
if !p.skipClaimsValidation {
|
||||||
// Make sure we have at least a default validator
|
// Make sure we have at least a default validator
|
||||||
if p.validator == nil {
|
if p.validator == nil {
|
||||||
p.validator = newValidator()
|
p.validator = NewValidator()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.validator.Validate(claims); err != nil {
|
if err := p.validator.Validate(claims); err != nil {
|
||||||
|
|
39
validator.go
39
validator.go
|
@ -28,13 +28,12 @@ type ClaimsValidator interface {
|
||||||
Validate() error
|
Validate() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// validator is the core of the new Validation API. It is automatically used by
|
// Validator is the core of the new Validation API. It is automatically used by
|
||||||
// a [Parser] during parsing and can be modified with various parser options.
|
// a [Parser] during parsing and can be modified with various parser options.
|
||||||
//
|
//
|
||||||
// Note: This struct is intentionally not exported (yet) as we want to
|
// The [NewValidator] function should be used to create an instance of this
|
||||||
// internally finalize its API. In the future, we might make it publicly
|
// struct.
|
||||||
// available.
|
type Validator struct {
|
||||||
type validator struct {
|
|
||||||
// leeway is an optional leeway that can be provided to account for clock skew.
|
// leeway is an optional leeway that can be provided to account for clock skew.
|
||||||
leeway time.Duration
|
leeway time.Duration
|
||||||
|
|
||||||
|
@ -65,16 +64,28 @@ type validator struct {
|
||||||
expectedSub string
|
expectedSub string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newValidator can be used to create a stand-alone validator with the supplied
|
// NewValidator can be used to create a stand-alone validator with the supplied
|
||||||
// options. This validator can then be used to validate already parsed claims.
|
// options. This validator can then be used to validate already parsed claims.
|
||||||
func newValidator(opts ...ParserOption) *validator {
|
//
|
||||||
|
// Note: Under normal circumstances, explicitly creating a validator is not
|
||||||
|
// needed and can potentially be dangerous; instead functions of the [Parser]
|
||||||
|
// class should be used.
|
||||||
|
//
|
||||||
|
// The [Validator] is only checking the *validity* of the claims, such as its
|
||||||
|
// expiration time, but it does NOT perform *signature verification* of the
|
||||||
|
// token.
|
||||||
|
func NewValidator(opts ...ParserOption) *Validator {
|
||||||
p := NewParser(opts...)
|
p := NewParser(opts...)
|
||||||
return p.validator
|
return p.validator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the given claims. It will also perform any custom
|
// Validate validates the given claims. It will also perform any custom
|
||||||
// validation if claims implements the [ClaimsValidator] interface.
|
// validation if claims implements the [ClaimsValidator] interface.
|
||||||
func (v *validator) Validate(claims Claims) error {
|
//
|
||||||
|
// Note: It will NOT perform any *signature verification* on the token that
|
||||||
|
// contains the claims and expects that the [Claim] was already successfully
|
||||||
|
// verified.
|
||||||
|
func (v *Validator) Validate(claims Claims) error {
|
||||||
var (
|
var (
|
||||||
now time.Time
|
now time.Time
|
||||||
errs []error = make([]error, 0, 6)
|
errs []error = make([]error, 0, 6)
|
||||||
|
@ -153,7 +164,7 @@ func (v *validator) Validate(claims Claims) error {
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error {
|
func (v *Validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error {
|
||||||
exp, err := claims.GetExpirationTime()
|
exp, err := claims.GetExpirationTime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -174,7 +185,7 @@ func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool)
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error {
|
func (v *Validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error {
|
||||||
iat, err := claims.GetIssuedAt()
|
iat, err := claims.GetIssuedAt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -195,7 +206,7 @@ func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool)
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error {
|
func (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error {
|
||||||
nbf, err := claims.GetNotBefore()
|
nbf, err := claims.GetNotBefore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -215,7 +226,7 @@ func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool)
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifyAudience(claims Claims, cmp string, required bool) error {
|
func (v *Validator) verifyAudience(claims Claims, cmp string, required bool) error {
|
||||||
aud, err := claims.GetAudience()
|
aud, err := claims.GetAudience()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -251,7 +262,7 @@ func (v *validator) verifyAudience(claims Claims, cmp string, required bool) err
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error {
|
func (v *Validator) verifyIssuer(claims Claims, cmp string, required bool) error {
|
||||||
iss, err := claims.GetIssuer()
|
iss, err := claims.GetIssuer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -271,7 +282,7 @@ func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error
|
||||||
//
|
//
|
||||||
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
// Additionally, if any error occurs while retrieving the claim, e.g., when its
|
||||||
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
// the wrong type, an ErrTokenUnverifiable error will be returned.
|
||||||
func (v *validator) verifySubject(claims Claims, cmp string, required bool) error {
|
func (v *Validator) verifySubject(claims Claims, cmp string, required bool) error {
|
||||||
sub, err := claims.GetSubject()
|
sub, err := claims.GetSubject()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (m MyCustomClaims) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_validator_Validate(t *testing.T) {
|
func Test_Validator_Validate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
leeway time.Duration
|
leeway time.Duration
|
||||||
timeFunc func() time.Time
|
timeFunc func() time.Time
|
||||||
|
@ -71,7 +71,7 @@ func Test_validator_Validate(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := &validator{
|
v := &Validator{
|
||||||
leeway: tt.fields.leeway,
|
leeway: tt.fields.leeway,
|
||||||
timeFunc: tt.fields.timeFunc,
|
timeFunc: tt.fields.timeFunc,
|
||||||
verifyIat: tt.fields.verifyIat,
|
verifyIat: tt.fields.verifyIat,
|
||||||
|
@ -86,7 +86,7 @@ func Test_validator_Validate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_validator_verifyExpiresAt(t *testing.T) {
|
func Test_Validator_verifyExpiresAt(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
leeway time.Duration
|
leeway time.Duration
|
||||||
timeFunc func() time.Time
|
timeFunc func() time.Time
|
||||||
|
@ -117,7 +117,7 @@ func Test_validator_verifyExpiresAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := &validator{
|
v := &Validator{
|
||||||
leeway: tt.fields.leeway,
|
leeway: tt.fields.leeway,
|
||||||
timeFunc: tt.fields.timeFunc,
|
timeFunc: tt.fields.timeFunc,
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ func Test_validator_verifyExpiresAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_validator_verifyIssuer(t *testing.T) {
|
func Test_Validator_verifyIssuer(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
expectedIss string
|
expectedIss string
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ func Test_validator_verifyIssuer(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := &validator{
|
v := &Validator{
|
||||||
expectedIss: tt.fields.expectedIss,
|
expectedIss: tt.fields.expectedIss,
|
||||||
}
|
}
|
||||||
err := v.verifyIssuer(tt.args.claims, tt.args.cmp, tt.args.required)
|
err := v.verifyIssuer(tt.args.claims, tt.args.cmp, tt.args.required)
|
||||||
|
@ -171,7 +171,7 @@ func Test_validator_verifyIssuer(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_validator_verifySubject(t *testing.T) {
|
func Test_Validator_verifySubject(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
expectedSub string
|
expectedSub string
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ func Test_validator_verifySubject(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := &validator{
|
v := &Validator{
|
||||||
expectedSub: tt.fields.expectedSub,
|
expectedSub: tt.fields.expectedSub,
|
||||||
}
|
}
|
||||||
err := v.verifySubject(tt.args.claims, tt.args.cmp, tt.args.required)
|
err := v.verifySubject(tt.args.claims, tt.args.cmp, tt.args.required)
|
||||||
|
@ -212,7 +212,7 @@ func Test_validator_verifySubject(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_validator_verifyIssuedAt(t *testing.T) {
|
func Test_Validator_verifyIssuedAt(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
leeway time.Duration
|
leeway time.Duration
|
||||||
timeFunc func() time.Time
|
timeFunc func() time.Time
|
||||||
|
@ -248,7 +248,7 @@ func Test_validator_verifyIssuedAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := &validator{
|
v := &Validator{
|
||||||
leeway: tt.fields.leeway,
|
leeway: tt.fields.leeway,
|
||||||
timeFunc: tt.fields.timeFunc,
|
timeFunc: tt.fields.timeFunc,
|
||||||
verifyIat: tt.fields.verifyIat,
|
verifyIat: tt.fields.verifyIat,
|
||||||
|
|
Loading…
Reference in New Issue