devise-jwt 0.6.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -2
- data/.rubocop.yml +40 -1
- data/.travis.yml +10 -10
- data/CHANGELOG.md +18 -1
- data/Dockerfile +7 -9
- data/LICENSE.txt +1 -1
- data/README.md +59 -36
- data/bin/console +5 -4
- data/devise-jwt.gemspec +9 -6
- data/docker-compose.yml +7 -2
- data/issue_template.md +1 -0
- data/lib/devise/jwt/defaults_generator.rb +0 -4
- data/lib/devise/jwt/mapping_inspector.rb +0 -2
- data/lib/devise/jwt/railtie.rb +9 -0
- data/lib/devise/jwt/revocation_strategies/{whitelist.rb → allowlist.rb} +8 -9
- data/lib/devise/jwt/revocation_strategies/{blacklist.rb → denylist.rb} +1 -1
- data/lib/devise/jwt/revocation_strategies.rb +2 -2
- data/lib/devise/jwt/test_helpers.rb +2 -7
- data/lib/devise/jwt/version.rb +1 -1
- data/lib/devise/jwt.rb +23 -19
- metadata +48 -24
- data/.overcommit.yml +0 -56
- data/.overcommit_gems.rb +0 -15
- data/.reek +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e40b6ccd72ec79899cc680dcbcafbfa0e2375ef5a079d21fd2bbd41863a0dd1
|
4
|
+
data.tar.gz: a2e8404f365a91acd324d5d62a6276943d08f0fd012b33f8e1a54098531d9327
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d39b950c6f645a487274958c644f637a61c4bcade854703027c04202f594fe4b7409861b8275b13f02d231ea2a292135b3ec94a06e3c04cc593529129ce7f68
|
7
|
+
data.tar.gz: a804da86acdc39451f169fef0e8b04ca7bd8874600a6259ae494a02ca85b727e3bd9e08590cfa0ca8c615af11ebec58df5032948a1ae76a57e3fd1f2cc52e11e
|
data/.codeclimate.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require: rubocop-rspec
|
2
2
|
AllCops:
|
3
|
-
TargetRubyVersion: 2.
|
3
|
+
TargetRubyVersion: 2.7
|
4
|
+
Exclude:
|
5
|
+
- Gemfile
|
6
|
+
- devise-jwt.gemspec
|
7
|
+
- spec/fixtures/rails_app/**/*
|
8
|
+
- vendor/**/*
|
4
9
|
RSpec/NestedGroups:
|
5
10
|
Max: 3
|
6
11
|
RSpec/MessageExpectation:
|
@@ -14,3 +19,37 @@ Metrics/BlockLength:
|
|
14
19
|
- "spec/**/*.rb"
|
15
20
|
Style/SafeNavigation:
|
16
21
|
Enabled: false
|
22
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
23
|
+
Enabled: true
|
24
|
+
Layout/SpaceAroundMethodCallOperator:
|
25
|
+
Enabled: true
|
26
|
+
Lint/DeprecatedOpenSSLConstant:
|
27
|
+
Enabled: true
|
28
|
+
Lint/MixedRegexpCaptureTypes:
|
29
|
+
Enabled: true
|
30
|
+
Lint/RaiseException:
|
31
|
+
Enabled: true
|
32
|
+
Lint/StructNewOverride:
|
33
|
+
Enabled: true
|
34
|
+
Style/AccessorGrouping:
|
35
|
+
Enabled: true
|
36
|
+
Style/BisectedAttrAccessor:
|
37
|
+
Enabled: true
|
38
|
+
Style/ExponentialNotation:
|
39
|
+
Enabled: true
|
40
|
+
Style/HashEachMethods:
|
41
|
+
Enabled: true
|
42
|
+
Style/HashTransformKeys:
|
43
|
+
Enabled: true
|
44
|
+
Style/HashTransformValues:
|
45
|
+
Enabled: true
|
46
|
+
Style/RedundantAssignment:
|
47
|
+
Enabled: true
|
48
|
+
Style/RedundantFetchBlock:
|
49
|
+
Enabled: true
|
50
|
+
Style/RedundantRegexpCharacterClass:
|
51
|
+
Enabled: true
|
52
|
+
Style/RedundantRegexpEscape:
|
53
|
+
Enabled: true
|
54
|
+
Style/SlicingWithRange:
|
55
|
+
Enabled: true
|
data/.travis.yml
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
sudo: false
|
2
1
|
language: ruby
|
2
|
+
cache: bundler
|
3
3
|
rvm:
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
-
|
4
|
+
- 2.6
|
5
|
+
- 2.7
|
6
|
+
- 3.0
|
7
|
+
- ruby-head
|
7
8
|
before_install:
|
8
9
|
- gem update --system --no-doc
|
9
|
-
-
|
10
|
-
before_script:
|
11
|
-
- git config --global user.email 'travis@travis.ci'
|
12
|
-
- git config --global user.name 'Travis CI'
|
10
|
+
- gem install bundler
|
13
11
|
script:
|
14
12
|
- bundle exec rspec
|
13
|
+
- bundle exec rubocop
|
15
14
|
- bundle exec codeclimate-test-reporter
|
16
|
-
|
17
|
-
|
15
|
+
jobs:
|
16
|
+
allow_failures:
|
17
|
+
- rvm: ruby-head
|
18
18
|
addons:
|
19
19
|
code_climate:
|
20
20
|
repo_token:
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
|
-
## [0.
|
7
|
+
## [0.9.0] - 2021-09-21
|
8
|
+
### Fixed
|
9
|
+
- Fix compatibility with dry-configurable 0.13
|
10
|
+
|
11
|
+
## [0.8.1] - 2021-02-14
|
12
|
+
### Fixed
|
13
|
+
- Fix behaviour on code reload
|
14
|
+
- Support ruby 3.0 and deprecate ruby 2.5
|
15
|
+
|
16
|
+
## [0.8.0] - 2020-07-06
|
17
|
+
### Fixed
|
18
|
+
- Fix compatibility with last version of dry-configurable
|
19
|
+
|
20
|
+
## [0.7.0] - 2020-06-03
|
21
|
+
### Fixed
|
22
|
+
- Replace whitelist/blacklist terminology with allowlist/denylist
|
23
|
+
|
24
|
+
## [0.6.0] - 2019-08-01
|
8
25
|
### Fixed
|
9
26
|
- Update warden-jwt_auth dependency to v0.4.0 so that now it is possible to configure algorithm.
|
10
27
|
|
data/Dockerfile
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
FROM ruby:
|
2
|
-
ENV
|
3
|
-
|
4
|
-
|
5
|
-
RUN
|
6
|
-
|
7
|
-
|
8
|
-
COPY $LIB_DIR/version.rb $APP_HOME/$LIB_DIR
|
9
|
-
RUN bundle install
|
1
|
+
FROM ruby:3.0.0
|
2
|
+
ENV APP_USER devise_jwt_user
|
3
|
+
RUN apt-get update -qq && \
|
4
|
+
apt-get install -y build-essential sqlite3 libsqlite3-dev
|
5
|
+
RUN useradd -ms /bin/bash $APP_USER
|
6
|
+
USER $APP_USER
|
7
|
+
WORKDIR /home/$APP_USER/app
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -14,19 +14,41 @@ solution using refresh tokens, like some implementation of OAuth2.
|
|
14
14
|
|
15
15
|
You can read about which security concerns this library takes into account and about JWT generic secure usage in the following series of posts:
|
16
16
|
|
17
|
-
- [Stand Up for JWT Revocation](http://waiting-for-dev.github.io/blog/2017/01/23/stand_up_for_jwt_revocation
|
18
|
-
- [JWT Revocation Strategies](http://waiting-for-dev.github.io/blog/2017/01/24/jwt_revocation_strategies
|
19
|
-
- [JWT Secure Usage](http://waiting-for-dev.github.io/blog/2017/01/25/jwt_secure_usage
|
20
|
-
- [A secure JWT authentication implementation for Rack and Rails](http://waiting-for-dev.github.io/blog/2017/01/26/a_secure_jwt_authentication_implementation_for_rack_and_rails
|
17
|
+
- [Stand Up for JWT Revocation](http://waiting-for-dev.github.io/blog/2017/01/23/stand_up_for_jwt_revocation)
|
18
|
+
- [JWT Revocation Strategies](http://waiting-for-dev.github.io/blog/2017/01/24/jwt_revocation_strategies)
|
19
|
+
- [JWT Secure Usage](http://waiting-for-dev.github.io/blog/2017/01/25/jwt_secure_usage)
|
20
|
+
- [A secure JWT authentication implementation for Rack and Rails](http://waiting-for-dev.github.io/blog/2017/01/26/a_secure_jwt_authentication_implementation_for_rack_and_rails)
|
21
21
|
|
22
22
|
`devise-jwt` is just a thin layer on top of [`warden-jwt_auth`](https://github.com/waiting-for-dev/warden-jwt_auth) that configures it to be used out of the box with devise and Rails.
|
23
23
|
|
24
|
+
## Upgrade notes
|
25
|
+
|
26
|
+
### v0.7.0
|
27
|
+
|
28
|
+
Since version v0.7.0 `Blacklist` revocation strategy has been renamed to `Denylist` while `Whitelist` has been renamed to `Allowlist`.
|
29
|
+
|
30
|
+
For `Denylist`, you only need to update the `include` line you're using in your revocation strategy model:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# include Devise::JWT::RevocationStrategies::Blacklist # before
|
34
|
+
include Devise::JWT::RevocationStrategies::Denylist
|
35
|
+
```
|
36
|
+
|
37
|
+
For `Allowlist`, you need to update the `include` line you're using in your user model:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# include Devise::JWT::RevocationStrategies::Whitelist # before
|
41
|
+
include Devise::JWT::RevocationStrategies::Allowlist
|
42
|
+
```
|
43
|
+
|
44
|
+
You also have to rename your `WhitelistedJwt` model to `AllowlistedJwt`, rename `model/whitelisted_jwt.rb` to `model/allowlisted_jwt.rb` and change the underlying database table to `allowlisted_jwts` (or configure the model to keep using the old name).
|
45
|
+
|
24
46
|
## Installation
|
25
47
|
|
26
48
|
Add this line to your application's Gemfile:
|
27
49
|
|
28
50
|
```ruby
|
29
|
-
gem 'devise-jwt'
|
51
|
+
gem 'devise-jwt'
|
30
52
|
```
|
31
53
|
|
32
54
|
And then execute:
|
@@ -69,14 +91,14 @@ You have to tell which user models you want to be able to authenticate with JWT
|
|
69
91
|
|
70
92
|
See [request_formats](#request_formats) configuration option if you are using paths with a format segment (like `.json`) in order to use it properly.
|
71
93
|
|
72
|
-
As you see, unlike other JWT authentication libraries, it is expected that tokens will be revoked by the server. I wrote about [why I think JWT revocation is needed and useful](http://waiting-for-dev.github.io/blog/2017/01/23/stand_up_for_jwt_revocation
|
94
|
+
As you see, unlike other JWT authentication libraries, it is expected that tokens will be revoked by the server. I wrote about [why I think JWT revocation is needed and useful](http://waiting-for-dev.github.io/blog/2017/01/23/stand_up_for_jwt_revocation).
|
73
95
|
|
74
96
|
An example configuration:
|
75
97
|
|
76
98
|
```ruby
|
77
99
|
class User < ApplicationRecord
|
78
100
|
devise :database_authenticatable,
|
79
|
-
:jwt_authenticatable, jwt_revocation_strategy:
|
101
|
+
:jwt_authenticatable, jwt_revocation_strategy: Denylist
|
80
102
|
end
|
81
103
|
```
|
82
104
|
|
@@ -132,7 +154,7 @@ This is so because of the following default devise workflow:
|
|
132
154
|
in the session without even reaching to any strategy (`:jwt_authenticatable`
|
133
155
|
in our case).
|
134
156
|
|
135
|
-
So, if you want to avoid this caveat you have
|
157
|
+
So, if you want to avoid this caveat you have three options:
|
136
158
|
|
137
159
|
- Disable the session. If you are developing an API, probably you don't need
|
138
160
|
it. In order to disable it, change `config/initializers/session_store.rb` to:
|
@@ -146,10 +168,19 @@ So, if you want to avoid this caveat you have two options:
|
|
146
168
|
```ruby
|
147
169
|
config.skip_session_storage = [:http_auth, :params_auth]
|
148
170
|
```
|
171
|
+
- If you are using Devise for another model (e.g. `AdminUser`) and doesn't want
|
172
|
+
to disable session storage for devise entirely, you can disable it on a
|
173
|
+
per-model basis:
|
174
|
+
```ruby
|
175
|
+
class User < ApplicationRecord
|
176
|
+
devise :database_authenticatable #, your other enabled modules...
|
177
|
+
self.skip_session_storage = [:http_auth, :params_auth]
|
178
|
+
end
|
179
|
+
```
|
149
180
|
|
150
181
|
### Revocation strategies
|
151
182
|
|
152
|
-
`devise-jwt` comes with three revocation strategies out of the box. Some of them are implementations of what is discussed in the blog post [JWT Revocation Strategies](http://waiting-for-dev.github.io/blog/2017/01/24/jwt_revocation_strategies
|
183
|
+
`devise-jwt` comes with three revocation strategies out of the box. Some of them are implementations of what is discussed in the blog post [JWT Revocation Strategies](http://waiting-for-dev.github.io/blog/2017/01/24/jwt_revocation_strategies), where I also talk about their pros and cons.
|
153
184
|
|
154
185
|
#### JTIMatcher
|
155
186
|
|
@@ -157,7 +188,7 @@ Here, the model class acts itself as the revocation strategy. It needs a new str
|
|
157
188
|
|
158
189
|
It works like the following:
|
159
190
|
|
160
|
-
-
|
191
|
+
- When a token is dispatched for a user, the `jti` claim is taken from the `jti` column in the model (which has been initialized when the record has been created).
|
161
192
|
- At every authenticated action, the incoming token `jti` claim is matched against the `jti` column for that user. The authentication only succeeds if they are the same.
|
162
193
|
- When the user requests to sign out its `jti` column changes, so that provided token won't be valid anymore.
|
163
194
|
|
@@ -196,29 +227,29 @@ def jwt_payload
|
|
196
227
|
end
|
197
228
|
```
|
198
229
|
|
199
|
-
####
|
230
|
+
#### Denylist
|
200
231
|
|
201
|
-
In this strategy, a database table is used as a
|
232
|
+
In this strategy, a database table is used as a list of revoked JWT tokens. The `jti` claim, which uniquely identifies a token, is persisted. The `exp` claim is also stored to allow the clean-up of staled tokens.
|
202
233
|
|
203
|
-
In order to use it, you need to create the
|
234
|
+
In order to use it, you need to create the denylist table in a migration:
|
204
235
|
|
205
236
|
```ruby
|
206
237
|
def change
|
207
|
-
create_table :
|
238
|
+
create_table :jwt_denylist do |t|
|
208
239
|
t.string :jti, null: false
|
209
240
|
t.datetime :exp, null: false
|
210
241
|
end
|
211
|
-
add_index :
|
242
|
+
add_index :jwt_denylist, :jti
|
212
243
|
end
|
213
244
|
```
|
214
245
|
For performance reasons, it is better if the `jti` column is an index.
|
215
246
|
|
216
|
-
Note: if you used the
|
247
|
+
Note: if you used the denylist strategy before vesion 0.4.0 you may not have the field *exp.* If not, run the following migration:
|
217
248
|
|
218
249
|
```ruby
|
219
|
-
class
|
250
|
+
class AddExpirationTimeToJWTDenylist < ActiveRecord::Migration
|
220
251
|
def change
|
221
|
-
add_column :
|
252
|
+
add_column :jwt_denylist, :exp, :datetime, null: false
|
222
253
|
end
|
223
254
|
end
|
224
255
|
|
@@ -227,10 +258,10 @@ end
|
|
227
258
|
Then, you need to create the corresponding model and include the strategy:
|
228
259
|
|
229
260
|
```ruby
|
230
|
-
class
|
231
|
-
include Devise::JWT::RevocationStrategies::
|
261
|
+
class JwtDenylist < ApplicationRecord
|
262
|
+
include Devise::JWT::RevocationStrategies::Denylist
|
232
263
|
|
233
|
-
self.table_name = '
|
264
|
+
self.table_name = 'jwt_denylist'
|
234
265
|
end
|
235
266
|
```
|
236
267
|
|
@@ -239,11 +270,11 @@ Last, configure the user model to use it:
|
|
239
270
|
```ruby
|
240
271
|
class User < ApplicationRecord
|
241
272
|
devise :database_authenticatable,
|
242
|
-
:jwt_authenticatable, jwt_revocation_strategy:
|
273
|
+
:jwt_authenticatable, jwt_revocation_strategy: JwtDenylist
|
243
274
|
end
|
244
275
|
```
|
245
276
|
|
246
|
-
####
|
277
|
+
#### Allowlist
|
247
278
|
|
248
279
|
Here, the model itself acts also as a revocation strategy, but it needs to have
|
249
280
|
a one-to-many association with another table which stores the tokens (in fact
|
@@ -266,11 +297,11 @@ devices for the same user.
|
|
266
297
|
The `exp` claim is also stored to allow the clean-up of staled tokens.
|
267
298
|
|
268
299
|
In order to use it, you have to create yourself the associated table and model.
|
269
|
-
The association table must be called `
|
300
|
+
The association table must be called `allowlisted_jwts`:
|
270
301
|
|
271
302
|
```ruby
|
272
303
|
def change
|
273
|
-
create_table :
|
304
|
+
create_table :allowlisted_jwts do |t|
|
274
305
|
t.string :jti, null: false
|
275
306
|
t.string :aud
|
276
307
|
# If you want to leverage the `aud` claim, add to it a `NOT NULL` constraint:
|
@@ -279,7 +310,7 @@ def change
|
|
279
310
|
t.references :your_user_table, foreign_key: { on_delete: :cascade }, null: false
|
280
311
|
end
|
281
312
|
|
282
|
-
add_index :
|
313
|
+
add_index :allowlisted_jwts, :jti, unique: true
|
283
314
|
end
|
284
315
|
```
|
285
316
|
Important: You are encouraged to set a unique index in the jti column. This way we can be sure at the database level that there aren't two valid tokens with same jti at the same time. Definining `foreign_key: { on_delete: :cascade }, null: false` on `t.references :your_user_table` helps to keep referential integrity of your database.
|
@@ -287,7 +318,7 @@ Important: You are encouraged to set a unique index in the jti column. This way
|
|
287
318
|
And then, the model:
|
288
319
|
|
289
320
|
```ruby
|
290
|
-
class
|
321
|
+
class AllowlistedJwt < ApplicationRecord
|
291
322
|
end
|
292
323
|
```
|
293
324
|
|
@@ -295,7 +326,7 @@ Finally, include the strategy in the model and configure it:
|
|
295
326
|
|
296
327
|
```ruby
|
297
328
|
class User < ApplicationRecord
|
298
|
-
include Devise::JWT::RevocationStrategies::
|
329
|
+
include Devise::JWT::RevocationStrategies::Allowlist
|
299
330
|
|
300
331
|
devise :database_authenticatable,
|
301
332
|
:jwt_authenticatable, jwt_revocation_strategy: self
|
@@ -490,14 +521,6 @@ An then, for example:
|
|
490
521
|
|
491
522
|
`docker-compose exec app rspec`
|
492
523
|
|
493
|
-
This gem uses [overcommit](https://github.com/brigade/overcommit) to execute some code review engines. If you submit a pull request, it will be executed in the CI process. In order to set it up, you need to do:
|
494
|
-
|
495
|
-
```ruby
|
496
|
-
bundle install --gemfile=.overcommit_gems.rb
|
497
|
-
overcommit --sign
|
498
|
-
overcommit --run # To test if it works
|
499
|
-
```
|
500
|
-
|
501
524
|
## Contributing
|
502
525
|
|
503
526
|
Bug reports and pull requests are welcome on GitHub at https://github.com/waiting-for-dev/devise-jwt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/bin/console
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'devise/jwt'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
8
9
|
|
9
10
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require
|
11
|
+
# require 'pry'
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start
|
data/devise-jwt.gemspec
CHANGED
@@ -22,17 +22,20 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_dependency 'devise', '~> 4.0'
|
25
|
-
spec.add_dependency 'warden-jwt_auth', '~> 0.
|
25
|
+
spec.add_dependency 'warden-jwt_auth', '~> 0.6'
|
26
26
|
|
27
27
|
spec.add_development_dependency "bundler", "> 1"
|
28
|
-
spec.add_development_dependency "rake", "~>
|
29
|
-
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
29
|
+
spec.add_development_dependency "rspec"
|
30
30
|
spec.add_development_dependency "pry-byebug", "~> 3.7"
|
31
31
|
# Needed to test the rails fixture application
|
32
|
-
spec.add_development_dependency 'rails', '~>
|
32
|
+
spec.add_development_dependency 'rails', '~> 6.0'
|
33
33
|
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
34
|
-
spec.add_development_dependency 'rspec-rails', '~>
|
34
|
+
spec.add_development_dependency 'rspec-rails', '~> 4.0'
|
35
|
+
# Cops
|
36
|
+
spec.add_development_dependency 'rubocop', '~> 0.87'
|
37
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.42'
|
35
38
|
# Test reporting
|
36
|
-
spec.add_development_dependency 'simplecov', '
|
39
|
+
spec.add_development_dependency 'simplecov', '0.17'
|
37
40
|
spec.add_development_dependency 'codeclimate-test-reporter', '~> 1.0'
|
38
41
|
end
|
data/docker-compose.yml
CHANGED
data/issue_template.md
CHANGED
@@ -18,6 +18,7 @@ Provide following information. Please, format pasted output as code. Feel free t
|
|
18
18
|
|
19
19
|
- Version of `devise-jwt` in use
|
20
20
|
- Version of `rails` in use
|
21
|
+
- Version of `warden-jwt_auth` in use
|
21
22
|
- Output of `Devise::JWT.config`
|
22
23
|
- Output of `Warden::JWTAuth.config`
|
23
24
|
- Output of `Devise.mappings`
|
@@ -42,14 +42,12 @@ module Devise
|
|
42
42
|
add_revocation_requests(inspector)
|
43
43
|
end
|
44
44
|
|
45
|
-
# :reek:FeatureEnvy
|
46
45
|
def add_mapping(inspector)
|
47
46
|
scope = inspector.scope
|
48
47
|
model = inspector.model
|
49
48
|
defaults[:mappings][scope] = model.name
|
50
49
|
end
|
51
50
|
|
52
|
-
# :reek:FeatureEnvy
|
53
51
|
def add_revocation_strategy(inspector)
|
54
52
|
scope = inspector.scope
|
55
53
|
strategy = inspector.model.jwt_revocation_strategy
|
@@ -91,7 +89,6 @@ module Devise
|
|
91
89
|
requests(inspector, :registration)
|
92
90
|
end
|
93
91
|
|
94
|
-
# :reek:FeatureEnvy
|
95
92
|
def requests(inspector, name)
|
96
93
|
path = inspector.path(name)
|
97
94
|
methods = inspector.methods(name)
|
@@ -100,7 +97,6 @@ module Devise
|
|
100
97
|
end
|
101
98
|
end
|
102
99
|
|
103
|
-
# :reek:UtilityFunction
|
104
100
|
def requests_for_format(path, methods, format)
|
105
101
|
path_regexp = format ? /^#{path}.#{format}$/ : /^#{path}$/
|
106
102
|
methods.map do |method|
|
@@ -27,7 +27,6 @@ module Devise
|
|
27
27
|
mapping.to
|
28
28
|
end
|
29
29
|
|
30
|
-
# :reek:FeatureEnvy
|
31
30
|
def path(name)
|
32
31
|
prefix, scope, request = path_parts(name)
|
33
32
|
[prefix, scope, request].delete_if do |item|
|
@@ -35,7 +34,6 @@ module Devise
|
|
35
34
|
end.join('/').prepend('/').gsub('//', '/')
|
36
35
|
end
|
37
36
|
|
38
|
-
# :reek:ControlParameter
|
39
37
|
def methods(name)
|
40
38
|
method = case name
|
41
39
|
when :sign_in then 'POST'
|
data/lib/devise/jwt/railtie.rb
CHANGED
@@ -21,6 +21,15 @@ module Devise
|
|
21
21
|
config.revocation_strategies = defaults[:revocation_strategies]
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
ActiveSupport::Reloader.to_prepare do
|
26
|
+
Warden::JWTAuth.configure do |config|
|
27
|
+
defaults = DefaultsGenerator.call
|
28
|
+
|
29
|
+
config.mappings = defaults[:mappings]
|
30
|
+
config.revocation_strategies = defaults[:revocation_strategies]
|
31
|
+
end
|
32
|
+
end
|
24
33
|
end
|
25
34
|
end
|
26
35
|
end
|
@@ -7,40 +7,39 @@ module Devise
|
|
7
7
|
module RevocationStrategies
|
8
8
|
# This strategy must be included in the user model.
|
9
9
|
#
|
10
|
-
# The
|
10
|
+
# The JwtAllowlist table must include `jti`, `aud`, `exp` and `user_id`
|
11
11
|
# columns
|
12
12
|
#
|
13
13
|
# In order to tell whether a token is revoked, it just tries to find the
|
14
|
-
# `jti` and `aud` values from the token on the `
|
14
|
+
# `jti` and `aud` values from the token on the `allowlisted_jwts`
|
15
15
|
# table for the respective user.
|
16
16
|
#
|
17
17
|
# If the values don't exist means the token was revoked.
|
18
18
|
# On revocation, it deletes the matching record from the
|
19
|
-
# `
|
19
|
+
# `allowlisted_jwts` table.
|
20
20
|
#
|
21
21
|
# On sign in, it creates a new record with the `jti` and `aud` values.
|
22
|
-
module
|
22
|
+
module Allowlist
|
23
23
|
extend ActiveSupport::Concern
|
24
24
|
|
25
25
|
included do
|
26
|
-
has_many :
|
26
|
+
has_many :allowlisted_jwts, dependent: :destroy
|
27
27
|
|
28
28
|
# @see Warden::JWTAuth::Interfaces::RevocationStrategy#jwt_revoked?
|
29
29
|
def self.jwt_revoked?(payload, user)
|
30
|
-
!user.
|
30
|
+
!user.allowlisted_jwts.exists?(payload.slice('jti', 'aud'))
|
31
31
|
end
|
32
32
|
|
33
33
|
# @see Warden::JWTAuth::Interfaces::RevocationStrategy#revoke_jwt
|
34
34
|
def self.revoke_jwt(payload, user)
|
35
|
-
jwt = user.
|
35
|
+
jwt = user.allowlisted_jwts.find_by(payload.slice('jti', 'aud'))
|
36
36
|
jwt.destroy! if jwt
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
40
|
# Warden::JWTAuth::Interfaces::User#on_jwt_dispatch
|
41
|
-
# :reek:FeatureEnvy
|
42
41
|
def on_jwt_dispatch(_token, payload)
|
43
|
-
|
42
|
+
allowlisted_jwts.create!(
|
44
43
|
jti: payload['jti'],
|
45
44
|
aud: payload['aud'],
|
46
45
|
exp: Time.at(payload['exp'].to_i)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'devise/jwt/revocation_strategies/jti_matcher'
|
4
|
-
require 'devise/jwt/revocation_strategies/
|
5
|
-
require 'devise/jwt/revocation_strategies/
|
4
|
+
require 'devise/jwt/revocation_strategies/denylist'
|
5
|
+
require 'devise/jwt/revocation_strategies/allowlist'
|
6
6
|
require 'devise/jwt/revocation_strategies/null'
|
7
7
|
|
8
8
|
module Devise
|
@@ -9,7 +9,7 @@ module Devise
|
|
9
9
|
#
|
10
10
|
# Side effects could happen if you have implemented
|
11
11
|
# `on_jwt_dispatch` method on the user model (as it happens in
|
12
|
-
# the
|
12
|
+
# the allowlist revocation strategy).
|
13
13
|
#
|
14
14
|
# Be aware that a fresh copy of `headers` is returned with the new
|
15
15
|
# key/value pair added, instead of modifying given argument.
|
@@ -20,18 +20,13 @@ module Devise
|
|
20
20
|
# autodetected.
|
21
21
|
# @param aud [String] The aud claim. If `nil` it will be autodetected from
|
22
22
|
# the header name configured in `Devise::JWT.config.aud_header`.
|
23
|
-
#
|
24
|
-
# :reek:LongParameterList
|
25
|
-
# :reek:ManualDispatch
|
26
23
|
def self.auth_headers(headers, user, scope: nil, aud: nil)
|
27
24
|
scope ||= Devise::Mapping.find_scope!(user)
|
28
25
|
aud ||= headers[Warden::JWTAuth.config.aud_header]
|
29
26
|
token, payload = Warden::JWTAuth::UserEncoder.new.call(
|
30
27
|
user, scope, aud
|
31
28
|
)
|
32
|
-
if user.respond_to?(:on_jwt_dispatch)
|
33
|
-
user.on_jwt_dispatch(token, payload)
|
34
|
-
end
|
29
|
+
user.on_jwt_dispatch(token, payload) if user.respond_to?(:on_jwt_dispatch)
|
35
30
|
Warden::JWTAuth::HeaderParser.to_headers(headers, token)
|
36
31
|
end
|
37
32
|
end
|
data/lib/devise/jwt/version.rb
CHANGED
data/lib/devise/jwt.rb
CHANGED
@@ -17,7 +17,9 @@ module Devise
|
|
17
17
|
#
|
18
18
|
# @see Warden::JWTAuth
|
19
19
|
def self.jwt
|
20
|
+
Warden::JWTAuth.config.to_h
|
20
21
|
yield(Devise::JWT.config)
|
22
|
+
Devise::JWT.config.to_h
|
21
23
|
end
|
22
24
|
|
23
25
|
add_module(:jwt_authenticatable, strategy: :jwt)
|
@@ -26,25 +28,31 @@ module Devise
|
|
26
28
|
module JWT
|
27
29
|
extend Dry::Configurable
|
28
30
|
|
29
|
-
setting
|
30
|
-
|
31
|
+
def self.forward_to_warden(setting, value)
|
32
|
+
default = Warden::JWTAuth.config.send(setting)
|
33
|
+
Warden::JWTAuth.config.send("#{setting}=", value || default)
|
34
|
+
Warden::JWTAuth.config.send(setting)
|
31
35
|
end
|
32
36
|
|
33
|
-
setting(:
|
34
|
-
|
35
|
-
|
37
|
+
setting(:secret,
|
38
|
+
default: Warden::JWTAuth.config.secret,
|
39
|
+
constructor: ->(value) { forward_to_warden(:secret, value) })
|
36
40
|
|
37
|
-
setting(:
|
38
|
-
|
39
|
-
|
41
|
+
setting(:expiration_time,
|
42
|
+
default: Warden::JWTAuth.config.expiration_time,
|
43
|
+
constructor: ->(value) { forward_to_warden(:expiration_time, value) })
|
40
44
|
|
41
|
-
setting(:
|
42
|
-
|
43
|
-
|
45
|
+
setting(:dispatch_requests,
|
46
|
+
default: Warden::JWTAuth.config.dispatch_requests,
|
47
|
+
constructor: ->(value) { forward_to_warden(:dispatch_requests, value) })
|
44
48
|
|
45
|
-
setting(:
|
46
|
-
|
47
|
-
|
49
|
+
setting(:revocation_requests,
|
50
|
+
default: Warden::JWTAuth.config.revocation_requests,
|
51
|
+
constructor: ->(value) { forward_to_warden(:revocation_requests, value) })
|
52
|
+
|
53
|
+
setting(:aud_header,
|
54
|
+
default: Warden::JWTAuth.config.aud_header,
|
55
|
+
constructor: ->(value) { forward_to_warden(:aud_header, value) })
|
48
56
|
|
49
57
|
# A hash of warden scopes as keys and an array of request formats that will
|
50
58
|
# be processed as values. When a scope is not present or if it has a nil
|
@@ -59,10 +67,6 @@ module Devise
|
|
59
67
|
# user: [:json],
|
60
68
|
# admin_user: [nil, :xml]
|
61
69
|
# }
|
62
|
-
setting :request_formats, {}
|
63
|
-
|
64
|
-
def self.forward_to_warden(setting, value)
|
65
|
-
Warden::JWTAuth.config.send("#{setting}=", value)
|
66
|
-
end
|
70
|
+
setting :request_formats, default: {}
|
67
71
|
end
|
68
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise-jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Busqué
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: devise
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.6'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,28 +58,28 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '13.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '13.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: pry-byebug
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '6.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '6.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: sqlite3
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,28 +128,56 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
131
|
+
version: '4.0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
138
|
+
version: '4.0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: rubocop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.87'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.87'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-rspec
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
159
|
+
version: '1.42'
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
166
|
+
version: '1.42'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0.17'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0.17'
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
182
|
name: codeclimate-test-reporter
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,9 +201,6 @@ extra_rdoc_files: []
|
|
173
201
|
files:
|
174
202
|
- ".codeclimate.yml"
|
175
203
|
- ".gitignore"
|
176
|
-
- ".overcommit.yml"
|
177
|
-
- ".overcommit_gems.rb"
|
178
|
-
- ".reek"
|
179
204
|
- ".rspec"
|
180
205
|
- ".rubocop.yml"
|
181
206
|
- ".travis.yml"
|
@@ -198,10 +223,10 @@ files:
|
|
198
223
|
- lib/devise/jwt/models/jwt_authenticatable.rb
|
199
224
|
- lib/devise/jwt/railtie.rb
|
200
225
|
- lib/devise/jwt/revocation_strategies.rb
|
201
|
-
- lib/devise/jwt/revocation_strategies/
|
226
|
+
- lib/devise/jwt/revocation_strategies/allowlist.rb
|
227
|
+
- lib/devise/jwt/revocation_strategies/denylist.rb
|
202
228
|
- lib/devise/jwt/revocation_strategies/jti_matcher.rb
|
203
229
|
- lib/devise/jwt/revocation_strategies/null.rb
|
204
|
-
- lib/devise/jwt/revocation_strategies/whitelist.rb
|
205
230
|
- lib/devise/jwt/test_helpers.rb
|
206
231
|
- lib/devise/jwt/version.rb
|
207
232
|
homepage: https://github.com/waiting-for-dev/devise-jwt
|
@@ -223,8 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
223
248
|
- !ruby/object:Gem::Version
|
224
249
|
version: '0'
|
225
250
|
requirements: []
|
226
|
-
|
227
|
-
rubygems_version: 2.7.8
|
251
|
+
rubygems_version: 3.1.2
|
228
252
|
signing_key:
|
229
253
|
specification_version: 4
|
230
254
|
summary: JWT authentication for devise
|
data/.overcommit.yml
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Select version of overcommit and the other tools from Gemfile
|
3
|
-
#
|
4
|
-
gemfile: .overcommit_gems.rb
|
5
|
-
|
6
|
-
#
|
7
|
-
# Hooks that are run against every commit message after a user has written it.
|
8
|
-
#
|
9
|
-
CommitMsg:
|
10
|
-
ALL:
|
11
|
-
required: true
|
12
|
-
exclude: &default_excludes
|
13
|
-
- Gemfile
|
14
|
-
- devise-jwt.gemspec
|
15
|
-
- spec/fixtures/rails_app/**/*
|
16
|
-
- README.md
|
17
|
-
- CHANGELOG.md
|
18
|
-
|
19
|
-
HardTabs:
|
20
|
-
enabled: true
|
21
|
-
|
22
|
-
SingleLineSubject:
|
23
|
-
enabled: true
|
24
|
-
|
25
|
-
#
|
26
|
-
# Hooks that are run after `git commit` is executed, before the commit message
|
27
|
-
# editor is displayed.
|
28
|
-
#
|
29
|
-
PreCommit:
|
30
|
-
ALL:
|
31
|
-
required: true
|
32
|
-
exclude: *default_excludes
|
33
|
-
|
34
|
-
BundleAudit:
|
35
|
-
enabled: true
|
36
|
-
|
37
|
-
BundleCheck:
|
38
|
-
enabled: true
|
39
|
-
|
40
|
-
LocalPathsInGemfile:
|
41
|
-
enabled: true
|
42
|
-
|
43
|
-
ExecutePermissions:
|
44
|
-
enabled: true
|
45
|
-
exclude:
|
46
|
-
- *default_excludes
|
47
|
-
- bin/*
|
48
|
-
|
49
|
-
Reek:
|
50
|
-
enabled: true
|
51
|
-
|
52
|
-
RuboCop:
|
53
|
-
enabled: true
|
54
|
-
|
55
|
-
TrailingWhitespace:
|
56
|
-
enabled: true
|
data/.overcommit_gems.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source 'https://rubygems.org'
|
4
|
-
|
5
|
-
gem 'overcommit', '~> 0.36'
|
6
|
-
|
7
|
-
# Patch-level verification for Bundled apps
|
8
|
-
gem 'bundler-audit', '~> 0.5'
|
9
|
-
|
10
|
-
# Ruby code smell reporter
|
11
|
-
gem 'reek', '~> 4.5'
|
12
|
-
|
13
|
-
# Ruby code style checking
|
14
|
-
gem 'rubocop', '~> 0.47'
|
15
|
-
gem 'rubocop-rspec', '~> 1.10'
|
data/.reek
DELETED
File without changes
|