jwt_sessions 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +60 -31
- data/lib/jwt_sessions/version.rb +1 -1
- 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: 5168d7361bdf008db2c14204b698bed8926aa0fc
|
4
|
+
data.tar.gz: 7a218c9787fef9375a260064adaaf69f31d53e78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5646630d2e046bf3f6519f384e21306ba109127f53cf831d06164e39d8805396f7a3e561a3afe702c647308faca3cee949300726e3e0134e3fb24563c1330981
|
7
|
+
data.tar.gz: 902ef70ff8adfe56771ebbab3f06b330bf762a81fc13e9c555df0eaab4b8dfd7ca30b95d84355cfaf4bf11c3508c0e015bcba744bf7eb39095d7c4f6b3b9f4c1
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ jwt_sessions itself uses `ext` claim and `HS256` signing by default.
|
|
21
21
|
|
22
22
|
Put this line in your Gemfile
|
23
23
|
|
24
|
-
```
|
24
|
+
```ruby
|
25
25
|
gem 'jwt_sessions'
|
26
26
|
```
|
27
27
|
|
@@ -39,7 +39,7 @@ bundle install
|
|
39
39
|
|
40
40
|
Include `JWTSessions::RailsAuthorization` in your controllers, add `JWTSessions::Errors::Unauthorized` exceptions handling if needed.
|
41
41
|
|
42
|
-
```
|
42
|
+
```ruby
|
43
43
|
class ApplicationController < ActionController::API
|
44
44
|
include JWTSessions::RailsAuthorization
|
45
45
|
rescue_from JWTSessions::Errors::Unauthorized, with: :not_authorized
|
@@ -55,14 +55,14 @@ end
|
|
55
55
|
Specify an encryption key for JSON Web Tokens in `config/initializers/jwt_session.rb` \
|
56
56
|
It's adviced to store the key itself within the app secrets.
|
57
57
|
|
58
|
-
```
|
58
|
+
```ruby
|
59
59
|
JWTSessions.encryption_key = Rails.application.secrets.secret_jwt_encryption_key
|
60
60
|
```
|
61
61
|
|
62
62
|
Generate access/refresh/csrf tokens with a custom payload. \
|
63
63
|
The payload will be available in the controllers once the access (or refresh) token is authorized.
|
64
64
|
|
65
|
-
```
|
65
|
+
```ruby
|
66
66
|
> payload = { user_id: user.id }
|
67
67
|
=> {:user_id=>1}
|
68
68
|
|
@@ -80,7 +80,7 @@ Refresh controller - to be able to get a new access token using refresh token af
|
|
80
80
|
Here is example of a simple login controller, which returns set of tokens as a plain JSON response. \
|
81
81
|
It's also possible to set tokens as cookies in the response instead.
|
82
82
|
|
83
|
-
```
|
83
|
+
```ruby
|
84
84
|
class LoginController < ApplicationController
|
85
85
|
def create
|
86
86
|
user = User.find_by!(email: params[:email])
|
@@ -97,14 +97,14 @@ end
|
|
97
97
|
|
98
98
|
Since it's not required to pass an access token when you want to perform a refresh you may need to have some data in the payload of the refresh token to allow you to construct a payload of the new access token during refresh.
|
99
99
|
|
100
|
-
```
|
100
|
+
```ruby
|
101
101
|
session = JWTSessions::Session.new(payload: payload, refresh_payload: refresh_payload)
|
102
102
|
```
|
103
103
|
|
104
104
|
Now you can build a refresh endpoint. To protect the endpoint use before_action `authorize_refresh_request!`. \
|
105
105
|
In the example `found_token` - is a token fetched from request headers or cookies.
|
106
106
|
|
107
|
-
```
|
107
|
+
```ruby
|
108
108
|
class RefreshController < ApplicationController
|
109
109
|
before_action :authorize_refresh_request!
|
110
110
|
|
@@ -129,7 +129,7 @@ POST /refresh
|
|
129
129
|
|
130
130
|
Now when there're login and refresh endpoints, you can protect the rest of your secure controllers with `before_action :authorize_access_request!`.
|
131
131
|
|
132
|
-
```
|
132
|
+
```ruby
|
133
133
|
class UsersController < ApplicationController
|
134
134
|
before_action :authorize_access_request!
|
135
135
|
|
@@ -151,7 +151,7 @@ GET /users
|
|
151
151
|
|
152
152
|
The `payload` method is available to fetch encoded data from the token.
|
153
153
|
|
154
|
-
```
|
154
|
+
```ruby
|
155
155
|
def current_user
|
156
156
|
@current_user ||= User.find(payload['user_id'])
|
157
157
|
end
|
@@ -163,7 +163,7 @@ You must include `JWTSessions::Authorization` module to your auth class and impl
|
|
163
163
|
|
164
164
|
1. request_headers
|
165
165
|
|
166
|
-
```
|
166
|
+
```ruby
|
167
167
|
def request_headers
|
168
168
|
# must return hash-like object with request headers
|
169
169
|
end
|
@@ -171,7 +171,7 @@ end
|
|
171
171
|
|
172
172
|
2. request_cookies
|
173
173
|
|
174
|
-
```
|
174
|
+
```ruby
|
175
175
|
def request_cookies
|
176
176
|
# must return hash-like object with request cookies
|
177
177
|
end
|
@@ -179,22 +179,28 @@ end
|
|
179
179
|
|
180
180
|
3. request_method
|
181
181
|
|
182
|
-
```
|
182
|
+
```ruby
|
183
183
|
def request_method
|
184
184
|
# must return current request verb as a string in upcase, f.e. 'GET', 'HEAD', 'POST', 'PATCH', etc
|
185
185
|
end
|
186
186
|
```
|
187
187
|
|
188
|
-
Example Sinatra app
|
188
|
+
Example Sinatra app.
|
189
|
+
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.
|
189
190
|
|
190
|
-
```
|
191
|
+
```ruby
|
191
192
|
require 'sinatra/base'
|
192
193
|
|
194
|
+
JWTSessions.access_header = 'authorization'
|
195
|
+
JWTSessions.refresh_header = 'x_refresh_token'
|
196
|
+
JWTSessions.csrf_header = 'x_csrf_token'
|
197
|
+
JWTSessions.encryption_key = 'secret key'
|
198
|
+
|
193
199
|
class SimpleApp < Sinatra::Base
|
194
200
|
include JWTSessions::Authorization
|
195
201
|
|
196
202
|
def request_headers
|
197
|
-
|
203
|
+
env.inject({}){|acc, (k,v)| acc[$1.downcase] = v if k =~ /^http_(.*)/i; acc}
|
198
204
|
end
|
199
205
|
|
200
206
|
def request_cookies
|
@@ -205,13 +211,33 @@ class SimpleApp < Sinatra::Base
|
|
205
211
|
request.request_method
|
206
212
|
end
|
207
213
|
|
214
|
+
before do
|
215
|
+
content_type 'application/json'
|
216
|
+
end
|
217
|
+
|
218
|
+
post '/login' do
|
219
|
+
access_payload = { key: 'access value' }
|
220
|
+
refresh_payload = { key: 'refresh value' }
|
221
|
+
session = JWTSessions::Session.new(payload: access_payload, refresh_payload: refresh_payload)
|
222
|
+
session.login.to_json
|
223
|
+
end
|
224
|
+
|
225
|
+
# POST /refresh
|
226
|
+
# x_refresh_token: ...
|
208
227
|
post '/refresh' do
|
209
|
-
content_type :json
|
210
228
|
authorize_refresh_request!
|
211
|
-
|
229
|
+
access_payload = { key: 'reloaded access value' }
|
230
|
+
session = JWTSessions::Session.new(payload: access_payload, refresh_payload: payload)
|
212
231
|
session.refresh(found_token).to_json
|
213
232
|
end
|
214
233
|
|
234
|
+
# GEY /payload
|
235
|
+
# authorization: Bearer ...
|
236
|
+
get '/payload' do
|
237
|
+
authorize_access_request!
|
238
|
+
payload.to_json
|
239
|
+
end
|
240
|
+
|
215
241
|
....
|
216
242
|
end
|
217
243
|
```
|
@@ -224,7 +250,7 @@ List of configurable settings with their default values.
|
|
224
250
|
|
225
251
|
Default token store configurations
|
226
252
|
|
227
|
-
```
|
253
|
+
```ruby
|
228
254
|
JWTSessions.redis_host = '127.0.0.1'
|
229
255
|
JWTSessions.redis_port = '6379'
|
230
256
|
JWTSessions.redis_db_name = 'jwtokens'
|
@@ -233,28 +259,21 @@ JWTSessions.token_prefix = 'jwt_' # used for redis db keys
|
|
233
259
|
|
234
260
|
##### JWT encryption
|
235
261
|
|
236
|
-
```
|
262
|
+
```ruby
|
237
263
|
JWTSessions.algorithm = 'HS256'
|
238
264
|
```
|
239
265
|
|
240
266
|
You need to specify a secret to use for HMAC, this setting doesn't have a default value.
|
241
267
|
|
242
|
-
```
|
268
|
+
```ruby
|
243
269
|
JWTSessions.secret = 'secret'
|
244
270
|
```
|
245
271
|
|
246
|
-
Or you need to specify public and private keys for RSA/EDCSA/EDDSA, there are no default values for keys. You can use instructions from [ruby-jwt](https://github.com/jwt/ruby-jwt) to generate keys corresponding keys.
|
247
|
-
|
248
|
-
```
|
249
|
-
JWTSessions.private_key = 'private_key'
|
250
|
-
JWTSessions.public_key = 'public_key_for_private'
|
251
|
-
```
|
252
|
-
|
253
272
|
##### Request headers and cookies names
|
254
273
|
|
255
274
|
Default request headers/cookies names can be re-configured
|
256
275
|
|
257
|
-
```
|
276
|
+
```ruby
|
258
277
|
JWTSessions.access_header = 'Authorization'
|
259
278
|
JWTSessions.access_cookie = 'jwt_access'
|
260
279
|
JWTSessions.refresh_header = 'X-Refresh-Token'
|
@@ -266,7 +285,7 @@ JWTSessions.csrf_header = 'X-CSRF-Token'
|
|
266
285
|
|
267
286
|
Acces token must have a short life span, while refresh tokens can be stored for a longer time period
|
268
287
|
|
269
|
-
```
|
288
|
+
```ruby
|
270
289
|
JWTSessions.access_exp_time = 3600 # 1 hour in seconds
|
271
290
|
JWTSessions.refresh_exp_time = 604800 # 1 week in seconds
|
272
291
|
```
|
@@ -276,7 +295,7 @@ JWTSessions.refresh_exp_time = 604800 # 1 week in seconds
|
|
276
295
|
In case when you use cookies as your tokens transport it gets vulnerable to CSRF. That's why both 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 `Authorization` exception will be raised in case of mismatch between the token from the header and the one stored in session. \
|
277
296
|
Although you don't need to mitigate BREACH attacks it's still possible to generate a new masked token with the access token
|
278
297
|
|
279
|
-
```
|
298
|
+
```ruby
|
280
299
|
session = JWTSessions::Session.new
|
281
300
|
session.masked_csrf(access_token)
|
282
301
|
```
|
@@ -286,11 +305,21 @@ session.masked_csrf(access_token)
|
|
286
305
|
There is a security recommendation regarding the usage of refresh tokens: only perform refresh when an access token gets expired. \
|
287
306
|
Since sessions are always defined by a pair of tokens and there can't be multiple access tokens for a single refresh token simultaneous usage of the refresh token by multiple users can be easily noticed as refresh will be perfomed before the expiration of the access token by one of the users. Because of that `refresh` method of the `Session` class supports optional block as one of its arguments which will be executed only in case of refresh being performed before the expiration of the access token.
|
288
307
|
|
289
|
-
```
|
308
|
+
```ruby
|
290
309
|
session = JwtSessions::Session.new(payload: payload)
|
291
310
|
session.refresh(refresh_token) { |refresh_token_uid, access_token_expiration| ... }
|
292
311
|
```
|
293
312
|
|
313
|
+
## TODO
|
314
|
+
|
315
|
+
Ability to specify public and private keys for RSA/EDCSA/EDDSA, there are no default values for keys. \
|
316
|
+
You can use instructions from [ruby-jwt](https://github.com/jwt/ruby-jwt) to generate keys corresponding keys.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
JWTSessions.private_key = 'private_key'
|
320
|
+
JWTSessions.public_key = 'public_key_for_private'
|
321
|
+
```
|
322
|
+
|
294
323
|
## Contributing
|
295
324
|
|
296
325
|
Fork & Pull Request
|
data/lib/jwt_sessions/version.rb
CHANGED
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
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.0.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-04-
|
11
|
+
date: 2018-04-17 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
19
|
version: '1.4'
|
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
26
|
version: '1.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|