jwt_sessions 1.0.2 → 1.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 +54 -10
- data/lib/jwt_sessions/authorization.rb +4 -2
- data/lib/jwt_sessions/session.rb +6 -4
- data/lib/jwt_sessions/token.rb +4 -13
- data/lib/jwt_sessions/version.rb +1 -1
- data/lib/jwt_sessions.rb +55 -18
- data/test/units/jwt_sessions/test_session.rb +1 -1
- data/test/units/jwt_sessions/test_token.rb +116 -8
- data/test/units/test_jwt_sessions.rb +5 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da93c28c622a3bac51b8f30be0190b0a3c79dda7
|
4
|
+
data.tar.gz: 29da1c13c80f4b89572d8f1c3ebe07dd031f775b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a21622dd516292d881f87a0b22f140b8dc7c17be838b200cfa7ccbcbb8a1a55a7e7bd075f3ea49982c06811a24fdda721f7625f3b821e4dfeff9583311942a2
|
7
|
+
data.tar.gz: b5897c5ef7e0a3be5db1dcc67df71fd0eb56f4a5f3ee5008782c881b006a34e139be045e6a30e7632f5231bf425c62de3b62b150a9e92895befa522802ef346b
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# jwt_sessions
|
2
2
|
[](https://badge.fury.io/rb/jwt_sessions)
|
3
3
|
[](https://codeclimate.com/github/tuwukee/jwt_sessions/maintainability)
|
4
|
+
[](https://www.codacy.com/app/tuwukee/jwt_sessions?utm_source=github.com&utm_medium=referral&utm_content=tuwukee/jwt_sessions&utm_campaign=Badge_Grade)
|
4
5
|
[](https://travis-ci.org/tuwukee/jwt_sessions)
|
5
6
|
|
6
7
|
XSS/CSRF safe JWT auth designed for SPA
|
@@ -77,9 +78,18 @@ Specify an encryption key for JSON Web Tokens in `config/initializers/jwt_sessio
|
|
77
78
|
It's adviced to store the key itself within the app secrets.
|
78
79
|
|
79
80
|
```ruby
|
81
|
+
JWTSessions.algorithm = 'HS256'
|
80
82
|
JWTSessions.encryption_key = Rails.application.secrets.secret_jwt_encryption_key
|
81
83
|
```
|
82
84
|
|
85
|
+
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 `encryotion_key` to sign the token. For other algorithms you must specify a private and public keys separately.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
JWTSessions.algorithm = 'RS256'
|
89
|
+
JWTSessions.private_key = OpenSSL::PKey::RSA.generate(2048)
|
90
|
+
JWTSessions.public_key = JWTSessions.private_key.public_key
|
91
|
+
```
|
92
|
+
|
83
93
|
Generate access/refresh/csrf tokens with a custom payload. \
|
84
94
|
The payload will be available in the controllers once the access (or refresh) token is authorized. \
|
85
95
|
Access/refresh tokens contain expiration time in their payload. Yet expiration times are also added to the output just in case.
|
@@ -290,9 +300,46 @@ JWTSessions.algorithm = 'HS256'
|
|
290
300
|
You need to specify a secret to use for HMAC, this setting doesn't have a default value.
|
291
301
|
|
292
302
|
```ruby
|
293
|
-
JWTSessions.
|
303
|
+
JWTSessions.encryption_key = 'secret'
|
304
|
+
```
|
305
|
+
|
306
|
+
If you are using another algorithm like RSA/ECDSA/EDDSA you should specify private and public keys.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
JWTSessions.private_key = 'abcd'
|
310
|
+
JWTSessions.public_key = 'efjh'
|
311
|
+
```
|
312
|
+
|
313
|
+
NOTE: ED25519 and HS512256 require rbnacl installation in order to make it work.
|
314
|
+
|
315
|
+
jwt_sessions only uses `exp` claim by default when it decodes tokens, you can specify which additional claims to use by
|
316
|
+
setting `jwt_options`. You can also specify leeway to account for clock skew.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
JWTSessions.jwt_options.verify_iss = true
|
320
|
+
JWTSessions.jwt_options.verify_sub = true
|
321
|
+
JWTSessions.jwt_options.verify_iat = true
|
322
|
+
JWTSessions.jwt_options.verify_aud = true
|
323
|
+
JWTSessions.jwt_options.leeway = 30 # seconds
|
294
324
|
```
|
295
325
|
|
326
|
+
To pass options like `sub`, `aud`, `iss`, or leeways you should specify a method called `token_claims` in your controller.
|
327
|
+
|
328
|
+
```ruby
|
329
|
+
class UsersController < ApplicationController
|
330
|
+
before_action :authorize_access_request!
|
331
|
+
|
332
|
+
def token_claims
|
333
|
+
{
|
334
|
+
aud: ['admin', 'staff'],
|
335
|
+
exp_leeway: 15 # will be used instead of default leeway only for exp claim
|
336
|
+
}
|
337
|
+
end
|
338
|
+
end
|
339
|
+
```
|
340
|
+
|
341
|
+
Claims are also supported by `JWTSessions::Session`, you can pass `access_claims` and `refresh_claims` options in the initializer
|
342
|
+
|
296
343
|
##### Request headers and cookies names
|
297
344
|
|
298
345
|
Default request headers/cookies names can be re-configured
|
@@ -307,7 +354,7 @@ JWTSessions.csrf_header = 'X-CSRF-Token'
|
|
307
354
|
|
308
355
|
##### Expiration time
|
309
356
|
|
310
|
-
|
357
|
+
Access token must have a short life span, while refresh tokens can be stored for a longer time period
|
311
358
|
|
312
359
|
```ruby
|
313
360
|
JWTSessions.access_exp_time = 3600 # 1 hour in seconds
|
@@ -341,17 +388,14 @@ session.refresh(refresh_token) { |refresh_token_uid, access_token_expiration| ..
|
|
341
388
|
|
342
389
|
## TODO
|
343
390
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
```ruby
|
348
|
-
JWTSessions.private_key = 'private_key'
|
349
|
-
JWTSessions.public_key = 'public_key_for_private'
|
350
|
-
```
|
391
|
+
Session cleanup by uid or refresh token instance. \
|
392
|
+
Refresh token namespaces to allow centralized token cleanup per namespace (scenarios like password reset). \
|
393
|
+
Documentation for code.
|
351
394
|
|
352
395
|
## Contributing
|
353
396
|
|
354
|
-
Fork & Pull Request
|
397
|
+
Fork & Pull Request \
|
398
|
+
RbNaCl and sodium cryptographic library are required for tests
|
355
399
|
|
356
400
|
## License
|
357
401
|
|
@@ -14,7 +14,8 @@ module JWTSessions
|
|
14
14
|
rescue Errors::Unauthorized
|
15
15
|
cookie_based_auth(token_type)
|
16
16
|
end
|
17
|
-
|
17
|
+
# triggers token decode and jwt claim checks
|
18
|
+
payload
|
18
19
|
check_csrf(token_type)
|
19
20
|
end
|
20
21
|
end
|
@@ -83,7 +84,8 @@ module JWTSessions
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def payload
|
86
|
-
|
87
|
+
claims = respond_to?(:token_claims) ? token_claims : {}
|
88
|
+
@_payload ||= Token.decode(found_token, claims).first
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|
data/lib/jwt_sessions/session.rb
CHANGED
@@ -9,6 +9,8 @@ module JWTSessions
|
|
9
9
|
@store = options.fetch(:store, JWTSessions.token_store)
|
10
10
|
@refresh_payload = options.fetch(:refresh_payload, {})
|
11
11
|
@payload = options.fetch(:payload, {})
|
12
|
+
@access_claims = options.fetch(:access_claims, {})
|
13
|
+
@refresh_claims = options.fetch(:refresh_claims, {})
|
12
14
|
end
|
13
15
|
|
14
16
|
def login
|
@@ -60,17 +62,17 @@ module JWTSessions
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def access_token_data(token)
|
63
|
-
uid = token_uid(token, :access)
|
65
|
+
uid = token_uid(token, :access, @access_claims)
|
64
66
|
store.fetch_access(uid)
|
65
67
|
end
|
66
68
|
|
67
69
|
def refresh_token_data(token)
|
68
|
-
uid = token_uid(token, :refresh)
|
70
|
+
uid = token_uid(token, :refresh, @refresh_claims)
|
69
71
|
retrieve_refresh_token(uid)
|
70
72
|
end
|
71
73
|
|
72
|
-
def token_uid(token, type)
|
73
|
-
token_payload = JWTSessions::Token.decode(token).first
|
74
|
+
def token_uid(token, type, claims)
|
75
|
+
token_payload = JWTSessions::Token.decode(token, claims).first
|
74
76
|
uid = token_payload.fetch('uid', nil)
|
75
77
|
if uid.nil?
|
76
78
|
message = "#{type.to_s.capitalize} token payload does not contain token uid"
|
data/lib/jwt_sessions/token.rb
CHANGED
@@ -7,30 +7,21 @@ module JWTSessions
|
|
7
7
|
class << self
|
8
8
|
def encode(payload)
|
9
9
|
exp_payload = meta.merge(payload)
|
10
|
-
JWT.encode(exp_payload, JWTSessions.
|
10
|
+
JWT.encode(exp_payload, JWTSessions.private_key, JWTSessions.algorithm)
|
11
11
|
end
|
12
12
|
|
13
|
-
def decode(token)
|
14
|
-
|
13
|
+
def decode(token, claims = {})
|
14
|
+
decode_options = { algorithm: JWTSessions.algorithm }.merge(JWTSessions.jwt_options.to_h).merge(claims)
|
15
|
+
JWT.decode(token, JWTSessions.public_key, JWTSessions.validate?, decode_options)
|
15
16
|
rescue JWT::DecodeError => e
|
16
17
|
raise Errors::Unauthorized, e.message
|
17
18
|
rescue StandardError
|
18
19
|
raise Errors::Unauthorized, 'could not decode a token'
|
19
20
|
end
|
20
21
|
|
21
|
-
def valid_payload?(payload)
|
22
|
-
!expired?(payload)
|
23
|
-
end
|
24
|
-
|
25
22
|
def meta
|
26
23
|
{ exp: JWTSessions.access_expiration }
|
27
24
|
end
|
28
|
-
|
29
|
-
def expired?(payload)
|
30
|
-
Time.at(payload['exp']) < Time.now
|
31
|
-
rescue StandardError
|
32
|
-
raise Errors::Unauthorized, 'invalid payload expiration time'
|
33
|
-
end
|
34
25
|
end
|
35
26
|
end
|
36
27
|
end
|
data/lib/jwt_sessions/version.rb
CHANGED
data/lib/jwt_sessions.rb
CHANGED
@@ -18,6 +18,10 @@ module JWTSessions
|
|
18
18
|
|
19
19
|
attr_writer :token_store
|
20
20
|
|
21
|
+
NONE = 'none'
|
22
|
+
|
23
|
+
JWTOptions = Struct.new(*JWT::DefaultOptions::DEFAULT_OPTIONS.keys)
|
24
|
+
|
21
25
|
DEFAULT_SETTINGS_KEYS = %i[access_cookie
|
22
26
|
access_exp_time
|
23
27
|
access_header
|
@@ -30,19 +34,19 @@ module JWTSessions
|
|
30
34
|
refresh_exp_time
|
31
35
|
refresh_header
|
32
36
|
token_prefix].freeze
|
33
|
-
DEFAULT_REDIS_HOST = '127.0.0.1'
|
34
|
-
DEFAULT_REDIS_PORT = '6379'
|
35
|
-
DEFAULT_REDIS_DB_NAME = 'jwtokens'
|
36
|
-
DEFAULT_TOKEN_PREFIX = 'jwt_'
|
37
|
-
DEFAULT_ALGORITHM = 'HS256'
|
38
|
-
DEFAULT_ACCESS_EXP_TIME = 3600 # 1 hour in seconds
|
39
|
-
DEFAULT_REFRESH_EXP_TIME = 604800 # 1 week in seconds
|
40
|
-
DEFAULT_ACCESS_COOKIE = 'jwt_access'
|
41
|
-
DEFAULT_ACCESS_HEADER = 'Authorization'
|
42
|
-
DEFAULT_REFRESH_COOKIE = 'jwt_refresh'
|
43
|
-
DEFAULT_REFRESH_HEADER = 'X-Refresh-Token'
|
44
|
-
DEFAULT_CSRF_HEADER = 'X-CSRF-Token'
|
45
37
|
|
38
|
+
DEFAULT_REDIS_HOST = '127.0.0.1'
|
39
|
+
DEFAULT_REDIS_PORT = '6379'
|
40
|
+
DEFAULT_REDIS_DB_NAME = 'jwtokens'
|
41
|
+
DEFAULT_TOKEN_PREFIX = 'jwt_'
|
42
|
+
DEFAULT_ALGORITHM = 'HS256'
|
43
|
+
DEFAULT_ACCESS_EXP_TIME = 3600 # 1 hour in seconds
|
44
|
+
DEFAULT_REFRESH_EXP_TIME = 604800 # 1 week in seconds
|
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'
|
46
50
|
|
47
51
|
DEFAULT_SETTINGS_KEYS.each do |setting|
|
48
52
|
var_name = :"@#{setting}"
|
@@ -61,13 +65,41 @@ module JWTSessions
|
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
68
|
+
def jwt_options
|
69
|
+
@jwt_options ||= JWTOptions.new(*JWT::DefaultOptions::DEFAULT_OPTIONS.values)
|
70
|
+
end
|
71
|
+
|
72
|
+
def algorithm=(algo)
|
73
|
+
raise Errors::Malconfigured, "algorithm #{algo} is not supported" unless supported_algos.include?(algo)
|
74
|
+
@algorithm = algo
|
75
|
+
end
|
76
|
+
|
64
77
|
def token_store
|
65
78
|
RedisTokenStore.instance(redis_host, redis_port, redis_db_name, token_prefix)
|
66
79
|
end
|
67
80
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
81
|
+
def validate?
|
82
|
+
algorithm != NONE
|
83
|
+
end
|
84
|
+
|
85
|
+
[:public_key, :private_key].each do |key|
|
86
|
+
var_name = :"@#{key}"
|
87
|
+
define_method("#{key}") do
|
88
|
+
return nil if algorithm == NONE
|
89
|
+
var = instance_variable_get(var_name)
|
90
|
+
raise Errors::Malconfigured, "#{key} is not specified" unless var
|
91
|
+
var
|
92
|
+
end
|
93
|
+
|
94
|
+
define_method("#{key}=") do |val|
|
95
|
+
instance_variable_set(var_name, val)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# should be used for hmac only
|
100
|
+
def encryption_key=(key)
|
101
|
+
@public_key = key
|
102
|
+
@private_key = key
|
71
103
|
end
|
72
104
|
|
73
105
|
def access_expiration
|
@@ -78,9 +110,6 @@ module JWTSessions
|
|
78
110
|
Time.now.to_i + refresh_exp_time.to_i
|
79
111
|
end
|
80
112
|
|
81
|
-
def encryption_key=(key)
|
82
|
-
@encryption_key = key
|
83
|
-
end
|
84
113
|
|
85
114
|
def header_by(token_type)
|
86
115
|
send("#{token_type}_header")
|
@@ -89,4 +118,12 @@ module JWTSessions
|
|
89
118
|
def cookie_by(token_type)
|
90
119
|
send("#{token_type}_cookie")
|
91
120
|
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def supported_algos
|
125
|
+
# TODO once ECDSA is fixed in ruby-jwt it can be added to the list of algos just the same way others are added
|
126
|
+
algos = JWT::Algos.constants - [:Unsupported, :Ecdsa]
|
127
|
+
algos.map { |algo| JWT::Algos.const_get(algo)::SUPPORTED }.flatten + [NONE, *JWT::Algos::Ecdsa::SUPPORTED.split(' ')]
|
128
|
+
end
|
92
129
|
end
|
@@ -39,10 +39,10 @@ class TestSession < Minitest::Test
|
|
39
39
|
JWTSessions.access_exp_time = 0
|
40
40
|
@session = JWTSessions::Session.new(payload: payload)
|
41
41
|
@tokens = session.login
|
42
|
+
JWTSessions.access_exp_time = 3600
|
42
43
|
refreshed_tokens = session.refresh(tokens[:refresh]) do
|
43
44
|
raise JWTSessions::Errors::Unauthorized
|
44
45
|
end
|
45
|
-
JWTSessions.access_exp_time = 3600
|
46
46
|
decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
|
47
47
|
assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
|
48
48
|
assert_equal payload[:test], decoded_access['test']
|
@@ -2,17 +2,128 @@
|
|
2
2
|
|
3
3
|
require 'minitest/autorun'
|
4
4
|
require 'jwt_sessions'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
nacl_supported_versions = {
|
8
|
+
'ruby' => ['~> 2.2.6', '~> 2.3.0', '~> 2.4.2'],
|
9
|
+
'jruby' => ['~> 9.1.6.0']
|
10
|
+
}.each_with_object([]) do |(platform, versions), acc|
|
11
|
+
acc.concat(versions.map { |version| Gem::Dependency.new(platform, version) })
|
12
|
+
end
|
13
|
+
|
14
|
+
nacl_supported = nacl_supported_versions.any? { |version| version.match?(RUBY_ENGINE, RUBY_VERSION) }
|
15
|
+
|
16
|
+
$uses_nacl = !!(defined?(RbNaCl) || require('rbnacl') if nacl_supported)
|
5
17
|
|
6
18
|
class TestToken < Minitest::Test
|
7
19
|
attr_reader :payload
|
8
20
|
|
9
21
|
def setup
|
10
|
-
JWTSessions.encryption_key = 'super secret'
|
11
22
|
@payload = { 'user_id' => 1, 'secret' => 'mystery' }
|
23
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
JWTSessions.algorithm = JWTSessions::DEFAULT_ALGORITHM
|
28
|
+
JWTSessions.instance_variable_set(:'@jwt_options', JWTSessions::JWTOptions.new(*JWT::DefaultOptions::DEFAULT_OPTIONS.values))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_rsa_token_decode
|
32
|
+
JWTSessions.algorithm = 'RS256'
|
33
|
+
JWTSessions.private_key = OpenSSL::PKey::RSA.generate 2048
|
34
|
+
JWTSessions.public_key = JWTSessions.private_key.public_key
|
35
|
+
|
36
|
+
token = JWTSessions::Token.encode(payload)
|
37
|
+
decoded = JWTSessions::Token.decode(token).first
|
38
|
+
assert_equal payload['user_id'], decoded['user_id']
|
39
|
+
assert_equal payload['secret'], decoded['secret']
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_eddsa_token_decode
|
43
|
+
skip unless $uses_nacl
|
44
|
+
JWTSessions.algorithm = 'ED25519'
|
45
|
+
JWTSessions.private_key = ::RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
|
46
|
+
JWTSessions.public_key = JWTSessions.private_key.verify_key
|
47
|
+
|
48
|
+
token = JWTSessions::Token.encode(payload)
|
49
|
+
decoded = JWTSessions::Token.decode(token).first
|
50
|
+
assert_equal payload['user_id'], decoded['user_id']
|
51
|
+
assert_equal payload['secret'], decoded['secret']
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_ecdsa_token_decode
|
55
|
+
JWTSessions.algorithm = 'ES256'
|
56
|
+
JWTSessions.private_key = OpenSSL::PKey::EC.new 'prime256v1'
|
57
|
+
JWTSessions.private_key.generate_key
|
58
|
+
JWTSessions.public_key = OpenSSL::PKey::EC.new JWTSessions.private_key
|
59
|
+
JWTSessions.public_key.private_key = nil
|
60
|
+
|
61
|
+
token = JWTSessions::Token.encode(payload)
|
62
|
+
decoded = JWTSessions::Token.decode(token).first
|
63
|
+
assert_equal payload['user_id'], decoded['user_id']
|
64
|
+
assert_equal payload['secret'], decoded['secret']
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_hmac_token_decode
|
68
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
69
|
+
token = JWTSessions::Token.encode(payload)
|
70
|
+
decoded = JWTSessions::Token.decode(token).first
|
71
|
+
assert_equal payload['user_id'], decoded['user_id']
|
72
|
+
assert_equal payload['secret'], decoded['secret']
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_token_sub_claim
|
76
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
77
|
+
JWTSessions.jwt_options.verify_sub = true
|
78
|
+
token = JWTSessions::Token.encode(payload.merge(sub: 'subject'))
|
79
|
+
decoded = JWTSessions::Token.decode(token, { sub: 'subject' }).first
|
80
|
+
assert_equal payload['user_id'], decoded['user_id']
|
81
|
+
assert_equal payload['secret'], decoded['secret']
|
82
|
+
assert_raises JWTSessions::Errors::Unauthorized do
|
83
|
+
JWTSessions::Token.decode(token, { sub: 'different subject' })
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_token_iss_claim
|
88
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
89
|
+
JWTSessions.jwt_options.verify_iss = true
|
90
|
+
token = JWTSessions::Token.encode(payload.merge(iss: 'Me'))
|
91
|
+
decoded = JWTSessions::Token.decode(token, { iss: 'Me' }).first
|
92
|
+
assert_equal payload['user_id'], decoded['user_id']
|
93
|
+
assert_equal payload['secret'], decoded['secret']
|
94
|
+
assert_raises JWTSessions::Errors::Unauthorized do
|
95
|
+
JWTSessions::Token.decode(token, { iss: 'Not Me' })
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_token_aud_claim
|
100
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
101
|
+
JWTSessions.jwt_options.verify_aud = true
|
102
|
+
token = JWTSessions::Token.encode(payload.merge(aud: ['young', 'old']))
|
103
|
+
decoded = JWTSessions::Token.decode(token, { aud: ['young'] }).first
|
104
|
+
assert_equal payload['user_id'], decoded['user_id']
|
105
|
+
assert_equal payload['secret'], decoded['secret']
|
106
|
+
assert_raises JWTSessions::Errors::Unauthorized do
|
107
|
+
JWTSessions::Token.decode(token, { aud: ['adult'] })
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_token_leeway_decode
|
112
|
+
JWTSessions.encryption_key = 'abcdefghijklmnopqrstuvwxyzABCDEF'
|
113
|
+
JWTSessions.jwt_options.leeway = 50
|
114
|
+
token = JWTSessions::Token.encode(payload.merge(exp: Time.now.to_i - 20))
|
115
|
+
decoded = JWTSessions::Token.decode(token).first
|
116
|
+
assert_equal payload['user_id'], decoded['user_id']
|
117
|
+
assert_equal payload['secret'], decoded['secret']
|
118
|
+
token = JWTSessions::Token.encode(payload.merge(exp: Time.now.to_i - 100))
|
119
|
+
assert_raises JWTSessions::Errors::Unauthorized do
|
120
|
+
JWTSessions::Token.decode(token)
|
121
|
+
end
|
12
122
|
end
|
13
123
|
|
14
|
-
def
|
15
|
-
|
124
|
+
def test_none_token_decode
|
125
|
+
JWTSessions.algorithm = JWTSessions::NONE
|
126
|
+
token = JWTSessions::Token.encode(payload)
|
16
127
|
decoded = JWTSessions::Token.decode(token).first
|
17
128
|
assert_equal payload['user_id'], decoded['user_id']
|
18
129
|
assert_equal payload['secret'], decoded['secret']
|
@@ -31,12 +142,9 @@ class TestToken < Minitest::Test
|
|
31
142
|
end
|
32
143
|
|
33
144
|
def test_payload_exp_time
|
145
|
+
token = JWTSessions::Token.encode(payload.merge(exp: Time.now.to_i - (3600 * 24)))
|
34
146
|
assert_raises JWTSessions::Errors::Unauthorized do
|
35
|
-
JWTSessions::Token.
|
147
|
+
JWTSessions::Token.decode(token)
|
36
148
|
end
|
37
|
-
payload['exp'] = Time.now - (3600 * 24)
|
38
|
-
assert_equal false, JWTSessions::Token.valid_payload?(payload)
|
39
|
-
payload['exp'] = Time.now + (3600 * 24)
|
40
|
-
assert_equal true, JWTSessions::Token.valid_payload?(payload)
|
41
149
|
end
|
42
150
|
end
|
@@ -21,7 +21,11 @@ class TestJWTSessions < Minitest::Test
|
|
21
21
|
def test_encryption_key
|
22
22
|
JWTSessions.encryption_key = nil
|
23
23
|
assert_raises JWTSessions::Errors::Malconfigured do
|
24
|
-
JWTSessions.
|
24
|
+
JWTSessions.private_key
|
25
|
+
end
|
26
|
+
|
27
|
+
assert_raises JWTSessions::Errors::Malconfigured do
|
28
|
+
JWTSessions.public_key
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_sessions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.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: 2018-04-
|
11
|
+
date: 2018-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
19
|
+
version: '2.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: redis
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '12.3'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '12.3'
|
97
97
|
description: XSS/CSRF safe JWT auth designed for SPA
|
98
98
|
email: yulia.oletskaya@gmail.com
|
99
99
|
executables: []
|