jwt_sessions 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d92af5c82b767203a340f54627281a85eb5576ed
4
- data.tar.gz: dce1a37bba83687b2815e757475d00e3f0a21fef
3
+ metadata.gz: 7b04e83f8dac3d2546364665988101a1a1fd6193
4
+ data.tar.gz: 9a84c2705ec41817a6ed79a5bdface8f5da48dba
5
5
  SHA512:
6
- metadata.gz: cc9b3e0a4bea5bab7560c38d8cb24b15b94f289c82acc9581206af29eed9890f0aa4987c126a59540ed6b1f02d126159714212e4fb08f6d470232d8f08b3eeb8
7
- data.tar.gz: f689a7cc6c5de3bf0d8ffec6bc3d20d0abc86b1a71ab85b33a782c5a1fad491baaaedf449032e62547bd2b8fdc7a833d7f5ecb569d476077abec70dc3a3c354b
6
+ metadata.gz: fc621aa40e3c9f4b5807655ee4198f8881cdb00814d32fa159fbf815f1061c47190456316606b67e4c4743b13a2c74a7fb1b1bea99219f8b4207beb693f263a2
7
+ data.tar.gz: c1bfcd3b9198dbdc641aa71dffaa8d0f9a1866dcb3ed800e690b967466a6f0d509daece7a3f58f45104d763aa4a7aafad0cb136608f91659f4d13db2fa64fc9a
data/README.md CHANGED
@@ -34,13 +34,11 @@ Main goal of this gem is to provide configurable, manageable, and safe stateful
34
34
 
35
35
  It's designed to be framework agnostic yet is easily integrable so Rails integration is also available out of the box.
36
36
 
