jwt_sessions 2.3.0 → 2.3.1
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/LICENSE +1 -1
- data/README.md +41 -41
- data/lib/jwt_sessions/access_token.rb +4 -4
- data/lib/jwt_sessions/authorization.rb +8 -8
- data/lib/jwt_sessions/refresh_token.rb +2 -2
- data/lib/jwt_sessions/session.rb +7 -7
- data/lib/jwt_sessions/store_adapters/memory_store_adapter.rb +10 -10
- data/lib/jwt_sessions/store_adapters/redis_store_adapter.rb +6 -6
- data/lib/jwt_sessions/token.rb +4 -4
- data/lib/jwt_sessions/version.rb +1 -1
- data/lib/jwt_sessions.rb +25 -25
- data/test/units/jwt_sessions/store_adapters/test_memory_store_adapter.rb +46 -46
- data/test/units/jwt_sessions/store_adapters/test_redis_store_adapter.rb +23 -23
- data/test/units/jwt_sessions/test_access_token.rb +3 -3
- data/test/units/jwt_sessions/test_csrf_token.rb +4 -4
- data/test/units/jwt_sessions/test_refresh_token.rb +3 -3
- data/test/units/jwt_sessions/test_session.rb +35 -24
- data/test/units/jwt_sessions/test_token.rb +48 -48
- data/test/units/test_jwt_sessions.rb +6 -6
- data/test/units/test_token_store.rb +7 -7
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea880100e93f25c3fc31e32161f6881a21c184cb
|
4
|
+
data.tar.gz: b549fea16c7d4ea09d8c1f7002a0d5cdd06c36b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78a5845fb2168f8ae8023d6ce6cc291ed2803fecefe2298afbc4423fc7319a29a7a75efd68fa2a17fc28e498a0b73d0c183ca1f1fb1f6005efc6430898acd92b
|
7
|
+
data.tar.gz: dc9074a35fee61a1749d42ec5bd3de687c683a8aa39c32c683eb58579b5831e735dc9904ff60c61b1add355ed61ddbb2686a74f171ceaa3922fcb737618f3bad
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -45,7 +45,7 @@ All tokens are encoded and decoded by [ruby-jwt](https://github.com/jwt/ruby-jwt
|
|
45
45
|
Put this line in your Gemfile
|
46
46
|
|
47
47
|
```ruby
|
48
|
-
gem
|
48
|
+
gem "jwt_sessions"
|
49
49
|
```
|
50
50
|
|
51
51
|
Then run
|
@@ -70,7 +70,7 @@ class ApplicationController < ActionController::API
|
|
70
70
|
private
|
71
71
|
|
72
72
|
def not_authorized
|
73
|
-
render json: { error:
|
73
|
+
render json: { error: "Not authorized" }, status: :unauthorized
|
74
74
|
end
|
75
75
|
end
|
76
76
|
```
|
@@ -79,14 +79,14 @@ Specify an encryption key for JSON Web Tokens in `config/initializers/jwt_sessio
|
|
79
79
|
It's adviced to store the key itself within the app secrets.
|
80
80
|
|
81
81
|
```ruby
|
82
|
-
JWTSessions.algorithm =
|
82
|
+
JWTSessions.algorithm = "HS256"
|
83
83
|
JWTSessions.encryption_key = Rails.application.secrets.secret_jwt_encryption_key
|
84
84
|
```
|
85
85
|
|
86
86
|
Most of the encryption algorithms require private and public keys to sign a token, yet HMAC only require a single key, so you can use a shortcat `encryption_key` to sign the token. For other algorithms you must specify a private and public keys separately.
|
87
87
|
|
88
88
|
```ruby
|
89
|
-
JWTSessions.algorithm =
|
89
|
+
JWTSessions.algorithm = "RS256"
|
90
90
|
JWTSessions.private_key = OpenSSL::PKey::RSA.generate(2048)
|
91
91
|
JWTSessions.public_key = JWTSessions.private_key.public_key
|
92
92
|
```
|
@@ -124,7 +124,7 @@ class LoginController < ApplicationController
|
|
124
124
|
session = JWTSessions::Session.new(payload: payload)
|
125
125
|
render json: session.login
|
126
126
|
else
|
127
|
-
render json:
|
127
|
+
render json: "Invalid user", status: :unauthorized
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
@@ -188,7 +188,7 @@ The `payload` method is available to fetch encoded data from the token.
|
|
188
188
|
|
189
189
|
```ruby
|
190
190
|
def current_user
|
191
|
-
@current_user ||= User.find(payload[
|
191
|
+
@current_user ||= User.find(payload["user_id"])
|
192
192
|
end
|
193
193
|
```
|
194
194
|
|
@@ -234,12 +234,12 @@ Example Sinatra app. \
|
|
234
234
|
NOTE: Since rack updates HTTP headers by using `HTTP_` prefix, upcasing and using underscores for sake of simplicity JWTSessions tokens header names are converted to rack-style in this example.
|
235
235
|
|
236
236
|
```ruby
|
237
|
-
require
|
237
|
+
require "sinatra/base"
|
238
238
|
|
239
|
-
JWTSessions.access_header =
|
240
|
-
JWTSessions.refresh_header =
|
241
|
-
JWTSessions.csrf_header =
|
242
|
-
JWTSessions.encryption_key =
|
239
|
+
JWTSessions.access_header = "authorization"
|
240
|
+
JWTSessions.refresh_header = "x_refresh_token"
|
241
|
+
JWTSessions.csrf_header = "x_csrf_token"
|
242
|
+
JWTSessions.encryption_key = "secret key"
|
243
243
|
|
244
244
|
class SimpleApp < Sinatra::Base
|
245
245
|
include JWTSessions::Authorization
|
@@ -257,28 +257,28 @@ class SimpleApp < Sinatra::Base
|
|
257
257
|
end
|
258
258
|
|
259
259
|
before do
|
260
|
-
content_type
|
260
|
+
content_type "application/json"
|
261
261
|
end
|
262
262
|
|
263
|
-
post
|
264
|
-
access_payload = { key:
|
265
|
-
refresh_payload = { key:
|
263
|
+
post "/login" do
|
264
|
+
access_payload = { key: "access value" }
|
265
|
+
refresh_payload = { key: "refresh value" }
|
266
266
|
session = JWTSessions::Session.new(payload: access_payload, refresh_payload: refresh_payload)
|
267
267
|
session.login.to_json
|
268
268
|
end
|
269
269
|
|
270
270
|
# POST /refresh
|
271
271
|
# x_refresh_token: ...
|
272
|
-
post
|
272
|
+
post "/refresh" do
|
273
273
|
authorize_refresh_request!
|
274
|
-
access_payload = { key:
|
274
|
+
access_payload = { key: "reloaded access value" }
|
275
275
|
session = JWTSessions::Session.new(payload: access_payload, refresh_payload: payload)
|
276
276
|
session.refresh(found_token).to_json
|
277
277
|
end
|
278
278
|
|
279
279
|
# GET /payload
|
280
280
|
# authorization: Bearer ...
|
281
|
-
get
|
281
|
+
get "/payload" do
|
282
282
|
authorize_access_request!
|
283
283
|
payload.to_json
|
284
284
|
end
|
@@ -298,18 +298,18 @@ In order to configure token store you should set up a store adapter in a followi
|
|
298
298
|
Memory store accepts only `prefix` (used for redis db keys). Here is a default configuration for Redis:
|
299
299
|
|
300
300
|
```ruby
|
301
|
-
|
302
|
-
redis_host:
|
303
|
-
redis_port:
|
304
|
-
redis_db_name:
|
305
|
-
token_prefix:
|
301
|
+
JWTSessions.token_store = :redis, {
|
302
|
+
redis_host: "127.0.0.1",
|
303
|
+
redis_port: "6379",
|
304
|
+
redis_db_name: "0",
|
305
|
+
token_prefix: "jwt_"
|
306
306
|
}
|
307
307
|
```
|
308
308
|
|
309
309
|
You can also provide a Redis URL instead:
|
310
310
|
|
311
311
|
```ruby
|
312
|
-
|
312
|
+
JWTSessions.token_store = :redis, { redis_url: "redis://localhost:6397" }
|
313
313
|
```
|
314
314
|
|
315
315
|
**NOTE:** if `REDIS_URL` environment variable is set it is used automatically.
|
@@ -317,20 +317,20 @@ JwtSessions.token_store = :redis, { redis_url: 'redis://localhost:6397' }
|
|
317
317
|
##### JWT signature
|
318
318
|
|
319
319
|
```ruby
|
320
|
-
JWTSessions.algorithm =
|
320
|
+
JWTSessions.algorithm = "HS256"
|
321
321
|
```
|
322
322
|
|
323
|
-
You need to specify a secret to use for HMAC, this setting doesn
|
323
|
+
You need to specify a secret to use for HMAC, this setting doesn"t have a default value.
|
324
324
|
|
325
325
|
```ruby
|
326
|
-
JWTSessions.encryption_key =
|
326
|
+
JWTSessions.encryption_key = "secret"
|
327
327
|
```
|
328
328
|
|
329
329
|
If you are using another algorithm like RSA/ECDSA/EDDSA you should specify private and public keys.
|
330
330
|
|
331
331
|
```ruby
|
332
|
-
JWTSessions.private_key =
|
333
|
-
JWTSessions.public_key =
|
332
|
+
JWTSessions.private_key = "abcd"
|
333
|
+
JWTSessions.public_key = "efjh"
|
334
334
|
```
|
335
335
|
|
336
336
|
NOTE: ED25519 and HS512256 require rbnacl installation in order to make it work.
|
@@ -354,7 +354,7 @@ class UsersController < ApplicationController
|
|
354
354
|
|
355
355
|
def token_claims
|
356
356
|
{
|
357
|
-
aud: [
|
357
|
+
aud: ["admin", "staff"],
|
358
358
|
exp_leeway: 15 # will be used instead of default leeway only for exp claim
|
359
359
|
}
|
360
360
|
end
|
@@ -368,11 +368,11 @@ Claims are also supported by `JWTSessions::Session`, you can pass `access_claims
|
|
368
368
|
Default request headers/cookies names can be re-configured
|
369
369
|
|
370
370
|
```ruby
|
371
|
-
JWTSessions.access_header =
|
372
|
-
JWTSessions.access_cookie =
|
373
|
-
JWTSessions.refresh_header =
|
374
|
-
JWTSessions.refresh_cookie =
|
375
|
-
JWTSessions.csrf_header =
|
371
|
+
JWTSessions.access_header = "Authorization"
|
372
|
+
JWTSessions.access_cookie = "jwt_access"
|
373
|
+
JWTSessions.refresh_header = "X-Refresh-Token"
|
374
|
+
JWTSessions.refresh_cookie = "jwt_refresh"
|
375
|
+
JWTSessions.csrf_header = "X-CSRF-Token"
|
376
376
|
```
|
377
377
|
|
378
378
|
##### Expiration time
|
@@ -417,7 +417,7 @@ class LoginController < ApplicationController
|
|
417
417
|
|
418
418
|
render json: { csrf: tokens[:csrf] }
|
419
419
|
else
|
420
|
-
render json:
|
420
|
+
render json: "Invalid email or password", status: :unauthorized
|
421
421
|
end
|
422
422
|
end
|
423
423
|
end
|
@@ -502,20 +502,20 @@ session.flush_by_uid(uid) # => 1
|
|
502
502
|
It's possible to group sessions by custom namespaces
|
503
503
|
|
504
504
|
```ruby
|
505
|
-
session = JWTSessions::Session.new(namespace:
|
505
|
+
session = JWTSessions::Session.new(namespace: "account-1")
|
506
506
|
```
|
507
507
|
|
508
508
|
and selectively flush sessions by namespace
|
509
509
|
|
510
510
|
```ruby
|
511
|
-
session = JWTSessions::Session.new(namespace:
|
511
|
+
session = JWTSessions::Session.new(namespace: "ie-sessions")
|
512
512
|
session.flush_namespaced # will flush all sessions which belong to the same namespace
|
513
513
|
```
|
514
514
|
|
515
515
|
it's posible to flush access tokens only
|
516
516
|
|
517
517
|
```ruby
|
518
|
-
session = JWTSessions::Session.new(namespace:
|
518
|
+
session = JWTSessions::Session.new(namespace: "ie-sessions")
|
519
519
|
session.flush_namespaced_access_tokens # will flush all access tokens which belong to the same namespace, but will keep refresh tokens
|
520
520
|
```
|
521
521
|
|
@@ -556,7 +556,7 @@ class LoginController < ApplicationController
|
|
556
556
|
|
557
557
|
render json: { access: tokens[:access], csrf: tokens[:csrf] }
|
558
558
|
else
|
559
|
-
render json:
|
559
|
+
render json: "Cannot login", status: :unauthorized
|
560
560
|
end
|
561
561
|
end
|
562
562
|
end
|
@@ -570,7 +570,7 @@ class RefreshController < ApplicationController
|
|
570
570
|
end
|
571
571
|
|
572
572
|
def access_payload
|
573
|
-
user = User.find_by!(email: payload[
|
573
|
+
user = User.find_by!(email: payload["user_id"])
|
574
574
|
{ user_id: user.id, role: user.role, permissions: user.permissions }
|
575
575
|
end
|
576
576
|
end
|
@@ -8,7 +8,7 @@ module JWTSessions
|
|
8
8
|
@csrf = csrf
|
9
9
|
@uid = uid
|
10
10
|
@expiration = expiration
|
11
|
-
@payload = payload.merge(
|
11
|
+
@payload = payload.merge("uid" => uid, "exp" => expiration.to_i)
|
12
12
|
@store = store
|
13
13
|
end
|
14
14
|
|
@@ -17,11 +17,11 @@ module JWTSessions
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def refresh_uid=(uid)
|
20
|
-
self.payload[
|
20
|
+
self.payload["ruid"] = uid
|
21
21
|
end
|
22
22
|
|
23
23
|
def refresh_uid
|
24
|
-
payload[
|
24
|
+
payload["ruid"]
|
25
25
|
end
|
26
26
|
|
27
27
|
def token
|
@@ -44,7 +44,7 @@ module JWTSessions
|
|
44
44
|
# and to retrieve session's CSRF token
|
45
45
|
def find(uid, store)
|
46
46
|
token_attrs = store.fetch_access(uid)
|
47
|
-
raise Errors::Unauthorized,
|
47
|
+
raise Errors::Unauthorized, "Access token not found" if token_attrs.empty?
|
48
48
|
build_with_token_attrs(store, uid, token_attrs)
|
49
49
|
end
|
50
50
|
|
@@ -67,15 +67,15 @@ module JWTSessions
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def request_headers
|
70
|
-
raise Errors::Malconfigured,
|
70
|
+
raise Errors::Malconfigured, "request_headers is not implemented"
|
71
71
|
end
|
72
72
|
|
73
73
|
def request_cookies
|
74
|
-
raise Errors::Malconfigured,
|
74
|
+
raise Errors::Malconfigured, "request_cookies is not implemented"
|
75
75
|
end
|
76
76
|
|
77
77
|
def request_method
|
78
|
-
raise Errors::Malconfigured,
|
78
|
+
raise Errors::Malconfigured, "request_method is not implemented"
|
79
79
|
end
|
80
80
|
|
81
81
|
def valid_csrf_token?(csrf_token, token_type)
|
@@ -98,20 +98,20 @@ module JWTSessions
|
|
98
98
|
|
99
99
|
def retrieve_csrf
|
100
100
|
token = request_headers[JWTSessions.csrf_header]
|
101
|
-
raise Errors::Unauthorized,
|
101
|
+
raise Errors::Unauthorized, "CSRF token is not found" unless token
|
102
102
|
token
|
103
103
|
end
|
104
104
|
|
105
105
|
def token_from_headers(token_type)
|
106
|
-
raw_token = request_headers[JWTSessions.header_by(token_type)] ||
|
107
|
-
token = raw_token.split(
|
108
|
-
raise Errors::Unauthorized,
|
106
|
+
raw_token = request_headers[JWTSessions.header_by(token_type)] || ""
|
107
|
+
token = raw_token.split(" ")[-1]
|
108
|
+
raise Errors::Unauthorized, "Token is not found" unless token
|
109
109
|
token
|
110
110
|
end
|
111
111
|
|
112
112
|
def token_from_cookies(token_type)
|
113
113
|
token = request_cookies[JWTSessions.cookie_by(token_type)]
|
114
|
-
raise Errors::Unauthorized,
|
114
|
+
raise Errors::Unauthorized, "Token is not found" unless token
|
115
115
|
token
|
116
116
|
end
|
117
117
|
|
@@ -16,7 +16,7 @@ module JWTSessions
|
|
16
16
|
@uid = options.fetch(:uid, SecureRandom.uuid)
|
17
17
|
@expiration = options.fetch(:expiration, JWTSessions.refresh_expiration)
|
18
18
|
@namespace = options.fetch(:namespace, nil)
|
19
|
-
@token = Token.encode(options.fetch(:payload, {}).merge(
|
19
|
+
@token = Token.encode(options.fetch(:payload, {}).merge("uid" => uid, "exp" => expiration.to_i))
|
20
20
|
end
|
21
21
|
|
22
22
|
class << self
|
@@ -35,7 +35,7 @@ module JWTSessions
|
|
35
35
|
|
36
36
|
def find(uid, store, namespace = nil)
|
37
37
|
token_attrs = store.fetch_refresh(uid, namespace)
|
38
|
-
raise Errors::Unauthorized,
|
38
|
+
raise Errors::Unauthorized, "Refresh token not found" if token_attrs.empty?
|
39
39
|
build_with_token_attrs(store, uid, token_attrs, namespace)
|
40
40
|
end
|
41
41
|
|
data/lib/jwt_sessions/session.rb
CHANGED
@@ -52,7 +52,7 @@ module JWTSessions
|
|
52
52
|
|
53
53
|
def refresh_by_access_payload(&block)
|
54
54
|
raise Errors::InvalidPayload if payload.nil?
|
55
|
-
ruid = retrieve_val_from(payload, :access,
|
55
|
+
ruid = retrieve_val_from(payload, :access, "ruid", "refresh uid")
|
56
56
|
retrieve_refresh_token(ruid)
|
57
57
|
|
58
58
|
check_access_uid_within_refresh_token(&block) if block_given?
|
@@ -62,7 +62,7 @@ module JWTSessions
|
|
62
62
|
|
63
63
|
def flush_by_access_payload
|
64
64
|
raise Errors::InvalidPayload if payload.nil?
|
65
|
-
ruid = retrieve_val_from(payload, :access,
|
65
|
+
ruid = retrieve_val_from(payload, :access, "ruid", "refresh uid")
|
66
66
|
flush_by_uid(ruid)
|
67
67
|
end
|
68
68
|
|
@@ -109,8 +109,8 @@ module JWTSessions
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def valid_access_request?(external_csrf_token, external_payload)
|
112
|
-
ruid = retrieve_val_from(external_payload, :access,
|
113
|
-
uid = retrieve_val_from(external_payload, :access,
|
112
|
+
ruid = retrieve_val_from(external_payload, :access, "ruid", "refresh uid")
|
113
|
+
uid = retrieve_val_from(external_payload, :access, "uid", "access uid")
|
114
114
|
|
115
115
|
refresh_token = RefreshToken.find(ruid, JWTSessions.token_store)
|
116
116
|
return false unless uid == refresh_token.access_uid
|
@@ -147,7 +147,7 @@ module JWTSessions
|
|
147
147
|
def access_token_data(token)
|
148
148
|
uid = token_uid(token, :access, @access_claims)
|
149
149
|
data = store.fetch_access(uid)
|
150
|
-
raise Errors::Unauthorized,
|
150
|
+
raise Errors::Unauthorized, "Access token not found" if data.empty?
|
151
151
|
data
|
152
152
|
end
|
153
153
|
|
@@ -158,7 +158,7 @@ module JWTSessions
|
|
158
158
|
|
159
159
|
def token_uid(token, type, claims)
|
160
160
|
token_payload = JWTSessions::Token.decode(token, claims).first
|
161
|
-
uid = token_payload.fetch(
|
161
|
+
uid = token_payload.fetch("uid", nil)
|
162
162
|
if uid.nil?
|
163
163
|
message = "#{type.to_s.capitalize} token payload does not contain token uid"
|
164
164
|
raise Errors::InvalidPayload, message
|
@@ -204,7 +204,7 @@ module JWTSessions
|
|
204
204
|
end
|
205
205
|
|
206
206
|
def check_access_uid_within_refresh_token
|
207
|
-
uid = retrieve_val_from(payload, :access,
|
207
|
+
uid = retrieve_val_from(payload, :access, "uid", "access uid")
|
208
208
|
access_uid = @_refresh.access_uid
|
209
209
|
return if access_uid.size.zero?
|
210
210
|
yield @_refresh.uid, @_refresh.access_expiration if access_uid != uid
|
@@ -13,20 +13,20 @@ module JWTSessions
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def fetch_access(uid)
|
16
|
-
access_token = value_if_not_expired(uid,
|
16
|
+
access_token = value_if_not_expired(uid, "access", "")
|
17
17
|
access_token.empty? ? {} : { csrf: access_token[:csrf] }
|
18
18
|
end
|
19
19
|
|
20
20
|
def persist_access(uid, csrf, expiration)
|
21
21
|
access_token = { csrf: csrf, expiration: expiration }
|
22
|
-
storage[
|
22
|
+
storage[""]["access"].store(uid, access_token)
|
23
23
|
end
|
24
24
|
|
25
25
|
def fetch_refresh(uid, namespace)
|
26
|
-
value_if_not_expired(uid,
|
26
|
+
value_if_not_expired(uid, "refresh", namespace.to_s)
|
27
27
|
end
|
28
28
|
|
29
|
-
def persist_refresh(uid:, access_expiration:, access_uid:, csrf:, expiration:, namespace:
|
29
|
+
def persist_refresh(uid:, access_expiration:, access_uid:, csrf:, expiration:, namespace: "")
|
30
30
|
update_refresh_fields(
|
31
31
|
uid,
|
32
32
|
namespace.to_s,
|
@@ -37,7 +37,7 @@ module JWTSessions
|
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
40
|
-
def update_refresh(uid:, access_expiration:, access_uid:, csrf:, namespace:
|
40
|
+
def update_refresh(uid:, access_expiration:, access_uid:, csrf:, namespace: "")
|
41
41
|
update_refresh_fields(
|
42
42
|
uid,
|
43
43
|
namespace.to_s,
|
@@ -51,16 +51,16 @@ module JWTSessions
|
|
51
51
|
namespace_keys = namespace.nil? ? storage.keys : [namespace]
|
52
52
|
|
53
53
|
namespace_keys.each_with_object({}) do |namespace_key, acc|
|
54
|
-
select_keys(storage[namespace_key][
|
54
|
+
select_keys(storage[namespace_key]["refresh"], acc)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def destroy_refresh(uid, namespace)
|
59
|
-
storage[namespace.to_s][
|
59
|
+
storage[namespace.to_s]["refresh"].delete(uid)
|
60
60
|
end
|
61
61
|
|
62
62
|
def destroy_access(uid)
|
63
|
-
storage[
|
63
|
+
storage[""]["access"].delete(uid)
|
64
64
|
end
|
65
65
|
|
66
66
|
private
|
@@ -71,8 +71,8 @@ module JWTSessions
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def update_refresh_fields(key, namespace, fields)
|
74
|
-
updated_refresh = value_if_not_expired(key,
|
75
|
-
storage[namespace][
|
74
|
+
updated_refresh = value_if_not_expired(key, "refresh", namespace).merge(fields)
|
75
|
+
storage[namespace]["refresh"].store(key, updated_refresh)
|
76
76
|
end
|
77
77
|
|
78
78
|
def select_keys(keys_hash, acc)
|
@@ -11,7 +11,7 @@ module JWTSessions
|
|
11
11
|
@prefix = token_prefix
|
12
12
|
|
13
13
|
begin
|
14
|
-
require
|
14
|
+
require "redis"
|
15
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 " \
|
@@ -61,7 +61,7 @@ module JWTSessions
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def all_refresh_tokens(namespace)
|
64
|
-
keys_in_namespace = storage.keys(refresh_key(
|
64
|
+
keys_in_namespace = storage.keys(refresh_key("*", namespace))
|
65
65
|
(keys_in_namespace || []).each_with_object({}) do |key, acc|
|
66
66
|
uid = uid_from_key(key)
|
67
67
|
acc[uid] = fetch_refresh(uid, namespace)
|
@@ -80,7 +80,7 @@ module JWTSessions
|
|
80
80
|
|
81
81
|
def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil)
|
82
82
|
if redis_url && (redis_host || redis_port || redis_db_name)
|
83
|
-
raise ArgumentError,
|
83
|
+
raise ArgumentError, "redis_url cannot be passed along with redis_host, redis_port or redis_db_name options"
|
84
84
|
end
|
85
85
|
|
86
86
|
redis_url ||= build_redis_url(
|
@@ -99,7 +99,7 @@ module JWTSessions
|
|
99
99
|
redis_host ||= JWTSessions.redis_host
|
100
100
|
redis_port ||= JWTSessions.redis_port
|
101
101
|
|
102
|
-
redis_base_url = ENV[
|
102
|
+
redis_base_url = ENV["REDIS_URL"] || "redis://#{redis_host}:#{redis_port}"
|
103
103
|
URI.join(redis_base_url, redis_db_name).to_s
|
104
104
|
end
|
105
105
|
|
@@ -116,7 +116,7 @@ module JWTSessions
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def wildcard_refresh_key(uid)
|
119
|
-
(storage.keys(refresh_key(uid,
|
119
|
+
(storage.keys(refresh_key(uid, "*")) || []).first
|
120
120
|
end
|
121
121
|
|
122
122
|
def access_key(uid)
|
@@ -124,7 +124,7 @@ module JWTSessions
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def uid_from_key(key)
|
127
|
-
key.split(
|
127
|
+
key.split("_").last
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
data/lib/jwt_sessions/token.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "jwt"
|
4
4
|
|
5
5
|
module JWTSessions
|
6
6
|
class Token
|
@@ -13,19 +13,19 @@ module JWTSessions
|
|
13
13
|
def decode(token, claims = {})
|
14
14
|
decode_options = { algorithm: JWTSessions.algorithm }.merge(JWTSessions.jwt_options.to_h).merge(claims)
|
15
15
|
JWT.decode(token, JWTSessions.public_key, JWTSessions.validate?, decode_options)
|
16
|
-
rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::InvalidAudError, JWT::InvalidSubError, JWT::InvalidJtiError => e
|
16
|
+
rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::InvalidAudError, JWT::InvalidSubError, JWT::InvalidJtiError, JWT::ExpiredSignature => e
|
17
17
|
raise Errors::ClaimsVerification, e.message
|
18
18
|
rescue JWT::DecodeError => e
|
19
19
|
raise Errors::Unauthorized, e.message
|
20
20
|
rescue StandardError
|
21
|
-
raise Errors::Unauthorized,
|
21
|
+
raise Errors::Unauthorized, "could not decode a token"
|
22
22
|
end
|
23
23
|
|
24
24
|
def decode!(token)
|
25
25
|
decode_options = { algorithm: JWTSessions.algorithm }
|
26
26
|
JWT.decode(token, JWTSessions.public_key, false, decode_options)
|
27
27
|
rescue StandardError
|
28
|
-
raise Errors::Unauthorized,
|
28
|
+
raise Errors::Unauthorized, "could not decode a token"
|
29
29
|
end
|
30
30
|
|
31
31
|
def meta
|
data/lib/jwt_sessions/version.rb
CHANGED
data/lib/jwt_sessions.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
3
|
+
require "securerandom"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
require "jwt_sessions/errors"
|
7
|
+
require "jwt_sessions/token"
|
8
|
+
require "jwt_sessions/refresh_token"
|
9
|
+
require "jwt_sessions/csrf_token"
|
10
|
+
require "jwt_sessions/access_token"
|
11
|
+
require "jwt_sessions/session"
|
12
|
+
require "jwt_sessions/authorization"
|
13
|
+
require "jwt_sessions/rails_authorization" if defined?(::Rails)
|
14
|
+
require "jwt_sessions/version"
|
15
|
+
require "jwt_sessions/store_adapters"
|
16
16
|
|
17
17
|
module JWTSessions
|
18
18
|
extend self
|
19
19
|
|
20
20
|
attr_accessor :redis_url
|
21
21
|
|
22
|
-
NONE =
|
22
|
+
NONE = "none"
|
23
23
|
|
24
24
|
JWTOptions = Struct.new(*JWT::DefaultOptions::DEFAULT_OPTIONS.keys)
|
25
25
|
|
@@ -35,18 +35,18 @@ module JWTSessions
|
|
35
35
|
refresh_header
|
36
36
|
token_prefix].freeze
|
37
37
|
|
38
|
-
DEFAULT_REDIS_HOST =
|
39
|
-
DEFAULT_REDIS_PORT =
|
40
|
-
DEFAULT_REDIS_DB_NAME =
|
41
|
-
DEFAULT_TOKEN_PREFIX =
|
42
|
-
DEFAULT_ALGORITHM =
|
38
|
+
DEFAULT_REDIS_HOST = "127.0.0.1"
|
39
|
+
DEFAULT_REDIS_PORT = "6379"
|
40
|
+
DEFAULT_REDIS_DB_NAME = "0"
|
41
|
+
DEFAULT_TOKEN_PREFIX = "jwt_"
|
42
|
+
DEFAULT_ALGORITHM = "HS256"
|
43
43
|
DEFAULT_ACCESS_EXP_TIME = 3600 # 1 hour in seconds
|
44
44
|
DEFAULT_REFRESH_EXP_TIME = 604800 # 1 week in seconds
|
45
|
-
DEFAULT_ACCESS_COOKIE =
|
46
|
-
DEFAULT_ACCESS_HEADER =
|
47
|
-
DEFAULT_REFRESH_COOKIE =
|
48
|
-
DEFAULT_REFRESH_HEADER =
|
49
|
-
DEFAULT_CSRF_HEADER =
|
45
|
+
DEFAULT_ACCESS_COOKIE = "jwt_access"
|
46
|
+
DEFAULT_ACCESS_HEADER = "Authorization"
|
47
|
+
DEFAULT_REFRESH_COOKIE = "jwt_refresh"
|
48
|
+
DEFAULT_REFRESH_HEADER = "X-Refresh-Token"
|
49
|
+
DEFAULT_CSRF_HEADER = "X-CSRF-Token"
|
50
50
|
|
51
51
|
DEFAULT_SETTINGS_KEYS.each do |setting|
|
52
52
|
var_name = :"@#{setting}"
|
@@ -149,6 +149,6 @@ module JWTSessions
|
|
149
149
|
def supported_algos
|
150
150
|
# TODO once ECDSA is fixed in ruby-jwt it can be added to the list of algos just the same way others are added
|
151
151
|
algos = JWT::Algos.constants - [:Unsupported, :Ecdsa]
|
152
|
-
algos.map { |algo| JWT::Algos.const_get(algo)::SUPPORTED }.flatten + [NONE, *JWT::Algos::Ecdsa::SUPPORTED.split(
|
152
|
+
algos.map { |algo| JWT::Algos.const_get(algo)::SUPPORTED }.flatten + [NONE, *JWT::Algos::Ecdsa::SUPPORTED.split(" ")]
|
153
153
|
end
|
154
154
|
end
|