1
1
package jwt
2
2
3
- import (
4
- "crypto/subtle"
5
- "fmt"
6
- "time"
7
- )
8
-
9
- // Claims must just have a Valid method that determines
10
- // if the token is invalid for any supported reason
3
+ // Claims represent any form of a JWT Claims Set according to
4
+ // https://datatracker.ietf.org/doc/html/rfc7519#section-4. In order to have a
5
+ // common basis for validation, it is required that an implementation is able to
6
+ // supply at least the claim names provided in
7
+ // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 namely `exp`,
8
+ // `iat`, `nbf`, `iss` and `aud`.
11
9
type Claims interface {
12
- Valid () error
10
+ GetExpiryAt () * NumericDate
11
+ GetIssuedAt () * NumericDate
12
+ GetNotBefore () * NumericDate
13
+ GetIssuer () string
14
+ GetAudience () ClaimStrings
13
15
}
14
16
15
17
// RegisteredClaims are a structured version of the JWT Claims Set,
16
18
// restricted to Registered Claim Names, as referenced at
17
19
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
18
20
//
19
21
// This type can be used on its own, but then additional private and
20
- // public claims embedded in the JWT will not be parsed. The typical usecase
22
+ // public claims embedded in the JWT will not be parsed. The typical use-case
21
23
// therefore is to embedded this in a user-defined claim type.
22
24
//
23
25
// See examples for how to use this with your own claim types.
@@ -44,230 +46,27 @@ type RegisteredClaims struct {
44
46
ID string `json:"jti,omitempty"`
45
47
}
46
48
47
- // Valid validates time based claims "exp, iat, nbf".
48
- // There is no accounting for clock skew.
49
- // As well, if any of the above claims are not in the token, it will still
50
- // be considered a valid claim.
51
- func (c RegisteredClaims ) Valid () error {
52
- vErr := new (ValidationError )
53
- now := TimeFunc ()
54
-
55
- // The claims below are optional, by default, so if they are set to the
56
- // default value in Go, let's not fail the verification for them.
57
- if ! c .VerifyExpiresAt (now , false ) {
58
- delta := now .Sub (c .ExpiresAt .Time )
59
- vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
60
- vErr .Errors |= ValidationErrorExpired
61
- }
62
-
63
- if ! c .VerifyIssuedAt (now , false ) {
64
- vErr .Inner = ErrTokenUsedBeforeIssued
65
- vErr .Errors |= ValidationErrorIssuedAt
66
- }
67
-
68
- if ! c .VerifyNotBefore (now , false ) {
69
- vErr .Inner = ErrTokenNotValidYet
70
- vErr .Errors |= ValidationErrorNotValidYet
71
- }
72
-
73
- if vErr .valid () {
74
- return nil
75
- }
76
-
77
- return vErr
78
- }
79
-
80
- // VerifyAudience compares the aud claim against cmp.
81
- // If required is false, this method will return true if the value matches or is unset
82
- func (c * RegisteredClaims ) VerifyAudience (cmp string , req bool ) bool {
83
- return verifyAud (c .Audience , cmp , req )
84
- }
85
-
86
- // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
87
- // If req is false, it will return true, if exp is unset.
88
- func (c * RegisteredClaims ) VerifyExpiresAt (cmp time.Time , req bool ) bool {
89
- if c .ExpiresAt == nil {
90
- return verifyExp (nil , cmp , req )
91
- }
92
-
93
- return verifyExp (& c .ExpiresAt .Time , cmp , req )
94
- }
95
-
96
- // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
97
- // If req is false, it will return true, if iat is unset.
98
- func (c * RegisteredClaims ) VerifyIssuedAt (cmp time.Time , req bool ) bool {
99
- if c .IssuedAt == nil {
100
- return verifyIat (nil , cmp , req )
101
- }
102
-
103
- return verifyIat (& c .IssuedAt .Time , cmp , req )
104
- }
105
-
106
- // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
107
- // If req is false, it will return true, if nbf is unset.
108
- func (c * RegisteredClaims ) VerifyNotBefore (cmp time.Time , req bool ) bool {
109
- if c .NotBefore == nil {
110
- return verifyNbf (nil , cmp , req )
111
- }
112
-
113
- return verifyNbf (& c .NotBefore .Time , cmp , req )
114
- }
115
-
116
- // VerifyIssuer compares the iss claim against cmp.
117
- // If required is false, this method will return true if the value matches or is unset
118
- func (c * RegisteredClaims ) VerifyIssuer (cmp string , req bool ) bool {
119
- return verifyIss (c .Issuer , cmp , req )
120
- }
121
-
122
- // StandardClaims are a structured version of the JWT Claims Set, as referenced at
123
- // https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the
124
- // specification exactly, since they were based on an earlier draft of the
125
- // specification and not updated. The main difference is that they only
126
- // support integer-based date fields and singular audiences. This might lead to
127
- // incompatibilities with other JWT implementations. The use of this is discouraged, instead
128
- // the newer RegisteredClaims struct should be used.
129
- //
130
- // Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
131
- type StandardClaims struct {
132
- Audience string `json:"aud,omitempty"`
133
- ExpiresAt int64 `json:"exp,omitempty"`
134
- Id string `json:"jti,omitempty"`
135
- IssuedAt int64 `json:"iat,omitempty"`
136
- Issuer string `json:"iss,omitempty"`
137
- NotBefore int64 `json:"nbf,omitempty"`
138
- Subject string `json:"sub,omitempty"`
139
- }
140
-
141
- // Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
142
- // As well, if any of the above claims are not in the token, it will still
143
- // be considered a valid claim.
144
- func (c StandardClaims ) Valid () error {
145
- vErr := new (ValidationError )
146
- now := TimeFunc ().Unix ()
147
-
148
- // The claims below are optional, by default, so if they are set to the
149
- // default value in Go, let's not fail the verification for them.
150
- if ! c .VerifyExpiresAt (now , false ) {
151
- delta := time .Unix (now , 0 ).Sub (time .Unix (c .ExpiresAt , 0 ))
152
- vErr .Inner = fmt .Errorf ("%s by %s" , ErrTokenExpired , delta )
153
- vErr .Errors |= ValidationErrorExpired
154
- }
155
-
156
- if ! c .VerifyIssuedAt (now , false ) {
157
- vErr .Inner = ErrTokenUsedBeforeIssued
158
- vErr .Errors |= ValidationErrorIssuedAt
159
- }
160
-
161
- if ! c .VerifyNotBefore (now , false ) {
162
- vErr .Inner = ErrTokenNotValidYet
163
- vErr .Errors |= ValidationErrorNotValidYet
164
- }
165
-
166
- if vErr .valid () {
167
- return nil
168
- }
169
-
170
- return vErr
171
- }
172
-
173
- // VerifyAudience compares the aud claim against cmp.
174
- // If required is false, this method will return true if the value matches or is unset
175
- func (c * StandardClaims ) VerifyAudience (cmp string , req bool ) bool {
176
- return verifyAud ([]string {c .Audience }, cmp , req )
177
- }
178
-
179
- // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
180
- // If req is false, it will return true, if exp is unset.
181
- func (c * StandardClaims ) VerifyExpiresAt (cmp int64 , req bool ) bool {
182
- if c .ExpiresAt == 0 {
183
- return verifyExp (nil , time .Unix (cmp , 0 ), req )
184
- }
185
-
186
- t := time .Unix (c .ExpiresAt , 0 )
187
- return verifyExp (& t , time .Unix (cmp , 0 ), req )
188
- }
189
-
190
- // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
191
- // If req is false, it will return true, if iat is unset.
192
- func (c * StandardClaims ) VerifyIssuedAt (cmp int64 , req bool ) bool {
193
- if c .IssuedAt == 0 {
194
- return verifyIat (nil , time .Unix (cmp , 0 ), req )
195
- }
196
-
197
- t := time .Unix (c .IssuedAt , 0 )
198
- return verifyIat (& t , time .Unix (cmp , 0 ), req )
199
- }
200
-
201
- // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
202
- // If req is false, it will return true, if nbf is unset.
203
- func (c * StandardClaims ) VerifyNotBefore (cmp int64 , req bool ) bool {
204
- if c .NotBefore == 0 {
205
- return verifyNbf (nil , time .Unix (cmp , 0 ), req )
206
- }
207
-
208
- t := time .Unix (c .NotBefore , 0 )
209
- return verifyNbf (& t , time .Unix (cmp , 0 ), req )
210
- }
211
-
212
- // VerifyIssuer compares the iss claim against cmp.
213
- // If required is false, this method will return true if the value matches or is unset
214
- func (c * StandardClaims ) VerifyIssuer (cmp string , req bool ) bool {
215
- return verifyIss (c .Issuer , cmp , req )
216
- }
217
-
218
- // ----- helpers
219
-
220
- func verifyAud (aud []string , cmp string , required bool ) bool {
221
- if len (aud ) == 0 {
222
- return ! required
223
- }
224
- // use a var here to keep constant time compare when looping over a number of claims
225
- result := false
226
-
227
- var stringClaims string
228
- for _ , a := range aud {
229
- if subtle .ConstantTimeCompare ([]byte (a ), []byte (cmp )) != 0 {
230
- result = true
231
- }
232
- stringClaims = stringClaims + a
233
- }
234
-
235
- // case where "" is sent in one or many aud claims
236
- if len (stringClaims ) == 0 {
237
- return ! required
238
- }
239
-
240
- return result
49
+ // GetExpiryAt implements the Claims interface.
50
+ func (c RegisteredClaims ) GetExpiryAt () * NumericDate {
51
+ return c .ExpiresAt
241
52
}
242
53
243
- func verifyExp (exp * time.Time , now time.Time , required bool ) bool {
244
- if exp == nil {
245
- return ! required
246
- }
247
- return now .Before (* exp )
54
+ // GetNotBefore implements the Claims interface.
55
+ func (c RegisteredClaims ) GetNotBefore () * NumericDate {
56
+ return c .NotBefore
248
57
}
249
58
250
- func verifyIat (iat * time.Time , now time.Time , required bool ) bool {
251
- if iat == nil {
252
- return ! required
253
- }
254
- return now .After (* iat ) || now .Equal (* iat )
59
+ // GetIssuedAt implements the Claims interface.
60
+ func (c RegisteredClaims ) GetIssuedAt () * NumericDate {
61
+ return c .IssuedAt
255
62
}
256
63
257
- func verifyNbf (nbf * time.Time , now time.Time , required bool ) bool {
258
- if nbf == nil {
259
- return ! required
260
- }
261
- return now .After (* nbf ) || now .Equal (* nbf )
64
+ // GetAudience implements the Claims interface.
65
+ func (c RegisteredClaims ) GetAudience () ClaimStrings {
66
+ return c .Audience
262
67
}
263
68
264
- func verifyIss (iss string , cmp string , required bool ) bool {
265
- if iss == "" {
266
- return ! required
267
- }
268
- if subtle .ConstantTimeCompare ([]byte (iss ), []byte (cmp )) != 0 {
269
- return true
270
- } else {
271
- return false
272
- }
69
+ // GetIssuer implements the Claims interface.
70
+ func (c RegisteredClaims ) GetIssuer () string {
71
+ return c .Issuer
273
72
}
0 commit comments