Skip to content

Commit a49fa5d

Browse files
authored
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.
1 parent c776b83 commit a49fa5d

File tree

5 files changed

+56
-35
lines changed

5 files changed

+56
-35
lines changed

MIGRATION_GUIDE.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ and corresponding updates for existing programs.
1717

1818
## Parsing and Validation Options
1919

20-
Under the hood, a new `validator` struct takes care of validating the claims. A
20+
Under the hood, a new `Validator` struct takes care of validating the claims. A
2121
long awaited feature has been the option to fine-tune the validation of tokens.
2222
This is now possible with several `ParserOption` functions that can be appended
2323
to most `Parse` functions, such as `ParseWithClaims`. The most important options
@@ -68,6 +68,16 @@ type Claims interface {
6868
}
6969
```
7070

71+
Users that previously directly called the `Valid` function on their claims,
72+
e.g., to perform validation independently of parsing/verifying a token, can now
73+
use the `jwt.NewValidator` function to create a `Validator` independently of the
74+
`Parser`.
75+
76+
```go
77+
var v = jwt.NewValidator(jwt.WithLeeway(5*time.Second))
78+
v.Validate(myClaims)
79+
```
80+
7181
### Supported Claim Types and Removal of `StandardClaims`
7282

7383
The two standard claim types supported by this library, `MapClaims` and

map_claims_test.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func TestVerifyAud(t *testing.T) {
6262
opts = append(opts, WithAudience(test.Comparison))
6363
}
6464

65-
validator := newValidator(opts...)
65+
validator := NewValidator(opts...)
6666
got := validator.Validate(test.MapClaims)
6767

6868
if (got == nil) != test.Expected {
@@ -77,7 +77,7 @@ func TestMapclaimsVerifyIssuedAtInvalidTypeString(t *testing.T) {
7777
"iat": "foo",
7878
}
7979
want := false
80-
got := newValidator(WithIssuedAt()).Validate(mapClaims)
80+
got := NewValidator(WithIssuedAt()).Validate(mapClaims)
8181
if want != (got == nil) {
8282
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
8383
}
@@ -88,7 +88,7 @@ func TestMapclaimsVerifyNotBeforeInvalidTypeString(t *testing.T) {
8888
"nbf": "foo",
8989
}
9090
want := false
91-
got := newValidator().Validate(mapClaims)
91+
got := NewValidator().Validate(mapClaims)
9292
if want != (got == nil) {
9393
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
9494
}
@@ -99,7 +99,7 @@ func TestMapclaimsVerifyExpiresAtInvalidTypeString(t *testing.T) {
9999
"exp": "foo",
100100
}
101101
want := false
102-
got := newValidator().Validate(mapClaims)
102+
got := NewValidator().Validate(mapClaims)
103103

104104
if want != (got == nil) {
105105
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
@@ -112,22 +112,22 @@ func TestMapClaimsVerifyExpiresAtExpire(t *testing.T) {
112112
"exp": float64(exp.Unix()),
113113
}
114114
want := false
115-
got := newValidator(WithTimeFunc(func() time.Time {
115+
got := NewValidator(WithTimeFunc(func() time.Time {
116116
return exp
117117
})).Validate(mapClaims)
118118
if want != (got == nil) {
119119
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
120120
}
121121

122-
got = newValidator(WithTimeFunc(func() time.Time {
122+
got = NewValidator(WithTimeFunc(func() time.Time {
123123
return exp.Add(1 * time.Second)
124124
})).Validate(mapClaims)
125125
if want != (got == nil) {
126126
t.Fatalf("Failed to verify claims, wanted: %v got %v", want, (got == nil))
127127
}
128128

129129
want = true
130-
got = newValidator(WithTimeFunc(func() time.Time {
130+
got = NewValidator(WithTimeFunc(func() time.Time {
131131
return exp.Add(-1 * time.Second)
132132
})).Validate(mapClaims)
133133
if want != (got == nil) {

parser.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type Parser struct {
1818
// Skip claims validation during token parsing.
1919
skipClaimsValidation bool
2020

21-
validator *validator
21+
validator *Validator
2222

2323
decodeStrict bool
2424

@@ -28,7 +28,7 @@ type Parser struct {
2828
// NewParser creates a new Parser with the specified options
2929
func NewParser(options ...ParserOption) *Parser {
3030
p := &Parser{
31-
validator: &validator{},
31+
validator: &Validator{},
3232
}
3333

3434
// Loop through our parsing options and apply them
@@ -115,7 +115,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
115115
if !p.skipClaimsValidation {
116116
// Make sure we have at least a default validator
117117
if p.validator == nil {
118-
p.validator = newValidator()
118+
p.validator = NewValidator()
119119
}
120120

121121
if err := p.validator.Validate(claims); err != nil {

validator.go

+25-14
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@ type ClaimsValidator interface {
2828
Validate() error
2929
}
3030

31-
// validator is the core of the new Validation API. It is automatically used by
31+
// Validator is the core of the new Validation API. It is automatically used by
3232
// a [Parser] during parsing and can be modified with various parser options.
3333
//
34-
// Note: This struct is intentionally not exported (yet) as we want to
35-
// internally finalize its API. In the future, we might make it publicly
36-
// available.
37-
type validator struct {
34+
// The [NewValidator] function should be used to create an instance of this
35+
// struct.
36+
type Validator struct {
3837
// leeway is an optional leeway that can be provided to account for clock skew.
3938
leeway time.Duration
4039

@@ -65,16 +64,28 @@ type validator struct {
6564
expectedSub string
6665
}
6766

68-
// newValidator can be used to create a stand-alone validator with the supplied
67+
// NewValidator can be used to create a stand-alone validator with the supplied
6968
// options. This validator can then be used to validate already parsed claims.
70-
func newValidator(opts ...ParserOption) *validator {
69+
//
70+
// Note: Under normal circumstances, explicitly creating a validator is not
71+
// needed and can potentially be dangerous; instead functions of the [Parser]
72+
// class should be used.
73+
//
74+
// The [Validator] is only checking the *validity* of the claims, such as its
75+
// expiration time, but it does NOT perform *signature verification* of the
76+
// token.
77+
func NewValidator(opts ...ParserOption) *Validator {
7178
p := NewParser(opts...)
7279
return p.validator
7380
}
7481

7582
// Validate validates the given claims. It will also perform any custom
7683
// validation if claims implements the [ClaimsValidator] interface.
77-
func (v *validator) Validate(claims Claims) error {
84+
//
85+
// Note: It will NOT perform any *signature verification* on the token that
86+
// contains the claims and expects that the [Claim] was already successfully
87+
// verified.
88+
func (v *Validator) Validate(claims Claims) error {
7889
var (
7990
now time.Time
8091
errs []error = make([]error, 0, 6)
@@ -153,7 +164,7 @@ func (v *validator) Validate(claims Claims) error {
153164
//
154165
// Additionally, if any error occurs while retrieving the claim, e.g., when its
155166
// the wrong type, an ErrTokenUnverifiable error will be returned.
156-
func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error {
167+
func (v *Validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error {
157168
exp, err := claims.GetExpirationTime()
158169
if err != nil {
159170
return err
@@ -174,7 +185,7 @@ func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool)
174185
//
175186
// Additionally, if any error occurs while retrieving the claim, e.g., when its
176187
// the wrong type, an ErrTokenUnverifiable error will be returned.
177-
func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error {
188+
func (v *Validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error {
178189
iat, err := claims.GetIssuedAt()
179190
if err != nil {
180191
return err
@@ -195,7 +206,7 @@ func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool)
195206
//
196207
// Additionally, if any error occurs while retrieving the claim, e.g., when its
197208
// the wrong type, an ErrTokenUnverifiable error will be returned.
198-
func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error {
209+
func (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error {
199210
nbf, err := claims.GetNotBefore()
200211
if err != nil {
201212
return err
@@ -215,7 +226,7 @@ func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool)
215226
//
216227
// Additionally, if any error occurs while retrieving the claim, e.g., when its
217228
// the wrong type, an ErrTokenUnverifiable error will be returned.
218-
func (v *validator) verifyAudience(claims Claims, cmp string, required bool) error {
229+
func (v *Validator) verifyAudience(claims Claims, cmp string, required bool) error {
219230
aud, err := claims.GetAudience()
220231
if err != nil {
221232
return err
@@ -251,7 +262,7 @@ func (v *validator) verifyAudience(claims Claims, cmp string, required bool) err
251262
//
252263
// Additionally, if any error occurs while retrieving the claim, e.g., when its
253264
// the wrong type, an ErrTokenUnverifiable error will be returned.
254-
func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error {
265+
func (v *Validator) verifyIssuer(claims Claims, cmp string, required bool) error {
255266
iss, err := claims.GetIssuer()
256267
if err != nil {
257268
return err
@@ -271,7 +282,7 @@ func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error
271282
//
272283
// Additionally, if any error occurs while retrieving the claim, e.g., when its
273284
// the wrong type, an ErrTokenUnverifiable error will be returned.
274-
func (v *validator) verifySubject(claims Claims, cmp string, required bool) error {
285+
func (v *Validator) verifySubject(claims Claims, cmp string, required bool) error {
275286
sub, err := claims.GetSubject()
276287
if err != nil {
277288
return err

validator_test.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func (m MyCustomClaims) Validate() error {
2020
return nil
2121
}
2222

23-
func Test_validator_Validate(t *testing.T) {
23+
func Test_Validator_Validate(t *testing.T) {
2424
type fields struct {
2525
leeway time.Duration
2626
timeFunc func() time.Time
@@ -71,7 +71,7 @@ func Test_validator_Validate(t *testing.T) {
7171
}
7272
for _, tt := range tests {
7373
t.Run(tt.name, func(t *testing.T) {
74-
v := &validator{
74+
v := &Validator{
7575
leeway: tt.fields.leeway,
7676
timeFunc: tt.fields.timeFunc,
7777
verifyIat: tt.fields.verifyIat,
@@ -86,7 +86,7 @@ func Test_validator_Validate(t *testing.T) {
8686
}
8787
}
8888

89-
func Test_validator_verifyExpiresAt(t *testing.T) {
89+
func Test_Validator_verifyExpiresAt(t *testing.T) {
9090
type fields struct {
9191
leeway time.Duration
9292
timeFunc func() time.Time
@@ -117,7 +117,7 @@ func Test_validator_verifyExpiresAt(t *testing.T) {
117117
}
118118
for _, tt := range tests {
119119
t.Run(tt.name, func(t *testing.T) {
120-
v := &validator{
120+
v := &Validator{
121121
leeway: tt.fields.leeway,
122122
timeFunc: tt.fields.timeFunc,
123123
}
@@ -130,7 +130,7 @@ func Test_validator_verifyExpiresAt(t *testing.T) {
130130
}
131131
}
132132

133-
func Test_validator_verifyIssuer(t *testing.T) {
133+
func Test_Validator_verifyIssuer(t *testing.T) {
134134
type fields struct {
135135
expectedIss string
136136
}
@@ -160,7 +160,7 @@ func Test_validator_verifyIssuer(t *testing.T) {
160160
}
161161
for _, tt := range tests {
162162
t.Run(tt.name, func(t *testing.T) {
163-
v := &validator{
163+
v := &Validator{
164164
expectedIss: tt.fields.expectedIss,
165165
}
166166
err := v.verifyIssuer(tt.args.claims, tt.args.cmp, tt.args.required)
@@ -171,7 +171,7 @@ func Test_validator_verifyIssuer(t *testing.T) {
171171
}
172172
}
173173

174-
func Test_validator_verifySubject(t *testing.T) {
174+
func Test_Validator_verifySubject(t *testing.T) {
175175
type fields struct {
176176
expectedSub string
177177
}
@@ -201,7 +201,7 @@ func Test_validator_verifySubject(t *testing.T) {
201201
}
202202
for _, tt := range tests {
203203
t.Run(tt.name, func(t *testing.T) {
204-
v := &validator{
204+
v := &Validator{
205205
expectedSub: tt.fields.expectedSub,
206206
}
207207
err := v.verifySubject(tt.args.claims, tt.args.cmp, tt.args.required)
@@ -212,7 +212,7 @@ func Test_validator_verifySubject(t *testing.T) {
212212
}
213213
}
214214

215-
func Test_validator_verifyIssuedAt(t *testing.T) {
215+
func Test_Validator_verifyIssuedAt(t *testing.T) {
216216
type fields struct {
217217
leeway time.Duration
218218
timeFunc func() time.Time
@@ -248,7 +248,7 @@ func Test_validator_verifyIssuedAt(t *testing.T) {
248248
}
249249
for _, tt := range tests {
250250
t.Run(tt.name, func(t *testing.T) {
251-
v := &validator{
251+
v := &Validator{
252252
leeway: tt.fields.leeway,
253253
timeFunc: tt.fields.timeFunc,
254254
verifyIat: tt.fields.verifyIat,

0 commit comments

Comments
 (0)