jwt 2.9.3 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -47
- data/CODE_OF_CONDUCT.md +14 -14
- data/CONTRIBUTING.md +9 -10
- data/README.md +273 -234
- data/UPGRADING.md +47 -0
- data/lib/jwt/base64.rb +4 -10
- data/lib/jwt/claims/audience.rb +10 -0
- data/lib/jwt/claims/crit.rb +35 -0
- data/lib/jwt/claims/decode_verifier.rb +3 -3
- data/lib/jwt/claims/expiration.rb +10 -0
- data/lib/jwt/claims/issued_at.rb +7 -0
- data/lib/jwt/claims/issuer.rb +10 -0
- data/lib/jwt/claims/jwt_id.rb +10 -0
- data/lib/jwt/claims/not_before.rb +10 -0
- data/lib/jwt/claims/numeric.rb +9 -19
- data/lib/jwt/claims/required.rb +10 -0
- data/lib/jwt/claims/subject.rb +10 -0
- data/lib/jwt/claims/verifier.rb +6 -7
- data/lib/jwt/claims.rb +4 -19
- data/lib/jwt/configuration/container.rb +20 -1
- data/lib/jwt/configuration/decode_configuration.rb +24 -0
- data/lib/jwt/configuration/jwk_configuration.rb +1 -0
- data/lib/jwt/configuration.rb +8 -0
- data/lib/jwt/decode.rb +42 -79
- data/lib/jwt/encode.rb +17 -56
- data/lib/jwt/encoded_token.rb +236 -0
- data/lib/jwt/error.rb +32 -1
- data/lib/jwt/json.rb +1 -1
- data/lib/jwt/jwa/ecdsa.rb +31 -13
- data/lib/jwt/jwa/hmac.rb +2 -7
- data/lib/jwt/jwa/none.rb +1 -0
- data/lib/jwt/jwa/ps.rb +3 -3
- data/lib/jwt/jwa/rsa.rb +6 -6
- data/lib/jwt/jwa/signing_algorithm.rb +3 -1
- data/lib/jwt/jwa/unsupported.rb +1 -0
- data/lib/jwt/jwa.rb +77 -24
- data/lib/jwt/jwk/ec.rb +54 -65
- data/lib/jwt/jwk/hmac.rb +5 -6
- data/lib/jwt/jwk/key_base.rb +16 -1
- data/lib/jwt/jwk/key_finder.rb +35 -8
- data/lib/jwt/jwk/kid_as_key_digest.rb +1 -0
- data/lib/jwt/jwk/rsa.rb +7 -4
- data/lib/jwt/jwk/set.rb +2 -0
- data/lib/jwt/jwk.rb +1 -1
- data/lib/jwt/token.rb +131 -0
- data/lib/jwt/version.rb +24 -19
- data/lib/jwt.rb +17 -7
- data/ruby-jwt.gemspec +2 -0
- metadata +36 -16
- data/lib/jwt/claims_validator.rb +0 -16
- data/lib/jwt/deprecations.rb +0 -48
- data/lib/jwt/jwa/compat.rb +0 -29
- data/lib/jwt/jwa/eddsa.rb +0 -34
- data/lib/jwt/jwa/hmac_rbnacl.rb +0 -49
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +0 -46
- data/lib/jwt/jwa/wrapper.rb +0 -43
- data/lib/jwt/jwk/okp_rbnacl.rb +0 -110
- data/lib/jwt/verify.rb +0 -34
data/README.md
CHANGED
@@ -1,224 +1,157 @@
|
|
1
1
|
# JWT
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/jwt)
|
4
|
-
[](https://github.com/jwt/ruby-jwt/actions)
|
5
|
-
[](https://codeclimate.com/github/jwt/ruby-jwt)
|
4
|
+
[](https://github.com/jwt/ruby-jwt/actions)
|
5
|
+
[](https://qlty.sh/gh/jwt/projects/ruby-jwt)
|
6
|
+
[](https://qlty.sh/gh/jwt/projects/ruby-jwt)
|
8
7
|
|
9
8
|
A ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
|
10
9
|
|
11
10
|
If you have further questions related to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
|
12
11
|
|
13
|
-
|
14
|
-
* Ruby 2.4 support was dropped in version 2.4.0
|
15
|
-
* Ruby 1.9.3 support was dropped at December 31st, 2016.
|
16
|
-
* Version 1.5.3 yanked. See: [#132](https://github.com/jwt/ruby-jwt/issues/132) and [#133](https://github.com/jwt/ruby-jwt/issues/133)
|
17
|
-
|
18
|
-
See [CHANGELOG.md](CHANGELOG.md) for a complete set of changes.
|
12
|
+
See [CHANGELOG.md](CHANGELOG.md) for a complete set of changes and [upgrade guide](UPGRADING.md) for upgrading between major versions.
|
19
13
|
|
20
14
|
## Sponsors
|
21
15
|
|
22
|
-
|Logo|Message|
|
23
|
-
|
24
|
-
|
16
|
+
| Logo | Message |
|
17
|
+
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
18
|
+
|  | If you want to quickly add secure token-based authentication to Ruby projects, feel free to check Auth0's Ruby SDK and free plan at [auth0.com/developers](https://auth0.com/developers?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=rubyjwt&utm_content=auth) |
|
25
19
|
|
26
20
|
## Installing
|
27
21
|
|
28
|
-
### Using Rubygems
|
22
|
+
### Using Rubygems
|
23
|
+
|
29
24
|
```bash
|
30
25
|
gem install jwt
|
31
26
|
```
|
32
27
|
|
33
|
-
### Using Bundler
|
28
|
+
### Using Bundler
|
29
|
+
|
34
30
|
Add the following to your Gemfile
|
35
|
-
|
31
|
+
|
32
|
+
```bash
|
36
33
|
gem 'jwt'
|
37
34
|
```
|
38
|
-
And run `bundle install`
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). **It is strongly recommended that you hard code the algorithm, as you may leave yourself vulnerable by dynamically picking the algorithm**
|
43
|
-
|
44
|
-
See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
|
45
|
-
|
46
|
-
### Deprecation warnings
|
36
|
+
And run `bundle install`
|
47
37
|
|
48
|
-
|
38
|
+
Finally require the gem in your application
|
49
39
|
|
50
40
|
```ruby
|
51
|
-
|
41
|
+
require 'jwt'
|
52
42
|
```
|
53
43
|
|
54
|
-
|
44
|
+
## Algorithms and Usage
|
45
|
+
|
46
|
+
The jwt gem natively supports the NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms via the openssl library. The gem can be extended with additional or alternative implementations of the algorithms via extensions.
|
55
47
|
|
56
|
-
|
48
|
+
Additionally the EdDSA algorithm is supported via a the [jwt-eddsa gem](https://rubygems.org/gems/jwt-eddsa).
|
57
49
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
```
|
50
|
+
For safe cryptographic signing, you need to specify the algorithm in the options hash whenever you call `JWT.decode` to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). **It is strongly recommended that you hard code the algorithm, as you may leave yourself vulnerable by dynamically picking the algorithm**
|
51
|
+
|
52
|
+
See [JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
|
62
53
|
|
63
54
|
### **NONE**
|
64
55
|
|
65
|
-
|
56
|
+
- none - unsigned token
|
66
57
|
|
67
58
|
```ruby
|
68
|
-
require 'jwt'
|
69
|
-
|
70
59
|
payload = { data: 'test' }
|
60
|
+
token = JWT.encode(payload, nil, 'none')
|
61
|
+
# => "eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9."
|
71
62
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
# Set password to nil and validation to false otherwise this won't work
|
79
|
-
decoded_token = JWT.decode token, nil, false
|
80
|
-
|
81
|
-
# Array
|
82
|
-
# [
|
83
|
-
# {"data"=>"test"}, # payload
|
84
|
-
# {"alg"=>"none"} # header
|
85
|
-
# ]
|
86
|
-
puts decoded_token
|
63
|
+
decoded_token = JWT.decode(token, nil, true, { algorithm: 'none' })
|
64
|
+
# => [
|
65
|
+
# {"data"=>"test"}, # payload
|
66
|
+
# {"alg"=>"none"} # header
|
67
|
+
# ]
|
87
68
|
```
|
88
69
|
|
89
70
|
### **HMAC**
|
90
71
|
|
91
|
-
|
92
|
-
|
93
|
-
|
72
|
+
- HS256 - HMAC using SHA-256 hash algorithm
|
73
|
+
- HS384 - HMAC using SHA-384 hash algorithm
|
74
|
+
- HS512 - HMAC using SHA-512 hash algorithm
|
94
75
|
|
95
76
|
```ruby
|
96
|
-
|
77
|
+
payload = { data: 'test' }
|
97
78
|
hmac_secret = 'my$ecretK3y'
|
98
79
|
|
99
|
-
token = JWT.encode
|
100
|
-
|
101
|
-
# eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
|
102
|
-
puts token
|
103
|
-
|
104
|
-
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
|
80
|
+
token = JWT.encode(payload, hmac_secret, 'HS256')
|
81
|
+
# => "eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y"
|
105
82
|
|
106
|
-
|
107
|
-
# [
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
puts decoded_token
|
83
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { algorithm: 'HS256' })
|
84
|
+
# => [
|
85
|
+
# {"data"=>"test"}, # payload
|
86
|
+
# {"alg"=>"HS256"} # header
|
87
|
+
# ]
|
112
88
|
```
|
113
89
|
|
114
90
|
### **RSA**
|
115
91
|
|
116
|
-
|
117
|
-
|
118
|
-
|
92
|
+
- RS256 - RSA using SHA-256 hash algorithm
|
93
|
+
- RS384 - RSA using SHA-384 hash algorithm
|
94
|
+
- RS512 - RSA using SHA-512 hash algorithm
|
119
95
|
|
120
96
|
```ruby
|
121
|
-
|
122
|
-
|
97
|
+
payload = { data: 'test' }
|
98
|
+
rsa_private = OpenSSL::PKey::RSA.generate(2048)
|
99
|
+
rsa_public = rsa_private.public_key
|
123
100
|
|
124
|
-
token = JWT.encode
|
101
|
+
token = JWT.encode(payload, rsa_private, 'RS256')
|
102
|
+
# => "eyJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.CCkO35qFPijW8Gwhbt8a80PB9fc9FJ19hCMnXSgoDF6Mlvlt0A4G-ah..."
|
125
103
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
# Array
|
132
|
-
# [
|
133
|
-
# {"data"=>"test"}, # payload
|
134
|
-
# {"alg"=>"RS256"} # header
|
135
|
-
# ]
|
136
|
-
puts decoded_token
|
104
|
+
decoded_token = JWT.decode(token, rsa_public, true, { algorithm: 'RS256' })
|
105
|
+
# => [
|
106
|
+
# {"data"=>"test"}, # payload
|
107
|
+
# {"alg"=>"RS256"} # header
|
108
|
+
# ]
|
137
109
|
```
|
138
110
|
|
139
111
|
### **ECDSA**
|
140
112
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
113
|
+
- ES256 - ECDSA using P-256 and SHA-256
|
114
|
+
- ES384 - ECDSA using P-384 and SHA-384
|
115
|
+
- ES512 - ECDSA using P-521 and SHA-512
|
116
|
+
- ES256K - ECDSA using P-256K and SHA-256
|
145
117
|
|
146
118
|
```ruby
|
119
|
+
payload = { data: 'test' }
|
147
120
|
ecdsa_key = OpenSSL::PKey::EC.generate('prime256v1')
|
148
121
|
|
149
|
-
token = JWT.encode
|
150
|
-
|
151
|
-
# eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
|
152
|
-
puts token
|
122
|
+
token = JWT.encode(payload, ecdsa_key, 'ES256')
|
123
|
+
# => "eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg"
|
153
124
|
|
154
|
-
decoded_token = JWT.decode
|
155
|
-
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# {"alg"=>"ES256"} # header
|
160
|
-
# ]
|
161
|
-
puts decoded_token
|
125
|
+
decoded_token = JWT.decode(token, ecdsa_key, true, { algorithm: 'ES256' })
|
126
|
+
# => [
|
127
|
+
# {"test"=>"data"}, # payload
|
128
|
+
# {"alg"=>"ES256"} # header
|
129
|
+
# ]
|
162
130
|
```
|
163
131
|
|
164
|
-
### **
|
165
|
-
|
166
|
-
In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`.
|
167
|
-
|
168
|
-
```ruby
|
169
|
-
gem 'rbnacl'
|
170
|
-
```
|
171
|
-
|
172
|
-
For more detailed installation instruction check the official [repository](https://github.com/RubyCrypto/rbnacl) on GitHub.
|
173
|
-
|
174
|
-
* ED25519
|
132
|
+
### **EdDSA**
|
175
133
|
|
176
|
-
|
177
|
-
private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
|
178
|
-
public_key = private_key.verify_key
|
179
|
-
token = JWT.encode payload, private_key, 'ED25519'
|
180
|
-
|
181
|
-
# eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
|
182
|
-
puts token
|
183
|
-
|
184
|
-
decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
|
185
|
-
# Array
|
186
|
-
# [
|
187
|
-
# {"test"=>"data"}, # payload
|
188
|
-
# {"alg"=>"ED25519"} # header
|
189
|
-
# ]
|
190
|
-
|
191
|
-
```
|
134
|
+
Since version 3.0, the EdDSA algorithm has been moved to the [jwt-eddsa gem](https://rubygems.org/gems/jwt-eddsa).
|
192
135
|
|
193
136
|
### **RSASSA-PSS**
|
194
137
|
|
195
|
-
|
138
|
+
- PS256 - RSASSA-PSS using SHA-256 hash algorithm
|
139
|
+
- PS384 - RSASSA-PSS using SHA-384 hash algorithm
|
140
|
+
- PS512 - RSASSA-PSS using SHA-512 hash algorithm
|
196
141
|
|
197
142
|
```ruby
|
198
|
-
|
199
|
-
|
143
|
+
payload = { data: 'test' }
|
144
|
+
rsa_private = OpenSSL::PKey::RSA.generate(2048)
|
145
|
+
rsa_public = rsa_private.public_key
|
200
146
|
|
201
|
-
|
202
|
-
|
203
|
-
* PS512 - RSASSA-PSS using SHA-512 hash algorithm
|
147
|
+
token = JWT.encode(payload, rsa_private, 'PS256')
|
148
|
+
# => "eyJhbGciOiJQUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.BRWizdUjD5zAWw-EDBcrl3dDpQDAePz9Ol3XKC43SggU47G8OWwveA_..."
|
204
149
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
# eyJhbGciOiJQUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.KEmqagMUHM-NcmXo6818ZazVTIAkn9qU9KQFT1c5Iq91n0KRpAI84jj4ZCdkysDlWokFs3Dmn4MhcXP03oJKLFgnoPL40_Wgg9iFr0jnIVvnMUp1kp2RFUbL0jqExGTRA3LdAhuvw6ZByGD1bkcWjDXygjQw-hxILrT1bENjdr0JhFd-cB0-ps5SB0mwhFNcUw-OM3Uu30B1-mlFaelUY8jHJYKwLTZPNxHzndt8RGXF8iZLp7dGb06HSCKMcVzhASGMH4ZdFystRe2hh31cwcvnl-Eo_D4cdwmpN3Abhk_8rkxawQJR3duh8HNKc4AyFPo7SabEaSu2gLnLfN3yfg
|
212
|
-
puts token
|
213
|
-
|
214
|
-
decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'PS256' }
|
215
|
-
|
216
|
-
# Array
|
217
|
-
# [
|
218
|
-
# {"data"=>"test"}, # payload
|
219
|
-
# {"alg"=>"PS256"} # header
|
220
|
-
# ]
|
221
|
-
puts decoded_token
|
150
|
+
decoded_token = JWT.decode(token, rsa_public, true, { algorithm: 'PS256' })
|
151
|
+
# => [
|
152
|
+
# {"data"=>"test"}, # payload
|
153
|
+
# {"alg"=>"PS256"} # header
|
154
|
+
# ]
|
222
155
|
```
|
223
156
|
|
224
157
|
### **Custom algorithms**
|
@@ -230,7 +163,6 @@ When encoding or decoding a token, you can pass in a custom object through the `
|
|
230
163
|
|
231
164
|
For customization options check the details from `JWT::JWA::SigningAlgorithm`.
|
232
165
|
|
233
|
-
|
234
166
|
```ruby
|
235
167
|
module CustomHS512Algorithm
|
236
168
|
extend JWT::JWA::SigningAlgorithm
|
@@ -248,82 +180,191 @@ module CustomHS512Algorithm
|
|
248
180
|
end
|
249
181
|
end
|
250
182
|
|
251
|
-
|
252
|
-
|
253
|
-
|
183
|
+
payload = { data: 'test' }
|
184
|
+
token = JWT.encode(payload, 'secret', CustomHS512Algorithm)
|
185
|
+
# => "eyJhbGciOiJIUzUxMiJ9.eyJkYXRhIjoidGVzdCJ9.aBNoejLEM2WMF3TxzRDKlehYdG2ATvFpGNauTI4GSD2VJseS_sC8covrVMlgslf0aJM4SKb3EIeORJBFPtZ33w"
|
254
186
|
|
255
|
-
|
256
|
-
|
257
|
-
|
187
|
+
decoded_token = JWT.decode(token, 'secret', true, algorithm: CustomHS512Algorithm)
|
188
|
+
# => [
|
189
|
+
# {"data"=>"test"}, # payload
|
190
|
+
# {"alg"=>"HS512"} # header
|
191
|
+
# ]
|
192
|
+
```
|
258
193
|
|
259
|
-
|
260
|
-
- 'nbf' (Not Before Time) Claim
|
261
|
-
- 'iss' (Issuer) Claim
|
262
|
-
- 'aud' (Audience) Claim
|
263
|
-
- 'jti' (JWT ID) Claim
|
264
|
-
- 'iat' (Issued At) Claim
|
265
|
-
- 'sub' (Subject) Claim
|
194
|
+
### Add custom header fields
|
266
195
|
|
267
|
-
|
268
|
-
Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
|
196
|
+
The ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
|
269
197
|
To add custom header fields you need to pass `header_fields` parameter
|
270
198
|
|
271
199
|
```ruby
|
272
|
-
|
200
|
+
payload = { data: 'test' }
|
201
|
+
|
202
|
+
token = JWT.encode(payload, nil, 'none', { typ: 'JWT' })
|
203
|
+
# => "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9."
|
204
|
+
|
205
|
+
decoded_token = JWT.decode(token, nil, true, { algorithm: 'none' })
|
206
|
+
# => [
|
207
|
+
# {"data"=>"test"}, # payload
|
208
|
+
# {"typ"=>"JWT", "alg"=>"none"} # header
|
209
|
+
# ]
|
210
|
+
```
|
211
|
+
|
212
|
+
## `JWT::Token` and `JWT::EncodedToken`
|
213
|
+
|
214
|
+
The `JWT::Token` and `JWT::EncodedToken` classes can be used to manage your JWTs.
|
215
|
+
|
216
|
+
### Signing and encoding a token
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
payload = { exp: Time.now.to_i + 60, jti: '1234', sub: "my-subject" }
|
220
|
+
header = { kid: 'hmac' }
|
221
|
+
|
222
|
+
token = JWT::Token.new(payload: payload, header: header)
|
223
|
+
token.sign!(algorithm: 'HS256', key: "secret")
|
224
|
+
|
225
|
+
token.jwt
|
226
|
+
# => "eyJraWQiOiJobWFjIiwiYWxnIjoiSFMyNTYifQ.eyJleHAiOjE3NTAwMDU0NzksImp0aSI6IjEyMzQiLCJzdWIiOiJteS1zdWJqZWN0In0.NRLcK6fYr3IdNfmncJePMWLQ34M4n14EgqSYrQIjL9w"
|
273
227
|
```
|
274
228
|
|
275
|
-
|
229
|
+
### Verifying and decoding a token
|
230
|
+
|
231
|
+
The `JWT::EncodedToken` can be used as a token object that allows verification of signatures and claims.
|
276
232
|
|
277
233
|
```ruby
|
278
|
-
|
234
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
279
235
|
|
280
|
-
|
236
|
+
encoded_token.verify_signature!(algorithm: 'HS256', key: "secret")
|
237
|
+
encoded_token.verify_signature!(algorithm: 'HS256', key: "wrong_secret") # raises JWT::VerificationError
|
238
|
+
encoded_token.verify_claims!(:exp, :jti)
|
239
|
+
encoded_token.verify_claims!(sub: ["not-my-subject"]) # raises JWT::InvalidSubError
|
240
|
+
encoded_token.claim_errors(sub: ["not-my-subject"]).map(&:message) # => ["Invalid subject. Expected [\"not-my-subject\"], received my-subject"]
|
241
|
+
encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
|
242
|
+
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
|
243
|
+
```
|
244
|
+
|
245
|
+
The `JWT::EncodedToken#verify!` method can be used to verify signature and claim verification in one go. The `exp` claim is verified by default.
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
249
|
+
encoded_token.verify!(signature: {algorithm: 'HS256', key: "secret"})
|
250
|
+
encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
|
251
|
+
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
|
252
|
+
```
|
253
|
+
|
254
|
+
A JWK can be used to sign and verify the token if it's possible to derive the signing algorithm from the key.
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
jwk_json = '{
|
258
|
+
"kty": "oct",
|
259
|
+
"k": "c2VjcmV0",
|
260
|
+
"alg": "HS256",
|
261
|
+
"kid": "hmac"
|
262
|
+
}'
|
263
|
+
|
264
|
+
jwk = JWT::JWK.import(JSON.parse(jwk_json))
|
265
|
+
|
266
|
+
token = JWT::Token.new(payload: payload, header: header)
|
267
|
+
|
268
|
+
token.sign!(key: jwk, algorithm: 'HS256')
|
281
269
|
|
282
|
-
|
283
|
-
|
270
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
271
|
+
encoded_token.verify!(signature: { algorithm: ["HS256", "HS512"], key: jwk})
|
272
|
+
```
|
273
|
+
|
274
|
+
#### Using a keyfinder
|
284
275
|
|
285
|
-
|
286
|
-
puts token
|
276
|
+
A keyfinder can be used to verify a signature. A keyfinder is an object responding to the `#call` method. The method expects to receive one argument, which is the token to be verified.
|
287
277
|
|
288
|
-
|
289
|
-
|
278
|
+
An example on using the built-in JWK keyfinder.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
# Create and sign a token
|
282
|
+
jwk = JWT::JWK.new(OpenSSL::PKey::RSA.generate(2048))
|
283
|
+
token = JWT::Token.new(payload: { pay: 'load' }, header: { kid: jwk.kid })
|
284
|
+
token.sign!(algorithm: 'RS256', key: jwk.signing_key)
|
290
285
|
|
291
|
-
#
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
#
|
296
|
-
puts decoded_token
|
286
|
+
# Create keyfinder object, verify and decode token
|
287
|
+
key_finder = JWT::JWK::KeyFinder.new(jwks: JWT::JWK::Set.new(jwk))
|
288
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
289
|
+
encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: key_finder})
|
290
|
+
encoded_token.payload # => { 'pay' => 'load' }
|
297
291
|
```
|
298
292
|
|
293
|
+
Using a custom keyfinder proc.
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
# Create and sign a token
|
297
|
+
key = OpenSSL::PKey::RSA.generate(2048)
|
298
|
+
token = JWT::Token.new(payload: { pay: 'load' })
|
299
|
+
token.sign!(algorithm: 'RS256', key: key)
|
300
|
+
|
301
|
+
# Verify and decode token
|
302
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
303
|
+
encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: ->(_token){ key.public_key }})
|
304
|
+
encoded_token.payload # => { 'pay' => 'load' }
|
305
|
+
```
|
306
|
+
|
307
|
+
### Detached payload
|
308
|
+
|
309
|
+
The `::JWT::Token#detach_payload!` method can be use to detach the payload from the JWT.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
token = JWT::Token.new(payload: { pay: 'load' })
|
313
|
+
token.sign!(algorithm: 'HS256', key: "secret")
|
314
|
+
token.detach_payload!
|
315
|
+
token.jwt # => "eyJhbGciOiJIUzI1NiJ9..UEhDY1Qlj29ammxuVRA_-gBah4qTy5FngIWg0yEAlC0"
|
316
|
+
token.encoded_payload # => "eyJwYXkiOiJsb2FkIn0"
|
317
|
+
```
|
318
|
+
|
319
|
+
The `JWT::EncodedToken` class can be used to decode a token with a detached payload by providing the payload to the token instance in separate.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
323
|
+
encoded_token.encoded_payload = "eyJwYXkiOiJsb2FkIn0"
|
324
|
+
encoded_token.verify_signature!(algorithm: 'HS256', key: "secret")
|
325
|
+
encoded_token.payload # => {"pay"=>"load"}
|
326
|
+
```
|
327
|
+
|
328
|
+
## Claims
|
329
|
+
|
330
|
+
JSON Web Token defines some reserved claim names and defines how they should be
|
331
|
+
used. JWT supports these reserved claim names:
|
332
|
+
|
333
|
+
- 'exp' (Expiration Time) Claim
|
334
|
+
- 'nbf' (Not Before Time) Claim
|
335
|
+
- 'iss' (Issuer) Claim
|
336
|
+
- 'aud' (Audience) Claim
|
337
|
+
- 'jti' (JWT ID) Claim
|
338
|
+
- 'iat' (Issued At) Claim
|
339
|
+
- 'sub' (Subject) Claim
|
340
|
+
|
299
341
|
### Expiration Time Claim
|
300
342
|
|
301
343
|
From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4):
|
302
344
|
|
303
|
-
> The `exp` (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the `exp` claim requires that the current date/time MUST be before the expiration date/time listed in the `exp` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a
|
304
|
-
|
305
|
-
**Handle Expiration Claim**
|
345
|
+
> The `exp` (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the `exp` claim requires that the current date/time MUST be before the expiration date/time listed in the `exp` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **_NumericDate_** value. Use of this claim is OPTIONAL.
|
306
346
|
|
307
347
|
```ruby
|
308
348
|
exp = Time.now.to_i + 4 * 3600
|
309
349
|
exp_payload = { data: 'data', exp: exp }
|
310
350
|
|
311
|
-
token = JWT.encode
|
351
|
+
token = JWT.encode(exp_payload, hmac_secret, 'HS256')
|
312
352
|
|
313
353
|
begin
|
314
|
-
decoded_token = JWT.decode
|
354
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { algorithm: 'HS256' })
|
315
355
|
rescue JWT::ExpiredSignature
|
316
356
|
# Handle expired token, e.g. logout user or deny access
|
317
357
|
end
|
318
358
|
```
|
319
359
|
|
320
360
|
The Expiration Claim verification can be disabled.
|
361
|
+
|
321
362
|
```ruby
|
322
363
|
# Decode token without raising JWT::ExpiredSignature error
|
323
|
-
JWT.decode
|
364
|
+
JWT.decode(token, hmac_secret, true, { verify_expiration: false, algorithm: 'HS256' })
|
324
365
|
```
|
325
366
|
|
326
|
-
|
367
|
+
Leeway and the exp claim.
|
327
368
|
|
328
369
|
```ruby
|
329
370
|
exp = Time.now.to_i - 10
|
@@ -332,11 +373,11 @@ leeway = 30 # seconds
|
|
332
373
|
exp_payload = { data: 'data', exp: exp }
|
333
374
|
|
334
375
|
# build expired token
|
335
|
-
token = JWT.encode
|
376
|
+
token = JWT.encode(exp_payload, hmac_secret, 'HS256')
|
336
377
|
|
337
378
|
begin
|
338
379
|
# add leeway to ensure the token is still accepted
|
339
|
-
decoded_token = JWT.decode
|
380
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { exp_leeway: leeway, algorithm: 'HS256' })
|
340
381
|
rescue JWT::ExpiredSignature
|
341
382
|
# Handle expired token, e.g. logout user or deny access
|
342
383
|
end
|
@@ -346,30 +387,29 @@ end
|
|
346
387
|
|
347
388
|
From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.5):
|
348
389
|
|
349
|
-
> The `nbf` (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the `nbf` claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the `nbf` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a
|
350
|
-
|
351
|
-
**Handle Not Before Claim**
|
390
|
+
> The `nbf` (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the `nbf` claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the `nbf` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **_NumericDate_** value. Use of this claim is OPTIONAL.
|
352
391
|
|
353
392
|
```ruby
|
354
393
|
nbf = Time.now.to_i - 3600
|
355
394
|
nbf_payload = { data: 'data', nbf: nbf }
|
356
395
|
|
357
|
-
token = JWT.encode
|
396
|
+
token = JWT.encode(nbf_payload, hmac_secret, 'HS256')
|
358
397
|
|
359
398
|
begin
|
360
|
-
decoded_token = JWT.decode
|
399
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { algorithm: 'HS256' })
|
361
400
|
rescue JWT::ImmatureSignature
|
362
401
|
# Handle invalid token, e.g. logout user or deny access
|
363
402
|
end
|
364
403
|
```
|
365
404
|
|
366
405
|
The Not Before Claim verification can be disabled.
|
406
|
+
|
367
407
|
```ruby
|
368
408
|
# Decode token without raising JWT::ImmatureSignature error
|
369
|
-
JWT.decode
|
409
|
+
JWT.decode(token, hmac_secret, true, { verify_not_before: false, algorithm: 'HS256' })
|
370
410
|
```
|
371
411
|
|
372
|
-
|
412
|
+
Leeway and the nbf claim.
|
373
413
|
|
374
414
|
```ruby
|
375
415
|
nbf = Time.now.to_i + 10
|
@@ -378,11 +418,11 @@ leeway = 30
|
|
378
418
|
nbf_payload = { data: 'data', nbf: nbf }
|
379
419
|
|
380
420
|
# build expired token
|
381
|
-
token = JWT.encode
|
421
|
+
token = JWT.encode(nbf_payload, hmac_secret, 'HS256')
|
382
422
|
|
383
423
|
begin
|
384
424
|
# add leeway to ensure the token is valid
|
385
|
-
decoded_token = JWT.decode
|
425
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { nbf_leeway: leeway, algorithm: 'HS256' })
|
386
426
|
rescue JWT::ImmatureSignature
|
387
427
|
# Handle invalid token, e.g. logout user or deny access
|
388
428
|
end
|
@@ -392,7 +432,7 @@ end
|
|
392
432
|
|
393
433
|
From [Oauth JSON Web Token 4.1.1. "iss" (Issuer) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.1):
|
394
434
|
|
395
|
-
> The `iss` (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The `iss` value is a case-sensitive string containing a
|
435
|
+
> The `iss` (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The `iss` value is a case-sensitive string containing a **_StringOrURI_** value. Use of this claim is OPTIONAL.
|
396
436
|
|
397
437
|
You can pass multiple allowed issuers as an Array, verification will pass if one of them matches the `iss` value in the payload.
|
398
438
|
|
@@ -400,11 +440,11 @@ You can pass multiple allowed issuers as an Array, verification will pass if one
|
|
400
440
|
iss = 'My Awesome Company Inc. or https://my.awesome.website/'
|
401
441
|
iss_payload = { data: 'data', iss: iss }
|
402
442
|
|
403
|
-
token = JWT.encode
|
443
|
+
token = JWT.encode(iss_payload, hmac_secret, 'HS256')
|
404
444
|
|
405
445
|
begin
|
406
446
|
# Add iss to the validation to check if the token has been manipulated
|
407
|
-
decoded_token = JWT.decode
|
447
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { iss: iss, verify_iss: true, algorithm: 'HS256' })
|
408
448
|
rescue JWT::InvalidIssuerError
|
409
449
|
# Handle invalid token, e.g. logout user or deny access
|
410
450
|
end
|
@@ -415,24 +455,24 @@ On supported ruby versions (>= 2.5) you can also delegate to methods, on older v
|
|
415
455
|
to convert them to proc (using `to_proc`)
|
416
456
|
|
417
457
|
```ruby
|
418
|
-
JWT.decode
|
458
|
+
JWT.decode(token, hmac_secret, true,
|
419
459
|
iss: %r'https://my.awesome.website/',
|
420
460
|
verify_iss: true,
|
421
|
-
algorithm: 'HS256'
|
461
|
+
algorithm: 'HS256')
|
422
462
|
```
|
423
463
|
|
424
464
|
```ruby
|
425
|
-
JWT.decode
|
465
|
+
JWT.decode(token, hmac_secret, true,
|
426
466
|
iss: ->(issuer) { issuer.start_with?('My Awesome Company Inc') },
|
427
467
|
verify_iss: true,
|
428
|
-
algorithm: 'HS256'
|
468
|
+
algorithm: 'HS256')
|
429
469
|
```
|
430
470
|
|
431
471
|
```ruby
|
432
|
-
JWT.decode
|
472
|
+
JWT.decode(token, hmac_secret, true,
|
433
473
|
iss: method(:valid_issuer?),
|
434
474
|
verify_iss: true,
|
435
|
-
algorithm: 'HS256'
|
475
|
+
algorithm: 'HS256')
|
436
476
|
|
437
477
|
# somewhere in the same class:
|
438
478
|
def valid_issuer?(issuer)
|
@@ -444,17 +484,17 @@ end
|
|
444
484
|
|
445
485
|
From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.3):
|
446
486
|
|
447
|
-
> The `aud` (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the `aud` claim when this claim is present, then the JWT MUST be rejected. In the general case, the `aud` value is an array of case-sensitive strings, each containing a
|
487
|
+
> The `aud` (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the `aud` claim when this claim is present, then the JWT MUST be rejected. In the general case, the `aud` value is an array of case-sensitive strings, each containing a **_StringOrURI_** value. In the special case when the JWT has one audience, the `aud` value MAY be a single case-sensitive string containing a **_StringOrURI_** value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.
|
448
488
|
|
449
489
|
```ruby
|
450
490
|
aud = ['Young', 'Old']
|
451
491
|
aud_payload = { data: 'data', aud: aud }
|
452
492
|
|
453
|
-
token = JWT.encode
|
493
|
+
token = JWT.encode(aud_payload, hmac_secret, 'HS256')
|
454
494
|
|
455
495
|
begin
|
456
496
|
# Add aud to the validation to check if the token has been manipulated
|
457
|
-
decoded_token = JWT.decode
|
497
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { aud: aud, verify_aud: true, algorithm: 'HS256' })
|
458
498
|
rescue JWT::InvalidAudError
|
459
499
|
# Handle invalid token, e.g. logout user or deny access
|
460
500
|
puts 'Audience Error'
|
@@ -473,15 +513,15 @@ jti_raw = [hmac_secret, iat].join(':').to_s
|
|
473
513
|
jti = Digest::MD5.hexdigest(jti_raw)
|
474
514
|
jti_payload = { data: 'data', iat: iat, jti: jti }
|
475
515
|
|
476
|
-
token = JWT.encode
|
516
|
+
token = JWT.encode(jti_payload, hmac_secret, 'HS256')
|
477
517
|
|
478
518
|
begin
|
479
519
|
# If :verify_jti is true, validation will pass if a JTI is present
|
480
|
-
#decoded_token = JWT.decode
|
520
|
+
#decoded_token = JWT.decode(token, hmac_secret, true, { verify_jti: true, algorithm: 'HS256' })
|
481
521
|
# Alternatively, pass a proc with your own code to check if the JTI has already been used
|
482
|
-
decoded_token = JWT.decode
|
522
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { verify_jti: proc { |jti| my_validation_method(jti) }, algorithm: 'HS256' })
|
483
523
|
# or
|
484
|
-
decoded_token = JWT.decode
|
524
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { verify_jti: proc { |jti, payload| my_validation_method(jti, payload) }, algorithm: 'HS256' })
|
485
525
|
rescue JWT::InvalidJtiError
|
486
526
|
# Handle invalid token, e.g. logout user or deny access
|
487
527
|
puts 'Error'
|
@@ -492,19 +532,17 @@ end
|
|
492
532
|
|
493
533
|
From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
|
494
534
|
|
495
|
-
> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. The `leeway` option is not taken into account when verifying this claim. The `iat_leeway` option was removed in version 2.2.0. Its value MUST be a number containing a
|
496
|
-
|
497
|
-
**Handle Issued At Claim**
|
535
|
+
> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. The `leeway` option is not taken into account when verifying this claim. The `iat_leeway` option was removed in version 2.2.0. Its value MUST be a number containing a **_NumericDate_** value. Use of this claim is OPTIONAL.
|
498
536
|
|
499
537
|
```ruby
|
500
538
|
iat = Time.now.to_i
|
501
539
|
iat_payload = { data: 'data', iat: iat }
|
502
540
|
|
503
|
-
token = JWT.encode
|
541
|
+
token = JWT.encode(iat_payload, hmac_secret, 'HS256')
|
504
542
|
|
505
543
|
begin
|
506
544
|
# Add iat to the validation to check if the token has been manipulated
|
507
|
-
decoded_token = JWT.decode
|
545
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { verify_iat: true, algorithm: 'HS256' })
|
508
546
|
rescue JWT::InvalidIatError
|
509
547
|
# Handle invalid token, e.g. logout user or deny access
|
510
548
|
end
|
@@ -514,17 +552,17 @@ end
|
|
514
552
|
|
515
553
|
From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2):
|
516
554
|
|
517
|
-
> The `sub` (subject) claim identifies the principal that is the subject of the JWT. The Claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The sub value is a case-sensitive string containing a
|
555
|
+
> The `sub` (subject) claim identifies the principal that is the subject of the JWT. The Claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The sub value is a case-sensitive string containing a **_StringOrURI_** value. Use of this claim is OPTIONAL.
|
518
556
|
|
519
557
|
```ruby
|
520
558
|
sub = 'Subject'
|
521
559
|
sub_payload = { data: 'data', sub: sub }
|
522
560
|
|
523
|
-
token = JWT.encode
|
561
|
+
token = JWT.encode(sub_payload, hmac_secret, 'HS256')
|
524
562
|
|
525
563
|
begin
|
526
564
|
# Add sub to the validation to check if the token has been manipulated
|
527
|
-
decoded_token = JWT.decode
|
565
|
+
decoded_token = JWT.decode(token, hmac_secret, true, { sub: sub, verify_sub: true, algorithm: 'HS256' })
|
528
566
|
rescue JWT::InvalidSubError
|
529
567
|
# Handle invalid token, e.g. logout user or deny access
|
530
568
|
end
|
@@ -535,6 +573,7 @@ end
|
|
535
573
|
The JWT claim verifications can be used to verify any Hash to include expected keys and values.
|
536
574
|
|
537
575
|
A few example on verifying the claims for a payload:
|
576
|
+
|
538
577
|
```ruby
|
539
578
|
JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10}, :numeric, :exp)
|
540
579
|
JWT::Claims.valid_payload?({"exp" => Time.now.to_i + 10}, :exp)
|
@@ -546,8 +585,6 @@ JWT::Claims.verify_payload!({"exp" => Time.now.to_i - 10}, exp: { leeway: 11})
|
|
546
585
|
JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10, "sub" => "subject"}, :exp, sub: "subject")
|
547
586
|
```
|
548
587
|
|
549
|
-
|
550
|
-
|
551
588
|
### Finding a Key
|
552
589
|
|
553
590
|
To dynamically find the key for verifying the JWT signature, pass a block to the decode block. The block receives headers and the original payload as parameters. It should return with the key to verify the signature that was used to sign the JWT.
|
@@ -558,7 +595,7 @@ iss_payload = { data: 'data', iss: issuers.first }
|
|
558
595
|
|
559
596
|
secrets = { issuers.first => hmac_secret, issuers.last => 'hmac_secret2' }
|
560
597
|
|
561
|
-
token = JWT.encode
|
598
|
+
token = JWT.encode(iss_payload, hmac_secret, 'HS256')
|
562
599
|
|
563
600
|
begin
|
564
601
|
# Add iss to the validation to check if the token has been manipulated
|
@@ -573,9 +610,10 @@ end
|
|
573
610
|
### Required Claims
|
574
611
|
|
575
612
|
You can specify claims that must be present for decoding to be successful. JWT::MissingRequiredClaim will be raised if any are missing
|
613
|
+
|
576
614
|
```ruby
|
577
615
|
# Will raise a JWT::MissingRequiredClaim error if the 'exp' claim is absent
|
578
|
-
JWT.decode
|
616
|
+
JWT.decode(token, hmac_secret, true, { required_claims: ['exp'], algorithm: 'HS256' })
|
579
617
|
```
|
580
618
|
|
581
619
|
### X.509 certificates in x5c header
|
@@ -599,7 +637,7 @@ rescue JWT::DecodeError
|
|
599
637
|
end
|
600
638
|
```
|
601
639
|
|
602
|
-
|
640
|
+
## JSON Web Key (JWK)
|
603
641
|
|
604
642
|
JWK is a JSON structure representing a cryptographic key. This gem currently supports RSA, EC, OKP and HMAC keys. OKP support requires [RbNaCl](https://github.com/RubyCrypto/rbnacl) and currently only supports the Ed25519 curve.
|
605
643
|
|
@@ -626,14 +664,14 @@ algorithms = jwks.map { |key| key[:alg] }.compact.uniq
|
|
626
664
|
JWT.decode(token, nil, true, algorithms: algorithms, jwks: jwks)
|
627
665
|
```
|
628
666
|
|
629
|
-
|
630
|
-
The `jwks` option can also be given as a lambda that evaluates every time a kid is resolved.
|
667
|
+
The `jwks` option can also be given as a lambda that evaluates every time a key identifier is resolved.
|
631
668
|
This can be used to implement caching of remotely fetched JWK Sets.
|
632
669
|
|
633
|
-
|
670
|
+
Key identifiers can be specified using `kid`, `x5t` header parameters.
|
671
|
+
If the requested identifier is not found from the given set the loader will be called a second time with the `kid_not_found` option set to `true`.
|
634
672
|
The application can choose to implement some kind of JWK cache invalidation or other mechanism to handle such cases.
|
635
673
|
|
636
|
-
Tokens without a specified `kid` are rejected by default.
|
674
|
+
Tokens without a specified key identifier (`kid` or `x5t`) are rejected by default.
|
637
675
|
This behaviour may be overwritten by setting the `allow_nil_kid` option for `decode` to `true`.
|
638
676
|
|
639
677
|
```ruby
|
@@ -712,7 +750,7 @@ jwk_hash = jwk.export
|
|
712
750
|
thumbprint_as_the_kid = jwk_hash[:kid]
|
713
751
|
```
|
714
752
|
|
715
|
-
|
753
|
+
## Development and testing
|
716
754
|
|
717
755
|
The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
|
718
756
|
|
@@ -721,7 +759,7 @@ bundle install
|
|
721
759
|
bundle exec appraisal rake test
|
722
760
|
```
|
723
761
|
|
724
|
-
|
762
|
+
## Releasing
|
725
763
|
|
726
764
|
To cut a new release adjust the [version.rb](lib/jwt/version.rb) and [CHANGELOG](CHANGELOG.md) with desired version numbers and dates and commit the changes. Tag the release with the version number using the following command:
|
727
765
|
|
@@ -732,6 +770,7 @@ rake release:source_control_push
|
|
732
770
|
This will tag a new version an trigger a [GitHub action](.github/workflows/push_gem.yml) that eventually will push the gem to rubygems.org.
|
733
771
|
|
734
772
|
## How to contribute
|
773
|
+
|
735
774
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
736
775
|
|
737
776
|
## Contributors
|