jwt_keeper 5.0.1 → 6.1.0
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/README.md +1 -0
- data/jwt_keeper.gemspec +2 -1
- data/lib/generators/templates/jwt_keeper.rb +2 -4
- data/lib/jwt_keeper/controller.rb +1 -8
- data/lib/jwt_keeper/datastore.rb +26 -13
- data/lib/jwt_keeper/exceptions.rb +3 -0
- data/lib/jwt_keeper/version.rb +1 -1
- data/spec/lib/jwt_keeper/controller_spec.rb +2 -18
- data/spec/lib/jwt_keeper/datastore_spec.rb +78 -41
- data/spec/spec_helper.rb +3 -1
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6ad212dc01831b9bae31adab98ba2edbe1fd803923e32eb9817e2b96515c706
|
4
|
+
data.tar.gz: c6ef8deb150ffeed569d4da108ac745a907c8bfc065c5564cd49b6aff978f00e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87f73d56ceb956a78337d4df685ac8fb86b6eda03046ba137f92f689eb10b5485103f9ea003ccdb7643149b4e74722d674571ec698e8eed40eb0a2d3b1b2feb2
|
7
|
+
data.tar.gz: 7695c3ce514f9e150d492766b90e7b76e04cb5c496e422aa265d6f255a311e72d986319f169fc0bb5919a42fbe4cb364da700bfeb42fb947bfdb9a6841c5c280
|
data/README.md
CHANGED
@@ -40,6 +40,7 @@ class ApplicationController < ActionController::Base
|
|
40
40
|
include JWTKeeper::Controller
|
41
41
|
|
42
42
|
before_action :require_authentication
|
43
|
+
rescue_from JWTKeeper::NotAuthenticatedError, with: :not_authenticated
|
43
44
|
|
44
45
|
def not_authenticated
|
45
46
|
# Overload to return status 401
|
data/jwt_keeper.gemspec
CHANGED
@@ -28,8 +28,9 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'rspec', '~> 3.8'
|
29
29
|
spec.add_development_dependency 'fuubar'
|
30
30
|
spec.add_development_dependency 'simplecov'
|
31
|
+
spec.add_development_dependency 'redis-client'
|
32
|
+
spec.add_development_dependency 'redis'
|
31
33
|
|
32
|
-
spec.add_dependency 'redis'
|
33
34
|
spec.add_dependency 'rails'
|
34
35
|
spec.add_dependency 'activesupport'
|
35
36
|
spec.add_dependency 'jwt', '>= 1.5'
|
@@ -27,10 +27,8 @@ JWTKeeper.configure do |config|
|
|
27
27
|
# config.audience = 'example.com'
|
28
28
|
|
29
29
|
# the location of redis config file
|
30
|
-
# config.redis_connection =
|
31
|
-
# config.redis_connection =
|
32
|
-
# Redis.new(url: ENV['REDISCLOUD_URL'] || 'redis://localhost:6379/')
|
33
|
-
# end
|
30
|
+
# config.redis_connection = RedisClient.new(connection_options)
|
31
|
+
# config.redis_connection = RedisClient.config(connection_options).new_pool(size: ENV.fetch('RAILS_MAX_THREADS', 5))
|
34
32
|
|
35
33
|
# A unique idenfitier for the token version.
|
36
34
|
# config.version = 1
|
@@ -10,7 +10,7 @@ module JWTKeeper
|
|
10
10
|
|
11
11
|
if token.nil?
|
12
12
|
clear_authentication_token
|
13
|
-
|
13
|
+
raise JWTKeeper::NotAuthenticatedError
|
14
14
|
end
|
15
15
|
|
16
16
|
if token.version_mismatch? || token.pending?
|
@@ -51,13 +51,6 @@ module JWTKeeper
|
|
51
51
|
@authentication_token = nil
|
52
52
|
end
|
53
53
|
|
54
|
-
# The default action for denying non-authenticated connections.
|
55
|
-
# You can override this method in your controllers
|
56
|
-
# @return [void]
|
57
|
-
def not_authenticated
|
58
|
-
redirect_to root_path
|
59
|
-
end
|
60
|
-
|
61
54
|
# The default action for accepting authenticated connections.
|
62
55
|
# You can override this method in your controllers
|
63
56
|
# @return [void]
|
data/lib/jwt_keeper/datastore.rb
CHANGED
@@ -27,27 +27,40 @@ module JWTKeeper
|
|
27
27
|
|
28
28
|
# @!visibility private
|
29
29
|
def set_with_expiry(jti, seconds, type)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
redis.setex
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
with_redis do |redis|
|
31
|
+
if redis.respond_to?(:call) # For RedisClient
|
32
|
+
redis.call('SETEX', jti, seconds, type)
|
33
|
+
elsif redis.respond_to?(:setex) # For Redis
|
34
|
+
redis.setex(jti, seconds, type)
|
35
|
+
else
|
36
|
+
throw 'Bad Redis Connection'
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
# @!visibility private
|
42
42
|
def get(jti)
|
43
|
+
with_redis do |redis|
|
44
|
+
if redis.respond_to?(:call) # For RedisClient
|
45
|
+
redis.call('GET', jti)
|
46
|
+
elsif redis.respond_to?(:get) # For Redis
|
47
|
+
redis.get(jti)
|
48
|
+
else
|
49
|
+
throw 'Bad Redis Connection'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!visibility private
|
55
|
+
def with_redis
|
43
56
|
redis = JWTKeeper.configuration.redis_connection
|
44
57
|
|
45
|
-
if redis.
|
46
|
-
redis.
|
47
|
-
|
48
|
-
|
58
|
+
if redis.respond_to?(:with)
|
59
|
+
redis.with do |conn|
|
60
|
+
yield conn
|
61
|
+
end
|
49
62
|
else
|
50
|
-
|
63
|
+
yield(redis)
|
51
64
|
end
|
52
65
|
end
|
53
66
|
end
|
data/lib/jwt_keeper/version.rb
CHANGED
@@ -52,7 +52,6 @@ RSpec.describe JWTKeeper do
|
|
52
52
|
it { is_expected.to respond_to(:read_authentication_token) }
|
53
53
|
it { is_expected.to respond_to(:write_authentication_token) }
|
54
54
|
it { is_expected.to respond_to(:clear_authentication_token) }
|
55
|
-
it { is_expected.to respond_to(:not_authenticated) }
|
56
55
|
it { is_expected.to respond_to(:authenticated) }
|
57
56
|
it { is_expected.to respond_to(:regenerate_claims) }
|
58
57
|
end
|
@@ -77,13 +76,9 @@ RSpec.describe JWTKeeper do
|
|
77
76
|
|
78
77
|
context 'with expired token' do
|
79
78
|
let(:token) { JWTKeeper::Token.create(exp: 3.hours.ago) }
|
80
|
-
before do
|
81
|
-
allow(test_controller).to receive(:not_authenticated)
|
82
|
-
end
|
83
79
|
|
84
|
-
it '
|
85
|
-
subject.require_authentication
|
86
|
-
expect(subject).to have_received(:not_authenticated).once
|
80
|
+
it 'raises NotAuthenticated' do
|
81
|
+
expect { subject.require_authentication }.to raise_error(JWTKeeper::NotAuthenticatedError)
|
87
82
|
end
|
88
83
|
end
|
89
84
|
|
@@ -162,16 +157,5 @@ RSpec.describe JWTKeeper do
|
|
162
157
|
expect(subject.response.headers['Authorization']).to be_nil
|
163
158
|
end
|
164
159
|
end
|
165
|
-
|
166
|
-
describe '#not_authenticated' do
|
167
|
-
before do
|
168
|
-
allow(test_controller).to receive(:redirect_to)
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'it calls redirect_to' do
|
172
|
-
subject.not_authenticated
|
173
|
-
expect(subject).to have_received(:redirect_to).with('/')
|
174
|
-
end
|
175
|
-
end
|
176
160
|
end
|
177
161
|
end
|
@@ -1,70 +1,107 @@
|
|
1
1
|
RSpec.describe JWTKeeper::Datastore do
|
2
|
-
include_context 'initialize config'
|
3
2
|
let(:jti) { SecureRandom.uuid }
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
shared_examples 'Datastore Specs' do
|
5
|
+
describe '.rotate' do
|
6
|
+
before { described_class.rotate(jti, 30) }
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
it 'stores a token_id with a soft expiry' do
|
9
|
+
expect(described_class.send(:get, jti)).to eq 'soft'
|
10
|
+
end
|
10
11
|
end
|
11
|
-
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
describe '.pending?' do
|
14
|
+
context 'with a missing token' do
|
15
|
+
it 'returns false' do
|
16
|
+
expect(described_class.pending?(jti)).to be false
|
17
|
+
end
|
17
18
|
end
|
18
|
-
end
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
context 'with a revoked token' do
|
21
|
+
before { described_class.revoke(jti, 30) }
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
it 'returns false' do
|
24
|
+
expect(described_class.pending?(jti)).to be false
|
25
|
+
end
|
25
26
|
end
|
26
|
-
end
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
context 'with a pending token' do
|
29
|
+
before { described_class.rotate(jti, 30) }
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
it 'returns true' do
|
32
|
+
expect(described_class.pending?(jti)).to be true
|
33
|
+
end
|
33
34
|
end
|
34
35
|
end
|
35
|
-
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
describe '.revoke' do
|
38
|
+
before do
|
39
|
+
described_class.revoke(jti, 30)
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
it 'stores a token_id with a hard expiry' do
|
43
|
+
expect(described_class.send(:get, jti)).to eq 'hard'
|
44
|
+
end
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
describe '.revoked?' do
|
48
|
+
context 'with a missing token' do
|
49
|
+
it 'returns false' do
|
50
|
+
expect(described_class.revoked?(jti)).to be false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a revoked token' do
|
55
|
+
before { described_class.revoke(jti, 30) }
|
56
|
+
|
57
|
+
it 'returns true' do
|
58
|
+
expect(described_class.revoked?(jti)).to be true
|
59
|
+
end
|
51
60
|
end
|
52
|
-
end
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
context 'with a pending token' do
|
63
|
+
before { described_class.rotate(jti, 30) }
|
56
64
|
|
57
|
-
|
58
|
-
|
65
|
+
it 'returns false' do
|
66
|
+
expect(described_class.revoked?(jti)).to be false
|
67
|
+
end
|
59
68
|
end
|
60
69
|
end
|
70
|
+
end
|
61
71
|
|
62
|
-
|
63
|
-
|
72
|
+
context 'with Redis' do
|
73
|
+
include_context 'initialize config'
|
74
|
+
|
75
|
+
let(:redis_connection) { Redis.new(url: ENV['REDIS_URL']) }
|
76
|
+
|
77
|
+
include_examples 'Datastore Specs'
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with Redis Connection Pool' do
|
81
|
+
include_context 'initialize config'
|
64
82
|
|
65
|
-
|
66
|
-
|
83
|
+
let(:redis_connection) do
|
84
|
+
ConnectionPool.new(size: ENV.fetch('RAILS_MAX_THREADS', 5)) do
|
85
|
+
Redis.new(url: ENV['REDISCLOUD_URL'] || 'redis://localhost:6379/')
|
67
86
|
end
|
68
87
|
end
|
88
|
+
|
89
|
+
include_examples 'Datastore Specs'
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with RedisClient' do
|
93
|
+
include_context 'initialize config'
|
94
|
+
|
95
|
+
let(:redis_connection) { RedisClient.new(url: ENV['REDIS_URL']) }
|
96
|
+
|
97
|
+
include_examples 'Datastore Specs'
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with RedisClient Pool' do
|
101
|
+
include_context 'initialize config'
|
102
|
+
|
103
|
+
let(:redis_connection) { RedisClient.config(url: ENV['REDIS_URL']).new_pool(size: ENV.fetch('RAILS_MAX_THREADS', 5)) }
|
104
|
+
|
105
|
+
include_examples 'Datastore Specs'
|
69
106
|
end
|
70
107
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -39,6 +39,7 @@ RSpec.configure do |config|
|
|
39
39
|
end
|
40
40
|
|
41
41
|
RSpec.shared_context 'initialize config' do
|
42
|
+
let(:redis_connection) { RedisClient.new(url: ENV['REDIS_URL']) }
|
42
43
|
let(:config) do
|
43
44
|
{
|
44
45
|
algorithm: 'HS256',
|
@@ -46,12 +47,13 @@ RSpec.shared_context 'initialize config' do
|
|
46
47
|
expiry: 24.hours,
|
47
48
|
issuer: 'api.example.com',
|
48
49
|
audience: 'example.com',
|
49
|
-
redis_connection:
|
50
|
+
redis_connection: redis_connection,
|
50
51
|
version: nil,
|
51
52
|
cookie_lock: false
|
52
53
|
}
|
53
54
|
end
|
54
55
|
|
56
|
+
|
55
57
|
before(:each) do
|
56
58
|
JWTKeeper.configure(JWTKeeper::Configuration.new(config))
|
57
59
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Rivera
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-02-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -137,6 +137,20 @@ dependencies:
|
|
137
137
|
- - ">="
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: redis-client
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
140
154
|
- !ruby/object:Gem::Dependency
|
141
155
|
name: redis
|
142
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,7 +158,7 @@ dependencies:
|
|
144
158
|
- - ">="
|
145
159
|
- !ruby/object:Gem::Version
|
146
160
|
version: '0'
|
147
|
-
type: :
|
161
|
+
type: :development
|
148
162
|
prerelease: false
|
149
163
|
version_requirements: !ruby/object:Gem::Requirement
|
150
164
|
requirements:
|
@@ -249,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
263
|
- !ruby/object:Gem::Version
|
250
264
|
version: '0'
|
251
265
|
requirements: []
|
252
|
-
rubygems_version: 3.
|
266
|
+
rubygems_version: 3.4.1
|
253
267
|
signing_key:
|
254
268
|
specification_version: 4
|
255
269
|
summary: JWT for Rails made easy
|