@@ -9,7 +9,10 @@ import (
99// Claims must just have a Valid method that determines
1010// if the token is invalid for any supported reason
1111type 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
1316}
1417
1518// RegisteredClaims are a structured version of the JWT Claims Set,
@@ -48,13 +51,13 @@ type RegisteredClaims struct {
4851// There is no accounting for clock skew.
4952// As well, if any of the above claims are not in the token, it will still
5053// be considered a valid claim.
51- func (c RegisteredClaims ) Valid () error {
54+ func (c RegisteredClaims ) Valid (opts ... validationOption ) error {
5255 vErr := new (ValidationError )
5356 now := TimeFunc ()
5457
5558 // The claims below are optional, by default, so if they are set to the
5659 // 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 ... ) {
5861 delta := now .Sub (c .ExpiresAt .Time )
5962 vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
6063 vErr .Errors |= ValidationErrorExpired
@@ -65,7 +68,7 @@ func (c RegisteredClaims) Valid() error {
6568 vErr .Errors |= ValidationErrorIssuedAt
6669 }
6770
68- if ! c .VerifyNotBefore (now , false ) {
71+ if ! c .VerifyNotBefore (now , false , opts ... ) {
6972 vErr .Inner = ErrTokenNotValidYet
7073 vErr .Errors |= ValidationErrorNotValidYet
7174 }
@@ -85,12 +88,16 @@ func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
8588
8689// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
8790// 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+ }
8996 if c .ExpiresAt == nil {
90- return verifyExp (nil , cmp , req )
97+ return verifyExp (nil , cmp , req , validator . leeway )
9198 }
9299
93- return verifyExp (& c .ExpiresAt .Time , cmp , req )
100+ return verifyExp (& c .ExpiresAt .Time , cmp , req , validator . leeway )
94101}
95102
96103// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@@ -105,12 +112,16 @@ func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
105112
106113// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
107114// 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+ }
109120 if c .NotBefore == nil {
110- return verifyNbf (nil , cmp , req )
121+ return verifyNbf (nil , cmp , req , validator . leeway )
111122 }
112123
113- return verifyNbf (& c .NotBefore .Time , cmp , req )
124+ return verifyNbf (& c .NotBefore .Time , cmp , req , validator . leeway )
114125}
115126
116127// VerifyIssuer compares the iss claim against cmp.
@@ -141,13 +152,13 @@ type StandardClaims struct {
141152// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
142153// As well, if any of the above claims are not in the token, it will still
143154// be considered a valid claim.
144- func (c StandardClaims ) Valid () error {
155+ func (c StandardClaims ) Valid (opts ... validationOption ) error {
145156 vErr := new (ValidationError )
146157 now := TimeFunc ().Unix ()
147158
148159 // The claims below are optional, by default, so if they are set to the
149160 // 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 ... ) {
151162 delta := time .Unix (now , 0 ).Sub (time .Unix (c .ExpiresAt , 0 ))
152163 vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
153164 vErr .Errors |= ValidationErrorExpired
@@ -158,7 +169,7 @@ func (c StandardClaims) Valid() error {
158169 vErr .Errors |= ValidationErrorIssuedAt
159170 }
160171
161- if ! c .VerifyNotBefore (now , false ) {
172+ if ! c .VerifyNotBefore (now , false , opts ... ) {
162173 vErr .Inner = ErrTokenNotValidYet
163174 vErr .Errors |= ValidationErrorNotValidYet
164175 }
@@ -178,13 +189,17 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
178189
179190// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
180191// 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+ }
182197 if c .ExpiresAt == 0 {
183- return verifyExp (nil , time .Unix (cmp , 0 ), req )
198+ return verifyExp (nil , time .Unix (cmp , 0 ), req , validator . leeway )
184199 }
185200
186201 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 )
188203}
189204
190205// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
@@ -200,13 +215,17 @@ func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
200215
201216// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
202217// 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+ }
204223 if c .NotBefore == 0 {
205- return verifyNbf (nil , time .Unix (cmp , 0 ), req )
224+ return verifyNbf (nil , time .Unix (cmp , 0 ), req , validator . leeway )
206225 }
207226
208227 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 )
210229}
211230
212231// VerifyIssuer compares the iss claim against cmp.
@@ -240,11 +259,11 @@ func verifyAud(aud []string, cmp string, required bool) bool {
240259 return result
241260}
242261
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 {
244263 if exp == nil {
245264 return ! required
246265 }
247- return now .Before (* exp )
266+ return now .Before (( * exp ). Add ( + skew ) )
248267}
249268
250269func 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 {
254273 return now .After (* iat ) || now .Equal (* iat )
255274}
256275
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 {
258277 if nbf == nil {
259278 return ! required
260279 }
261- return now .After (* nbf ) || now .Equal (* nbf )
280+ t := (* nbf ).Add (- skew )
281+ return now .After (t ) || now .Equal (t )
262282}
263283
264284func verifyIss (iss string , cmp string , required bool ) bool {
0 commit comments