@@ -9,7 +9,10 @@ import (
9
9
// Claims must just have a Valid method that determines
10
10
// if the token is invalid for any supported reason
11
11
type Claims interface {
12
- Valid () error
12
+ // Valid implements claim validation. The opts are function style options that can
13
+ // be used to fine-tune the validation. The type used for the options is intentionally
14
+ // un-exported, since its API and its naming is subject to change.
15
+ Valid (opts ... validationOption ) error
13
16
}
14
17
15
18
// RegisteredClaims are a structured version of the JWT Claims Set,
@@ -48,13 +51,13 @@ type RegisteredClaims struct {
48
51
// There is no accounting for clock skew.
49
52
// As well, if any of the above claims are not in the token, it will still
50
53
// be considered a valid claim.
51
- func (c RegisteredClaims ) Valid () error {
54
+ func (c RegisteredClaims ) Valid (opts ... validationOption ) error {
52
55
vErr := new (ValidationError )
53
56
now := TimeFunc ()
54
57
55
58
// The claims below are optional, by default, so if they are set to the
56
59
// default value in Go, let's not fail the verification for them.
57
- if ! c .VerifyExpiresAt (now , false ) {
60
+ if ! c .VerifyExpiresAt (now , false , opts ... ) {
58
61
delta := now .Sub (c .ExpiresAt .Time )
59
62
vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
60
63
vErr .Errors |= ValidationErrorExpired
@@ -65,7 +68,7 @@ func (c RegisteredClaims) Valid() error {
65
68
vErr .Errors |= ValidationErrorIssuedAt
66
69
}
67
70
68
- if ! c .VerifyNotBefore (now , false ) {
71
+ if ! c .VerifyNotBefore (now , false , opts ... ) {
69
72
vErr .Inner = ErrTokenNotValidYet
70
73
vErr .Errors |= ValidationErrorNotValidYet
71
74
}
@@ -85,12 +88,16 @@ func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
85
88
86
89
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
87
90
// If req is false, it will return true, if exp is unset.
88
- func (c * RegisteredClaims ) VerifyExpiresAt (cmp time.Time , req bool ) bool {
91
+ func (c * RegisteredClaims ) VerifyExpiresAt (cmp time.Time , req bool , opts ... validationOption ) bool {
92
+ validator := validator {}
93
+ for _ , o := range opts {
94
+ o (& validator )
95
+ }
89
96
if c .ExpiresAt == nil {
90
- return verifyExp (nil , cmp , req )
97
+ return verifyExp (nil , cmp , req , validator . leeway )
91
98
}
92
99
93
- return verifyExp (& c .ExpiresAt .Time , cmp , req )
100
+ return verifyExp (& c .ExpiresAt .Time , cmp , req , validator . leeway )
94
101
}
95
102
96
103
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@@ -105,12 +112,16 @@ func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
105
112
106
113
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
107
114
// If req is false, it will return true, if nbf is unset.
108
- func (c * RegisteredClaims ) VerifyNotBefore (cmp time.Time , req bool ) bool {
115
+ func (c * RegisteredClaims ) VerifyNotBefore (cmp time.Time , req bool , opts ... validationOption ) bool {
116
+ validator := validator {}
117
+ for _ , o := range opts {
118
+ o (& validator )
119
+ }
109
120
if c .NotBefore == nil {
110
- return verifyNbf (nil , cmp , req )
121
+ return verifyNbf (nil , cmp , req , validator . leeway )
111
122
}
112
123
113
- return verifyNbf (& c .NotBefore .Time , cmp , req )
124
+ return verifyNbf (& c .NotBefore .Time , cmp , req , validator . leeway )
114
125
}
115
126
116
127
// VerifyIssuer compares the iss claim against cmp.
@@ -141,13 +152,13 @@ type StandardClaims struct {
141
152
// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
142
153
// As well, if any of the above claims are not in the token, it will still
143
154
// be considered a valid claim.
144
- func (c StandardClaims ) Valid () error {
155
+ func (c StandardClaims ) Valid (opts ... validationOption ) error {
145
156
vErr := new (ValidationError )
146
157
now := TimeFunc ().Unix ()
147
158
148
159
// The claims below are optional, by default, so if they are set to the
149
160
// default value in Go, let's not fail the verification for them.
150
- if ! c .VerifyExpiresAt (now , false ) {
161
+ if ! c .VerifyExpiresAt (now , false , opts ... ) {
151
162
delta := time .Unix (now , 0 ).Sub (time .Unix (c .ExpiresAt , 0 ))
152
163
vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
153
164
vErr .Errors |= ValidationErrorExpired
@@ -158,7 +169,7 @@ func (c StandardClaims) Valid() error {
158
169
vErr .Errors |= ValidationErrorIssuedAt
159
170
}
160
171
161
- if ! c .VerifyNotBefore (now , false ) {
172
+ if ! c .VerifyNotBefore (now , false , opts ... ) {
162
173
vErr .Inner = ErrTokenNotValidYet
163
174
vErr .Errors |= ValidationErrorNotValidYet
164
175
}
@@ -178,13 +189,17 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
178
189
179
190
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
180
191
// If req is false, it will return true, if exp is unset.
181
- func (c * StandardClaims ) VerifyExpiresAt (cmp int64 , req bool ) bool {
192
+ func (c * StandardClaims ) VerifyExpiresAt (cmp int64 , req bool , opts ... validationOption ) bool {
193
+ validator := validator {}
194
+ for _ , o := range opts {
195
+ o (& validator )
196
+ }
182
197
if c .ExpiresAt == 0 {
183
- return verifyExp (nil , time .Unix (cmp , 0 ), req )
198
+ return verifyExp (nil , time .Unix (cmp , 0 ), req , validator . leeway )
184
199
}
185
200
186
201
t := time .Unix (c .ExpiresAt , 0 )
187
- return verifyExp (& t , time .Unix (cmp , 0 ), req )
202
+ return verifyExp (& t , time .Unix (cmp , 0 ), req , validator . leeway )
188
203
}
189
204
190
205
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@@ -200,13 +215,17 @@ func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
200
215
201
216
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
202
217
// If req is false, it will return true, if nbf is unset.
203
- func (c * StandardClaims ) VerifyNotBefore (cmp int64 , req bool ) bool {
218
+ func (c * StandardClaims ) VerifyNotBefore (cmp int64 , req bool , opts ... validationOption ) bool {
219
+ validator := validator {}
220
+ for _ , o := range opts {
221
+ o (& validator )
222
+ }
204
223
if c .NotBefore == 0 {
205
- return verifyNbf (nil , time .Unix (cmp , 0 ), req )
224
+ return verifyNbf (nil , time .Unix (cmp , 0 ), req , validator . leeway )
206
225
}
207
226
208
227
t := time .Unix (c .NotBefore , 0 )
209
- return verifyNbf (& t , time .Unix (cmp , 0 ), req )
228
+ return verifyNbf (& t , time .Unix (cmp , 0 ), req , validator . leeway )
210
229
}
211
230
212
231
// VerifyIssuer compares the iss claim against cmp.
@@ -240,11 +259,11 @@ func verifyAud(aud []string, cmp string, required bool) bool {
240
259
return result
241
260
}
242
261
243
- func verifyExp (exp * time.Time , now time.Time , required bool ) bool {
262
+ func verifyExp (exp * time.Time , now time.Time , required bool , skew time. Duration ) bool {
244
263
if exp == nil {
245
264
return ! required
246
265
}
247
- return now .Before (* exp )
266
+ return now .Before (( * exp ). Add ( + skew ) )
248
267
}
249
268
250
269
func verifyIat (iat * time.Time , now time.Time , required bool ) bool {
@@ -254,11 +273,12 @@ func verifyIat(iat *time.Time, now time.Time, required bool) bool {
254
273
return now .After (* iat ) || now .Equal (* iat )
255
274
}
256
275
257
- func verifyNbf (nbf * time.Time , now time.Time , required bool ) bool {
276
+ func verifyNbf (nbf * time.Time , now time.Time , required bool , skew time. Duration ) bool {
258
277
if nbf == nil {
259
278
return ! required
260
279
}
261
- return now .After (* nbf ) || now .Equal (* nbf )
280
+ t := (* nbf ).Add (- skew )
281
+ return now .After (t ) || now .Equal (t )
262
282
}
263
283
264
284
func verifyIss (iss string , cmp string , required bool ) bool {
0 commit comments