jwt_sessions 2.5.0 → 2.7.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/CHANGELOG.md +55 -0
- data/README.md +25 -3
- data/lib/jwt_sessions/store_adapters/memory_store_adapter.rb +1 -1
- data/lib/jwt_sessions/store_adapters/redis_store_adapter.rb +30 -12
- data/lib/jwt_sessions/store_adapters.rb +1 -1
- data/lib/jwt_sessions/token.rb +5 -3
- data/lib/jwt_sessions/version.rb +1 -1
- data/lib/jwt_sessions.rb +2 -2
- data/test/units/jwt_sessions/store_adapters/test_redis_store_adapter.rb +18 -7
- data/test/units/jwt_sessions/test_token.rb +3 -3
- metadata +14 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad42d33af309b3295e9aa6402179df2a9a0ac5e17618d133427e27edafca9578
|
4
|
+
data.tar.gz: e67b97b9bf3a2aec4bba3f66cc517d4dad03bcb4ce73269147cce31bcd9d85d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe6bc8a993388688a85d6b86ea139ab8d3123b628db77181fa4db6ba68d92c0080ca16fdfcaeb3567be9f2a07896df711cb9edb9c7845d6a226408e6fda00de9
|
7
|
+
data.tar.gz: 712ea681644f82710f5d561a96e78df86c1c3e4c225b8535a9d1cd2e03bcb363d93e4b42764847b786461e26f6f9440a3f85b026e02fa557c647a5866eef70dc
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
## 2.7.0 (October 05, 2021)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- added redis_client setting to JWTSessions::StoreAdapters::RedisStoreAdapter
|
6
|
+
|
7
|
+
## 2.6.0 (June 01, 2021)
|
8
|
+
|
9
|
+
Features:
|
10
|
+
|
11
|
+
- added support for all Redis settings
|
12
|
+
|
13
|
+
Support:
|
14
|
+
|
15
|
+
- updated jwt to '>= 2.2.3'
|
16
|
+
- switched to redis scan when looking for keys
|
17
|
+
- removed extra gems from gemspec deps
|
18
|
+
- updated gems in dummy apps
|
19
|
+
|
20
|
+
## 2.5.2 (July 06, 2020)
|
21
|
+
|
22
|
+
Bugfixes:
|
23
|
+
|
24
|
+
- fixed `Using the last argument as keyword parameters is deprecated;` warnings
|
25
|
+
|
26
|
+
## 2.5.1 (April 20, 2020)
|
27
|
+
|
28
|
+
Features:
|
29
|
+
|
30
|
+
- added changelog
|
31
|
+
|
32
|
+
Bugfixes:
|
33
|
+
|
34
|
+
- fixed double exp key in payload
|
35
|
+
|
36
|
+
Support:
|
37
|
+
|
38
|
+
- moved decode error text to a constant within token class
|
39
|
+
|
40
|
+
## 2.5.0 (April 12, 2020)
|
41
|
+
|
42
|
+
Features:
|
43
|
+
|
44
|
+
- added new error class `JWTSessions::Errors::Expired`
|
45
|
+
|
46
|
+
## 2.4.3 (September 19, 2019)
|
47
|
+
|
48
|
+
Bugfixes:
|
49
|
+
|
50
|
+
- fixed lookup for refresh token for namespaced sessions
|
51
|
+
|
52
|
+
Support:
|
53
|
+
|
54
|
+
- updated sqlite to ~> 1.4 in `dummy_api`
|
55
|
+
- added 2.6.3 Ruby to CI
|
data/README.md
CHANGED
@@ -153,11 +153,11 @@ end
|
|
153
153
|
```
|
154
154
|
|
155
155
|
Specify an encryption key for JSON Web Tokens in `config/initializers/jwt_session.rb` \
|
156
|
-
It is advisable to store the key itself within
|
156
|
+
It is advisable to store the key itself in a secure way, f.e. within app credentials.
|
157
157
|
|
158
158
|
```ruby
|
159
159
|
JWTSessions.algorithm = "HS256"
|
160
|
-
JWTSessions.encryption_key = Rails.application.
|
160
|
+
JWTSessions.encryption_key = Rails.application.credentials.secret_jwt_encryption_key
|
161
161
|
```
|
162
162
|
|
163
163
|
Most of the encryption algorithms require private and public keys to sign a token. However, HMAC requires only a single key and you can use the `encryption_key` shortcut to sign the token. For other algorithms you must specify private and public keys separately.
|
@@ -369,6 +369,21 @@ JWTSessions.token_store = :redis, { redis_url: "redis://localhost:6397" }
|
|
369
369
|
|
370
370
|
**NOTE:** if `REDIS_URL` environment variable is set it is used automatically.
|
371
371
|
|
372
|
+
SSL, timeout, reconnect, etc. redis settings are supported:
|
373
|
+
```ruby
|
374
|
+
JWTSessions.token_store = :redis, {
|
375
|
+
read_timeout: 1.5,
|
376
|
+
reconnect_attempts: 10,
|
377
|
+
ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
|
378
|
+
}
|
379
|
+
```
|
380
|
+
|
381
|
+
If you already have a configured Redis client, you can pass it among the options to reduce opened connections to a Redis server:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
JWTSessions.token_store = :redis, {redis_client: Redis.current}
|
385
|
+
```
|
386
|
+
|
372
387
|
##### JWT signature
|
373
388
|
|
374
389
|
```ruby
|
@@ -449,7 +464,7 @@ It is defined globally, but can be overridden on a session level. See `JWTSessio
|
|
449
464
|
`JWTSessions::Errors::InvalidPayload` - token's payload doesn't contain required keys or they are invalid. \
|
450
465
|
`JWTSessions::Errors::Unauthorized` - token can't be decoded or JWT claims are invalid. \
|
451
466
|
`JWTSessions::Errors::ClaimsVerification` - JWT claims are invalid (inherited from `JWTSessions::Errors::Unauthorized`). \
|
452
|
-
`JWTSessions::Errors::Expired` - token is expired (inherited from `JWTSessions::Errors::
|
467
|
+
`JWTSessions::Errors::Expired` - token is expired (inherited from `JWTSessions::Errors::ClaimsVerification`).
|
453
468
|
|
454
469
|
#### CSRF and cookies
|
455
470
|
|
@@ -592,6 +607,13 @@ session = JWTSessions::Session.new(namespace: "ie-sessions")
|
|
592
607
|
session.flush_namespaced # will flush all sessions which belong to the same namespace
|
593
608
|
```
|
594
609
|
|
610
|
+
Selectively flush one single session inside a namespace by its access token:
|
611
|
+
|
612
|
+
```ruby
|
613
|
+
session = JWTSessions::Session.new(namespace: "ie-sessions", payload: payload)
|
614
|
+
session.flush_by_access_payload # will flush a specific session which belongs to an existing namespace
|
615
|
+
```
|
616
|
+
|
595
617
|
Flush access tokens only:
|
596
618
|
|
597
619
|
```ruby
|
@@ -5,7 +5,7 @@ module JWTSessions
|
|
5
5
|
class MemoryStoreAdapter < AbstractStoreAdapter
|
6
6
|
attr_reader :storage
|
7
7
|
|
8
|
-
def initialize(options)
|
8
|
+
def initialize(**options)
|
9
9
|
raise ArgumentError, "Memory store doesn't support any options" if options.any?
|
10
10
|
@storage = Hash.new do |h, k|
|
11
11
|
h[k] = Hash.new { |hh, kk| hh[kk] = {} }
|
@@ -7,16 +7,20 @@ module JWTSessions
|
|
7
7
|
|
8
8
|
REFRESH_KEYS = %i[csrf access_uid access_expiration expiration].freeze
|
9
9
|
|
10
|
-
def initialize(token_prefix: JWTSessions.token_prefix, **options)
|
10
|
+
def initialize(token_prefix: JWTSessions.token_prefix, redis_client: nil, **options)
|
11
11
|
@prefix = token_prefix
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
if redis_client
|
14
|
+
@storage = redis_client
|
15
|
+
else
|
16
|
+
begin
|
17
|
+
require "redis"
|
18
|
+
@storage = configure_redis_client(**options)
|
19
|
+
rescue LoadError => e
|
20
|
+
msg = "Could not load the 'redis' gem, please add it to your gemfile or " \
|
21
|
+
"configure a different adapter (e.g. JWTSessions.store_adapter = :memory)"
|
22
|
+
raise e.class, msg, e.backtrace
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -62,7 +66,7 @@ module JWTSessions
|
|
62
66
|
end
|
63
67
|
|
64
68
|
def all_refresh_tokens(namespace)
|
65
|
-
keys_in_namespace =
|
69
|
+
keys_in_namespace = scan_keys(refresh_key("*", namespace))
|
66
70
|
(keys_in_namespace || []).each_with_object({}) do |key, acc|
|
67
71
|
uid = uid_from_key(key)
|
68
72
|
acc[uid] = fetch_refresh(uid, namespace)
|
@@ -80,7 +84,7 @@ module JWTSessions
|
|
80
84
|
|
81
85
|
private
|
82
86
|
|
83
|
-
def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil)
|
87
|
+
def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil, **options)
|
84
88
|
if redis_url && (redis_host || redis_port || redis_db_name)
|
85
89
|
raise ArgumentError, "redis_url cannot be passed along with redis_host, redis_port or redis_db_name options"
|
86
90
|
end
|
@@ -91,7 +95,7 @@ module JWTSessions
|
|
91
95
|
redis_db_name: redis_db_name
|
92
96
|
)
|
93
97
|
|
94
|
-
Redis.new(url: redis_url)
|
98
|
+
Redis.new(options.merge(url: redis_url))
|
95
99
|
end
|
96
100
|
|
97
101
|
def build_redis_url(redis_host: nil, redis_port: nil, redis_db_name: nil)
|
@@ -111,7 +115,7 @@ module JWTSessions
|
|
111
115
|
|
112
116
|
def first_refresh_key(uid)
|
113
117
|
key = full_refresh_key(uid, "*")
|
114
|
-
(
|
118
|
+
(scan_keys(key) || []).first
|
115
119
|
end
|
116
120
|
|
117
121
|
def refresh_key(uid, namespace)
|
@@ -126,6 +130,20 @@ module JWTSessions
|
|
126
130
|
def uid_from_key(key)
|
127
131
|
key.split("_").last
|
128
132
|
end
|
133
|
+
|
134
|
+
def scan_keys(key_pattern)
|
135
|
+
cursor = 0
|
136
|
+
all_keys = []
|
137
|
+
|
138
|
+
loop do
|
139
|
+
cursor, keys = storage.scan(cursor, match: key_pattern, count: 1000)
|
140
|
+
all_keys |= keys
|
141
|
+
|
142
|
+
break if cursor == "0"
|
143
|
+
end
|
144
|
+
|
145
|
+
all_keys
|
146
|
+
end
|
129
147
|
end
|
130
148
|
end
|
131
149
|
end
|
@@ -9,7 +9,7 @@ module JWTSessions
|
|
9
9
|
def self.build_by_name(adapter, options = nil)
|
10
10
|
camelized_adapter = adapter.to_s.split('_').map(&:capitalize).join
|
11
11
|
adapter_class_name = "#{camelized_adapter}StoreAdapter"
|
12
|
-
StoreAdapters.const_get(adapter_class_name).new(options || {})
|
12
|
+
StoreAdapters.const_get(adapter_class_name).new(**(options || {}))
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
data/lib/jwt_sessions/token.rb
CHANGED
@@ -4,6 +4,8 @@ require "jwt"
|
|
4
4
|
|
5
5
|
module JWTSessions
|
6
6
|
class Token
|
7
|
+
DECODE_ERROR = "cannot decode the token"
|
8
|
+
|
7
9
|
class << self
|
8
10
|
def encode(payload)
|
9
11
|
exp_payload = meta.merge(payload)
|
@@ -20,18 +22,18 @@ module JWTSessions
|
|
20
22
|
rescue JWT::DecodeError => e
|
21
23
|
raise Errors::Unauthorized, e.message
|
22
24
|
rescue StandardError
|
23
|
-
raise Errors::Unauthorized,
|
25
|
+
raise Errors::Unauthorized, DECODE_ERROR
|
24
26
|
end
|
25
27
|
|
26
28
|
def decode!(token)
|
27
29
|
decode_options = { algorithm: JWTSessions.algorithm }
|
28
30
|
JWT.decode(token, JWTSessions.public_key, false, decode_options)
|
29
31
|
rescue StandardError
|
30
|
-
raise Errors::Unauthorized,
|
32
|
+
raise Errors::Unauthorized, DECODE_ERROR
|
31
33
|
end
|
32
34
|
|
33
35
|
def meta
|
34
|
-
{ exp
|
36
|
+
{ "exp" => JWTSessions.access_expiration }
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
data/lib/jwt_sessions/version.rb
CHANGED
data/lib/jwt_sessions.rb
CHANGED
@@ -155,7 +155,7 @@ module JWTSessions
|
|
155
155
|
private
|
156
156
|
|
157
157
|
def supported_algos
|
158
|
-
algos = JWT::Algos
|
159
|
-
algos.map { |algo|
|
158
|
+
algos = JWT::Algos::ALGOS - [JWT::Algos::Unsupported]
|
159
|
+
algos.map { |algo| algo::SUPPORTED }.flatten + [NONE]
|
160
160
|
end
|
161
161
|
end
|
@@ -20,13 +20,18 @@ class TestRedisStoreAdapter < Minitest::Test
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
def test_support_of_extra_options
|
24
|
+
adapter = JWTSessions::StoreAdapters::RedisStoreAdapter.new(
|
25
|
+
redis_url: "redis://127.0.0.1:6379",
|
26
|
+
ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE },
|
27
|
+
reconnect_delay: 2,
|
28
|
+
timeout: 8
|
29
|
+
)
|
30
|
+
options = adapter.storage.instance_variable_get(:@options)
|
31
|
+
|
32
|
+
assert_equal 8, options[:timeout]
|
33
|
+
assert_equal 2, options[:reconnect_delay]
|
34
|
+
assert_equal 0, options[:ssl_params][:verify_mode]
|
30
35
|
end
|
31
36
|
|
32
37
|
def test_default_url
|
@@ -72,4 +77,10 @@ class TestRedisStoreAdapter < Minitest::Test
|
|
72
77
|
adapter = JWTSessions::StoreAdapters::RedisStoreAdapter.new
|
73
78
|
assert_equal "redis://127.0.0.2:6322/0", adapter.storage.connection[:id]
|
74
79
|
end
|
80
|
+
|
81
|
+
def test_configuration_via_redis_client
|
82
|
+
client = Object.new
|
83
|
+
adapter = JWTSessions::StoreAdapters::RedisStoreAdapter.new(redis_client: client)
|
84
|
+
assert_equal client, adapter.storage
|
85
|
+
end
|
75
86
|
end
|
@@ -110,11 +110,11 @@ class TestToken < Minitest::Test
|
|
110
110
|
def test_token_leeway_decode
|
111
111
|
JWTSessions.encryption_key = "abcdefghijklmnopqrstuvwxyzABCDEF"
|
112
112
|
JWTSessions.jwt_options.leeway = 50
|
113
|
-
token = JWTSessions::Token.encode(payload.merge(exp
|
113
|
+
token = JWTSessions::Token.encode(payload.merge("exp" => Time.now.to_i - 20))
|
114
114
|
decoded = JWTSessions::Token.decode(token).first
|
115
115
|
assert_equal payload["user_id"], decoded["user_id"]
|
116
116
|
assert_equal payload["secret"], decoded["secret"]
|
117
|
-
token = JWTSessions::Token.encode(payload.merge(exp
|
117
|
+
token = JWTSessions::Token.encode(payload.merge("exp" => Time.now.to_i - 100))
|
118
118
|
assert_raises JWTSessions::Errors::Unauthorized do
|
119
119
|
JWTSessions::Token.decode(token)
|
120
120
|
end
|
@@ -141,7 +141,7 @@ class TestToken < Minitest::Test
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def test_payload_exp_time
|
144
|
-
token = JWTSessions::Token.encode(payload.merge(exp
|
144
|
+
token = JWTSessions::Token.encode(payload.merge("exp" => Time.now.to_i - (3600 * 24)))
|
145
145
|
assert_raises JWTSessions::Errors::Expired do
|
146
146
|
JWTSessions::Token.decode(token)
|
147
147
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_sessions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yulia Oletskaya
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.2.3
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '3'
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 2.
|
29
|
+
version: 2.2.3
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '3'
|
@@ -44,34 +44,6 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '1.16'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: minitest
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - "~>"
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '5.11'
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - "~>"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '5.11'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: pry
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '0.11'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '0.11'
|
75
47
|
- !ruby/object:Gem::Dependency
|
76
48
|
name: rake
|
77
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,6 +64,7 @@ executables: []
|
|
92
64
|
extensions: []
|
93
65
|
extra_rdoc_files: []
|
94
66
|
files:
|
67
|
+
- CHANGELOG.md
|
95
68
|
- LICENSE
|
96
69
|
- README.md
|
97
70
|
- lib/jwt_sessions.rb
|
@@ -120,8 +93,12 @@ files:
|
|
120
93
|
homepage: http://rubygems.org/gems/jwt_sessions
|
121
94
|
licenses:
|
122
95
|
- MIT
|
123
|
-
metadata:
|
124
|
-
|
96
|
+
metadata:
|
97
|
+
homepage_uri: https://github.com/tuwukee/jwt_sessions
|
98
|
+
changelog_uri: https://github.com/tuwukee/jwt_sessions/blob/master/CHANGELOG.md
|
99
|
+
source_code_uri: https://github.com/tuwukee/jwt_sessions
|
100
|
+
bug_tracker_uri: https://github.com/tuwukee/jwt_sessions/issues
|
101
|
+
post_install_message:
|
125
102
|
rdoc_options: []
|
126
103
|
require_paths:
|
127
104
|
- lib
|
@@ -136,8 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
113
|
- !ruby/object:Gem::Version
|
137
114
|
version: '0'
|
138
115
|
requirements: []
|
139
|
-
rubygems_version: 3.
|
140
|
-
signing_key:
|
116
|
+
rubygems_version: 3.2.5
|
117
|
+
signing_key:
|
141
118
|
specification_version: 4
|
142
119
|
summary: JWT Sessions
|
143
120
|
test_files:
|