jwt_sessions 2.4.2 → 2.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 603df513aaba9a73e5360895ea7fffddf52dd02e
4
- data.tar.gz: f70b523406ae364b05d16b45423d3b98971f4c75
2
+ SHA256:
3
+ metadata.gz: 8a485796c7aa19c00c79992111ed0135c0ec93af6e88aff80b233be779bb2301
4
+ data.tar.gz: d0b89a924589aa8450cfcaefb2061674db4d61cb32ed5ace92f1cab021db1fb1
5
5
  SHA512:
6
- metadata.gz: f937d9d3362f0ec7c63dc4af813038a3a0b49647a29e6f0338583ae106094cc9677bc83790ae330df3ef46d1159d441cab595cb62f274294a1fdc7af23ecb2cb
7
- data.tar.gz: fd37c7ec5f318893567dfce15ba8562c35a25b0ec8c53c12707947ba0f81e9d973144c0f153364ff1e3a92d17eab58c8a91ec1f4f67f0f84457334c83da38f8e
6
+ metadata.gz: e0bef3934719502d51f1299b210fad2df87360ef91cc3471384c3840870f3d548f43c5c5c1ff89a33e9e8847b706f9b7e1d2c240f288a21a16533369d8b0fce5
7
+ data.tar.gz: cd8b450b1a09e13290e3ac6ec5bcd17cf52c7314f01044ca1c2888321816585ff27eb7ae7b3935f5aa388fec3c11d9b8ebb0fe2c5fdde5a99cbc390932c4a646
data/CHANGELOG.md ADDED
@@ -0,0 +1,49 @@
1
+ ## 2.6.0 (June 01, 2021)
2
+
3
+ Features:
4
+
5
+ - added support for all Redis settings
6
+
7
+ Support:
8
+
9
+ - updated jwt to '>= 2.2.3'
10
+ - switched to redis scan when looking for keys
11
+ - removed extra gems from gemspec deps
12
+ - updated gems in dummy apps
13
+
14
+ ## 2.5.2 (July 06, 2020)
15
+
16
+ Bugfixes:
17
+
18
+ - fixed `Using the last argument as keyword parameters is deprecated;` warnings
19
+
20
+ ## 2.5.1 (April 20, 2020)
21
+
22
+ Features:
23
+
24
+ - added changelog
25
+
26
+ Bugfixes:
27
+
28
+ - fixed double exp key in payload
29
+
30
+ Support:
31
+
32
+ - moved decode error text to a constant within token class
33
+
34
+ ## 2.5.0 (April 12, 2020)
35
+
36
+ Features:
37
+
38
+ - added new error class `JWTSessions::Errors::Expired`
39
+
40
+ ## 2.4.3 (September 19, 2019)
41
+
42
+ Bugfixes:
43
+
44
+ - fixed lookup for refresh token for namespaced sessions
45
+
46
+ Support:
47
+
48
+ - updated sqlite to ~> 1.4 in `dummy_api`
49
+ - added 2.6.3 Ruby to CI
data/README.md CHANGED
@@ -15,10 +15,11 @@ XSS/CSRF safe JWT auth designed for SPA
15
15
  - [Rails integration](#rails-integration)
16
16
  - [Non-Rails usage](#non-rails-usage)
17
17
  - [Configuration](#configuration)
18
- - [Token store](#token-store)
19
- - [JWT signature](#jwt-signature)
20
- - [Request headers and cookies names](#request-headers-and-cookies-names)
21
- - [Expiration time](#expiration-time)
18
+ - [Token store](#token-store)
19
+ - [JWT signature](#jwt-signature)
20
+ - [Request headers and cookies names](#request-headers-and-cookies-names)
21
+ - [Expiration time](#expiration-time)
22
+ - [Exceptions](#exceptions)
22
23
  - [CSRF and cookies](#csrf-and-cookies)
23
24
  - [Refresh with access token](#refresh-with-access-token)
24
25
  - [Refresh token hijack protection](#refresh-token-hijack-protection)
@@ -368,6 +369,15 @@ JWTSessions.token_store = :redis, { redis_url: "redis://localhost:6397" }
368
369
 
369
370
  **NOTE:** if `REDIS_URL` environment variable is set it is used automatically.
370
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
+
371
381
  ##### JWT signature
372
382
 
373
383
  ```ruby
@@ -441,6 +451,15 @@ JWTSessions.refresh_exp_time = 604800 # 1 week in seconds
441
451
 
442
452
  It is defined globally, but can be overridden on a session level. See `JWTSessions::Session.new` options for more info.
443
453
 
454
+ ##### Exceptions
455
+
456
+ `JWTSessions::Errors::Error` - base class, all possible exceptions are inhereted from it. \
457
+ `JWTSessions::Errors::Malconfigured` - some required gem settings are empty, or methods are not implemented. \
458
+ `JWTSessions::Errors::InvalidPayload` - token's payload doesn't contain required keys or they are invalid. \
459
+ `JWTSessions::Errors::Unauthorized` - token can't be decoded or JWT claims are invalid. \
460
+ `JWTSessions::Errors::ClaimsVerification` - JWT claims are invalid (inherited from `JWTSessions::Errors::Unauthorized`). \
461
+ `JWTSessions::Errors::Expired` - token is expired (inherited from `JWTSessions::Errors::ClaimsVerification`).
462
+
444
463
  #### CSRF and cookies
445
464
 
446
465
  When you use cookies as your tokens transport it becomes vulnerable to CSRF. That is why both the login and refresh methods of the `Session` class produce CSRF tokens for you. `Authorization` mixin expects that this token is sent with all requests except GET and HEAD in a header specified among this gem's settings (`X-CSRF-Token` by default). Verification will be done automatically and the `Authorization` exception will be raised in case of a mismatch between the token from the header and the one stored in the session. \
@@ -455,7 +474,7 @@ session.masked_csrf(access_token)
455
474
 
456
475
  Sometimes it is not secure enough to store the refresh tokens in web / JS clients. \
457
476
  This is why you have the option to only use an access token and to not pass the refresh token to the client at all. \
458
- Session accepts `refresh_by_access_allowed: true` setting, which links the access token to the corresponding refresh token. \
477
+ Session accepts `refresh_by_access_allowed: true` setting, which links the access token to the corresponding refresh token.
459
478
 
460
479
  Example Rails login controller, which passes an access token token via cookies and renders CSRF:
461
480
 
@@ -490,7 +509,18 @@ tokens = session.refresh_by_access_payload
490
509
 
491
510
  In case of token forgery and successful refresh performed by an attacker the original user will have to logout. \
492
511
  To protect the endpoint use the before_action `authorize_refresh_by_access_request!`. \
493
- Refresh should be performed once the access token is already expired and we need to use the `claimless_payload` method in order to skip JWT expiration validation (and other claims) in order to proceed. \
512
+ Refresh should be performed once the access token is already expired and we need to use the `claimless_payload` method in order to skip JWT expiration validation (and other claims) in order to proceed.
513
+
514
+ Optionally `refresh_by_access_payload` accepts a block argument (the same way `refresh` method does).
515
+ The block will be called if the refresh action is performed before the access token is expired.
516
+ Thereby it's possible to prohibit users from making refresh calls while their access token is still active.
517
+
518
+ ```ruby
519
+ tokens = session.refresh_by_access_payload do
520
+ # here goes malicious activity alert
521
+ raise JWTSessions::Errors::Unauthorized, "Refresh action is performed before the expiration of the access token."
522
+ end
523
+ ```
494
524
 
495
525
  Example Rails refresh by access controller with cookies as token transport:
496
526
 
@@ -571,6 +601,13 @@ session = JWTSessions::Session.new(namespace: "ie-sessions")
571
601
  session.flush_namespaced # will flush all sessions which belong to the same namespace
572
602
  ```
573
603
 
604
+ Selectively flush one single session inside a namespace by its access token:
605
+
606
+ ```ruby
607
+ session = JWTSessions::Session.new(namespace: "ie-sessions", payload: payload)
608
+ session.flush_by_access_payload # will flush a specific session which belongs to an existing namespace
609
+ ```
610
+
574
611
  Flush access tokens only:
575
612
 
576
613
  ```ruby
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.constants - [:Unsupported]
159
- algos.map { |algo| JWT::Algos.const_get(algo)::SUPPORTED }.flatten + [NONE]
158
+ algos = JWT::Algos::ALGOS - [JWT::Algos::Unsupported]
159
+ algos.map { |algo| algo::SUPPORTED }.flatten + [NONE]
160
160
  end
161
161
  end
@@ -5,5 +5,6 @@ module JWTSessions
5
5
  class InvalidPayload < Error; end
6
6
  class Unauthorized < Error; end
7
7
  class ClaimsVerification < Unauthorized; end
8
+ class Expired < ClaimsVerification; end
8
9
  end
9
10
  end
@@ -37,7 +37,7 @@ module JWTSessions
37
37
  end
38
38
 
39
39
  def session_exists?(token, token_type = :access)
40
- send(:"#{token_type}_token_data", token)
40
+ send(:"#{token_type}_token_data", token, true)
41
41
  true
42
42
  rescue Errors::Unauthorized
43
43
  false
@@ -142,20 +142,20 @@ module JWTSessions
142
142
  end
143
143
 
144
144
  def refresh_csrf(refresh_token)
145
- refresh_token_instance = refresh_token_data(refresh_token)
145
+ refresh_token_instance = refresh_token_data(refresh_token, true)
146
146
  CSRFToken.new(refresh_token_instance.csrf)
147
147
  end
148
148
 
149
- def access_token_data(token)
149
+ def access_token_data(token, _first_match = false)
150
150
  uid = token_uid(token, :access, @access_claims)
151
151
  data = store.fetch_access(uid)
152
152
  raise Errors::Unauthorized, "Access token not found" if data.empty?
153
153
  data
154
154
  end
155
155
 
156
- def refresh_token_data(token)
156
+ def refresh_token_data(token, first_match = false)
157
157
  uid = token_uid(token, :refresh, @refresh_claims)
158
- retrieve_refresh_token(uid)
158
+ retrieve_refresh_token(uid, first_match: first_match)
159
159
  end
160
160
 
161
161
  def token_uid(token, type, claims)
@@ -177,8 +177,8 @@ module JWTSessions
177
177
  val
178
178
  end
179
179
 
180
- def retrieve_refresh_token(uid)
181
- @_refresh = RefreshToken.find(uid, store, namespace)
180
+ def retrieve_refresh_token(uid, first_match: false)
181
+ @_refresh = RefreshToken.find(uid, store, namespace, first_match: first_match)
182
182
  end
183
183
 
184
184
  def tokens_hash
@@ -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
@@ -11,7 +11,8 @@ module JWTSessions
11
11
  raise NotImplementedError
12
12
  end
13
13
 
14
- def fetch_refresh(_uid, _namespace)
14
+ # Set first_match to true to look up through all namespaces
15
+ def fetch_refresh(_uid, _namespace, _first_match)
15
16
  raise NotImplementedError
16
17
  end
17
18
 
@@ -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] = {} }
@@ -22,8 +22,16 @@ module JWTSessions
22
22
  storage[""]["access"].store(uid, access_token)
23
23
  end
24
24
 
25
- def fetch_refresh(uid, namespace, _first_match = false)
26
- value_if_not_expired(uid, "refresh", namespace.to_s)
25
+ def fetch_refresh(uid, namespace, first_match = false)
26
+ if first_match
27
+ storage.keys.each do |namespace_key|
28
+ val = value_if_not_expired(uid, "refresh", namespace_key)
29
+ return val unless val.empty?
30
+ end
31
+ {}
32
+ else
33
+ value_if_not_expired(uid, "refresh", namespace.to_s)
34
+ end
27
35
  end
28
36
 
29
37
  def persist_refresh(uid:, access_expiration:, access_uid:, csrf:, expiration:, namespace: "")
@@ -12,7 +12,7 @@ module JWTSessions
12
12
 
13
13
  begin
14
14
  require "redis"
15
- @storage = configure_redis_client(options)
15
+ @storage = configure_redis_client(**options)
16
16
  rescue LoadError => e
17
17
  msg = "Could not load the 'redis' gem, please add it to your gemfile or " \
18
18
  "configure a different adapter (e.g. JWTSessions.store_adapter = :memory)"
@@ -62,7 +62,7 @@ module JWTSessions
62
62
  end
63
63
 
64
64
  def all_refresh_tokens(namespace)
65
- keys_in_namespace = storage.keys(refresh_key("*", namespace))
65
+ keys_in_namespace = scan_keys(refresh_key("*", namespace))
66
66
  (keys_in_namespace || []).each_with_object({}) do |key, acc|
67
67
  uid = uid_from_key(key)
68
68
  acc[uid] = fetch_refresh(uid, namespace)
@@ -80,7 +80,7 @@ module JWTSessions
80
80
 
81
81
  private
82
82
 
83
- def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil)
83
+ def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil, **options)
84
84
  if redis_url && (redis_host || redis_port || redis_db_name)
85
85
  raise ArgumentError, "redis_url cannot be passed along with redis_host, redis_port or redis_db_name options"
86
86
  end
@@ -91,7 +91,7 @@ module JWTSessions
91
91
  redis_db_name: redis_db_name
92
92
  )
93
93
 
94
- Redis.new(url: redis_url)
94
+ Redis.new(options.merge(url: redis_url))
95
95
  end
96
96
 
97
97
  def build_redis_url(redis_host: nil, redis_port: nil, redis_db_name: nil)
@@ -111,7 +111,7 @@ module JWTSessions
111
111
 
112
112
  def first_refresh_key(uid)
113
113
  key = full_refresh_key(uid, "*")
114
- (storage.keys(key) || []).first
114
+ (scan_keys(key) || []).first
115
115
  end
116
116
 
117
117
  def refresh_key(uid, namespace)
@@ -126,6 +126,20 @@ module JWTSessions
126
126
  def uid_from_key(key)
127
127
  key.split("_").last
128
128
  end
129
+
130
+ def scan_keys(key_pattern)
131
+ cursor = 0
132
+ all_keys = []
133
+
134
+ loop do
135
+ cursor, keys = storage.scan(cursor, match: key_pattern, count: 1000)
136
+ all_keys |= keys
137
+
138
+ break if cursor == "0"
139
+ end
140
+
141
+ all_keys
142
+ end
129
143
  end
130
144
  end
131
145
  end
@@ -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)
@@ -13,23 +15,25 @@ module JWTSessions
13
15
  def decode(token, claims = {})
14
16
  decode_options = { algorithm: JWTSessions.algorithm }.merge(JWTSessions.jwt_options.to_h).merge(claims)
15
17
  JWT.decode(token, JWTSessions.public_key, JWTSessions.validate?, decode_options)
16
- rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::InvalidAudError, JWT::InvalidSubError, JWT::InvalidJtiError, JWT::ExpiredSignature => e
18
+ rescue JWT::ExpiredSignature => e
19
+ raise Errors::Expired, e.message
20
+ rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::InvalidAudError, JWT::InvalidSubError, JWT::InvalidJtiError => e
17
21
  raise Errors::ClaimsVerification, e.message
18
22
  rescue JWT::DecodeError => e
19
23
  raise Errors::Unauthorized, e.message
20
24
  rescue StandardError
21
- raise Errors::Unauthorized, "could not decode a token"
25
+ raise Errors::Unauthorized, DECODE_ERROR
22
26
  end
23
27
 
24
28
  def decode!(token)
25
29
  decode_options = { algorithm: JWTSessions.algorithm }
26
30
  JWT.decode(token, JWTSessions.public_key, false, decode_options)
27
31
  rescue StandardError
28
- raise Errors::Unauthorized, "could not decode a token"
32
+ raise Errors::Unauthorized, DECODE_ERROR
29
33
  end
30
34
 
31
35
  def meta
32
- { exp: JWTSessions.access_expiration }
36
+ { "exp" => JWTSessions.access_expiration }
33
37
  end
34
38
  end
35
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWTSessions
4
- VERSION = "2.4.2"
4
+ VERSION = "2.6.0"
5
5
  end
@@ -20,13 +20,18 @@ class TestRedisStoreAdapter < Minitest::Test
20
20
  end
21
21
  end
22
22
 
23
- def test_error_on_unknown_option
24
- assert_raises ArgumentError do
25
- JWTSessions::StoreAdapters::RedisStoreAdapter.new(
26
- redis_url: "redis://127.0.0.1:6379/0",
27
- something: "something"
28
- )
29
- end
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
@@ -69,6 +69,18 @@ class TestSession < Minitest::Test
69
69
  end
70
70
  end
71
71
 
72
+ def test_refresh_with_namespace
73
+ @new_session = JWTSessions::Session.new(
74
+ payload: payload,
75
+ namespace: "custom-namespace"
76
+ )
77
+ new_tokens = @new_session.login
78
+ refreshed_tokens = @new_session.refresh(new_tokens[:refresh])
79
+ decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
80
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
81
+ assert_equal payload[:test], decoded_access["test"]
82
+ end
83
+
72
84
  def test_refresh_by_access_payload
73
85
  session = JWTSessions::Session.new(payload: payload, refresh_by_access_allowed: true)
74
86
  session.login
@@ -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: Time.now.to_i - 20))
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: Time.now.to_i - 100))
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,8 +141,8 @@ class TestToken < Minitest::Test
141
141
  end
142
142
 
143
143
  def test_payload_exp_time
144
- token = JWTSessions::Token.encode(payload.merge(exp: Time.now.to_i - (3600 * 24)))
145
- assert_raises JWTSessions::Errors::Unauthorized do
144
+ token = JWTSessions::Token.encode(payload.merge("exp" => Time.now.to_i - (3600 * 24)))
145
+ assert_raises JWTSessions::Errors::Expired do
146
146
  JWTSessions::Token.decode(token)
147
147
  end
148
148
  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.2
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yulia Oletskaya
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-30 00:00:00.000000000 Z
11
+ date: 2021-06-01 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.1.1
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.1.1
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,7 +93,11 @@ files:
120
93
  homepage: http://rubygems.org/gems/jwt_sessions
121
94
  licenses:
122
95
  - MIT
123
- metadata: {}
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
124
101
  post_install_message:
125
102
  rdoc_options: []
126
103
  require_paths:
@@ -136,18 +113,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
113
  - !ruby/object:Gem::Version
137
114
  version: '0'
138
115
  requirements: []
139
- rubyforge_project:
140
- rubygems_version: 2.6.13
116
+ rubygems_version: 3.0.3
141
117
  signing_key:
142
118
  specification_version: 4
143
119
  summary: JWT Sessions
144
120
  test_files:
145
121
  - test/units/test_jwt_sessions.rb
146
122
  - test/units/test_token_store.rb
147
- - test/units/jwt_sessions/store_adapters/test_memory_store_adapter.rb
148
- - test/units/jwt_sessions/store_adapters/test_redis_store_adapter.rb
149
- - test/units/jwt_sessions/test_access_token.rb
150
123
  - test/units/jwt_sessions/test_csrf_token.rb
124
+ - test/units/jwt_sessions/test_access_token.rb
125
+ - test/units/jwt_sessions/store_adapters/test_redis_store_adapter.rb
126
+ - test/units/jwt_sessions/store_adapters/test_memory_store_adapter.rb
127
+ - test/units/jwt_sessions/test_token.rb
151
128
  - test/units/jwt_sessions/test_refresh_token.rb
152
129
  - test/units/jwt_sessions/test_session.rb
153
- - test/units/jwt_sessions/test_token.rb