37
- Core concept behind jwt_sessions is that each session is represented by a pair of tokens: access and refresh,
38
- and a session store used to handle CSRF checks and refresh token hijacking. Default token store is based on redis
39
- but you can freely implement your own store with whichever backend you prefer.
37
+ Core concept behind `jwt_sessions` is that each session is represented by a pair of tokens: access and refresh, and a session store is used to handle CSRF checks and refresh token hijacking. Both tokens have configurable expiration times, but in general refresh token is supposed to have a longer lifespan than an access token. Access token is used to retrieve secured resources and refresh token is used to renew the access token once it's expired. Default token store is based on redis.
38
+
39
+ All tokens are encoded and decoded by [ruby-jwt](https://github.com/jwt/ruby-jwt) gem, and its reserved claim names are supported as well as it's allowed to configure claim checks and cryptographic signing algorithms supported by it.
40
+ `jwt_sessions` itself uses `ext` claim and `HS256` signing by default.
40
41
 
41
- All tokens are encoded and decoded by [ruby-jwt](https://github.com/jwt/ruby-jwt) gem, and its reserved claim names are supported
42
- as well as it's allowed to configure claim checks and cryptographic signing algorithms supported by it.
43
- jwt_sessions itself uses `ext` claim and `HS256` signing by default.
44
42
 
45
43
  ## Installation
46
44
 
@@ -58,7 +56,7 @@ bundle install
58
56
 
59
57
  ## Getting Started
60
58
 
61
- `Authorization` mixin is supposed to be included in your controllers and is used to retrieve access and refresh tokens from incoming requests and verify CSRF token if needed.
59
+ `Authorization` mixin is supposed to be included in your controllers and is used to retrieve access and refresh tokens from incoming requests and verify CSRF token if needed. It assumes that a token is either in a cookie or in a header (cookie and header names are configurable). It tries to retrieve it from headers first, then from cookies (CSRF check included) if the headers check failed.
62
60
 
63
61
  ### Rails integration
64
62
 
@@ -139,7 +137,7 @@ session = JWTSessions::Session.new(payload: payload, refresh_payload: refresh_pa
139
137
  ```
140
138
 
141
139
  Now you can build a refresh endpoint. To protect the endpoint use before_action `authorize_refresh_request!`. \
142
- In the example `found_token` - is a token fetched from request headers or cookies.
140
+ The endpoint itself should return a renewed access token.
143
141
 
144
142
  ```ruby
145
143
  class RefreshController < ApplicationController
@@ -156,7 +154,7 @@ class RefreshController < ApplicationController
156
154
  end
157
155
  end
158
156
  ```
159
-
157
+ In the example `found_token` - is a token fetched from request headers or cookies. In the context of `RefreshController` it's a refresh token. \
160
158
  The refresh request with headers must include `X-Refresh-Token` (header name is configurable) with refresh token.
161
159
 
162
160
  ```
@@ -275,7 +273,7 @@ class SimpleApp < Sinatra::Base
275
273
  payload.to_json
276
274
  end
277
275
 
278
- ....
276
+ # ...
279
277
  end
280
278
  ```
281
279
 
@@ -466,7 +464,10 @@ Flush a session by its access token.
466
464
  ```ruby
467
465
  session = JWTSessions::Session.new(refresh_by_access_allowed: true)
468
466
  tokens = session.login
469
- session.flush_by_access_token(tokens[:access]) # => 1
467
+ session.flush_by_access_payload
468
+ # or
469
+ session = JWTSessions::Session.new(refresh_by_access_allowed: true, payload: payload)
470
+ session.flush_by_access_payload
470
471
  ```
471
472
 
472
473
  Or by refresh token UID
@@ -514,6 +515,52 @@ To logout with an access token `refresh_by_access_allowed` setting should be set
514
515
  [Rails API](test/support/dummy_api) \
515
516
  [Sinatra API](test/support/dummy_sinatra_api)
516
517
 
518
+ You can use a mixed approach for the cases when you'd like to store an access token in localStorage and refresh token in HTTP-only secure cookies. \
519
+ Rails controllers setup example:
520
+
521
+ ```ruby
522
+ class LoginController < ApplicationController
523
+ def create
524
+ user = User.find_by(email: params[:email])
525
+ if user&.authenticate(params[:password])
526
+
527
+ payload = { user_id: user.id, role: user.role, permissions: user.permissions }
528
+ refresh_payload = { user_id: user.id }
529
+ session = JWTSessions::Session.new(payload: payload, refresh_payload: refresh_payload)
530
+ tokens = session.login
531
+ response.set_cookie(JWTSessions.refresh_cookie,
532
+ value: tokens[:refresh],
533
+ httponly: true,
534
+ secure: Rails.env.production?)
535
+
536
+ render json: { access: tokens[:access], csrf: tokens[:csrf] }
537
+ else
538
+ render json: 'Cannot login', status: :unauthorized
539
+ end
540
+ end
541
+ end
542
+
543
+ class RefreshController < ApplicationController
544
+ before_action :authorize_refresh_request!
545
+
546
+ def create
547
+ tokens = JWTSessions::Session.new(payload: access_payload).refresh(found_token)
548
+ render json: { access: tokens[:access], csrf: tokens[:csrf] }
549
+ end
550
+
551
+ def access_payload
552
+ user = User.find_by!(email: payload['user_id'])
553
+ { user_id: user.id, role: user.role, permissions: user.permissions }
554
+ end
555
+ end
556
+
557
+ class ResourcesController < ApplicationController
558
+ before_action :authorize_access_request!
559
+ before_action :validate_role_and_permissions_from_payload
560
+
561
+ # ...
562
+ end
563
+ ```
517
564
 
518
565
  ## Contributing
519
566
 
@@ -187,6 +187,14 @@ module JWTSessions
187
187
  }
188
188
  end
189
189
 
190
+ def refresh_tokens_hash
191
+ {
192
+ csrf: csrf_token,
193
+ access: access_token,
194
+ access_expires_at: Time.at(@_access.expiration.to_i)
195
+ }
196
+ end
197
+
190
198
  def check_refresh_on_time
191
199
  expiration = @_refresh.access_expiration
192
200
  yield @_refresh.uid, expiration if expiration.to_i > Time.now.to_i
@@ -197,7 +205,7 @@ module JWTSessions
197
205
  create_access_token
198
206
  update_refresh_token
199
207
 
200
- tokens_hash
208
+ refresh_tokens_hash
201
209
  end
202
210
 
203
211
  def update_refresh_token
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWTSessions
4
- VERSION = '2.2.0'
4
+ VERSION = '2.2.1'
5
5
  end
@@ -5,7 +5,8 @@ require 'jwt_sessions'
5
5
 
6
6
  class TestSession < Minitest::Test
7
7
  attr_reader :session, :payload, :tokens
8
- EXPECTED_KEYS = %i[access access_expires_at csrf refresh refresh_expires_at].freeze
8
+ LOGIN_KEYS = %i[access access_expires_at csrf refresh refresh_expires_at].freeze
9
+ REFRESH_KEYS = %i[access access_expires_at csrf].freeze
9
10
 
10
11
  def setup
11
12
  JWTSessions.encryption_key = 'encrypted'
@@ -22,14 +23,14 @@ class TestSession < Minitest::Test
22
23
 
23
24
  def test_login
24
25
  decoded_access = JWTSessions::Token.decode(tokens[:access]).first
25
- assert_equal EXPECTED_KEYS, tokens.keys.sort
26
+ assert_equal LOGIN_KEYS, tokens.keys.sort
26
27
  assert_equal payload[:test], decoded_access['test']
27
28
  end
28
29
 
29
30
  def test_refresh
30
31
  refreshed_tokens = session.refresh(tokens[:refresh])
31
32
  decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
32
- assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
33
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
33
34
  assert_equal payload[:test], decoded_access['test']
34
35
  end
35
36
 
@@ -41,7 +42,7 @@ class TestSession < Minitest::Test
41
42
  refreshed_tokens = session.refresh_by_access_payload
42
43
  access2 = session.instance_variable_get('@_access')
43
44
  decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
44
- assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
45
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
45
46
  assert_equal payload[:test], decoded_access['test']
46
47
  assert_equal session.instance_variable_get('@_refresh').uid, decoded_access['ruid']
47
48
  assert_equal access2.expiration > access1.expiration, true
@@ -54,7 +55,7 @@ class TestSession < Minitest::Test
54
55
  refreshed_tokens = session.refresh_by_access_payload
55
56
  decoded_access = JWTSessions::Token.decode!(refreshed_tokens[:access]).first
56
57
  JWTSessions.access_exp_time = 3600
57
- assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
58
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
58
59
  assert_equal payload[:test], decoded_access['test']
59
60
  assert_equal session.instance_variable_get('@_refresh').uid, decoded_access['ruid']
60
61
  end
@@ -68,7 +69,7 @@ class TestSession < Minitest::Test
68
69
  end
69
70
  decoded_access = JWTSessions::Token.decode!(refreshed_tokens[:access]).first
70
71
  JWTSessions.access_exp_time = 3600
71
- assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
72
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
72
73
  assert_equal payload[:test], decoded_access['test']
73
74
  assert_equal session.instance_variable_get('@_refresh').uid, decoded_access['ruid']
74
75
  end
@@ -100,7 +101,7 @@ class TestSession < Minitest::Test
100
101
  raise JWTSessions::Errors::Unauthorized
101
102
  end
102
103
  decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
103
- assert_equal EXPECTED_KEYS, refreshed_tokens.keys.sort
104
+ assert_equal REFRESH_KEYS, refreshed_tokens.keys.sort
104
105
  assert_equal payload[:test], decoded_access['test']
105
106
  end
106
107
 
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.2.0
4
+ version: 2.2.1
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-07-25 00:00:00.000000000 Z
11
+ date: 2018-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt