jwt_sessions 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/jwt_sessions.svg)](https://badge.fury.io/rb/jwt_sessions)
|
3
3
|
[![Maintainability](https://api.codeclimate.com/v1/badges/53de11b8334933b1c0ef/maintainability)](https://codeclimate.com/github/tuwukee/jwt_sessions/maintainability)
|
4
|
+
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c86efdfca81448919ec3e1c1e48fc152)](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
|
[![Build Status](https://travis-ci.org/tuwukee/jwt_sessions.svg?branch=master)](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: []
|