rails_jwt_auth 0.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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +378 -0
  4. data/Rakefile +26 -0
  5. data/app/controllers/rails_jwt_auth/confirmations_controller.rb +29 -0
  6. data/app/controllers/rails_jwt_auth/passwords_controller.rb +31 -0
  7. data/app/controllers/rails_jwt_auth/registrations_controller.rb +35 -0
  8. data/app/controllers/rails_jwt_auth/sessions_controller.rb +50 -0
  9. data/app/controllers/unauthorized_controller.rb +11 -0
  10. data/app/helpers/rails_jwt_auth/warden_helper.rb +23 -0
  11. data/app/mailers/rails_jwt_auth/mailer.rb +37 -0
  12. data/app/models/concerns/rails_jwt_auth/authenticatable.rb +54 -0
  13. data/app/models/concerns/rails_jwt_auth/confirmable.rb +57 -0
  14. data/app/models/concerns/rails_jwt_auth/recoverable.rb +22 -0
  15. data/app/validators/email_validator.rb +7 -0
  16. data/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb +5 -0
  17. data/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb +8 -0
  18. data/config/locales/en.yml +15 -0
  19. data/lib/generators/rails_jwt_auth/install_generator.rb +15 -0
  20. data/lib/generators/templates/initializer.rb +25 -0
  21. data/lib/rails_jwt_auth.rb +47 -0
  22. data/lib/rails_jwt_auth/engine.rb +19 -0
  23. data/lib/rails_jwt_auth/errors/not_authorized.rb +6 -0
  24. data/lib/rails_jwt_auth/jwt/manager.rb +37 -0
  25. data/lib/rails_jwt_auth/jwt/request.rb +26 -0
  26. data/lib/rails_jwt_auth/spec/helpers.rb +23 -0
  27. data/lib/rails_jwt_auth/strategies/jwt.rb +17 -0
  28. data/lib/rails_jwt_auth/version.rb +3 -0
  29. data/lib/tasks/rails_token_jwt_tasks.rake +4 -0
  30. metadata +129 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 358aa1478beb6c6865bfae0f5cd68ba858bb1aba
4
+ data.tar.gz: e5abe5407f709050b90cc4803fbe842169cc36c7
5
+ SHA512:
6
+ metadata.gz: 6ce00fa7c9865dcd86711796e7a9572fab1087170b22923462d2c10a9ac9930bda6c0019f0fa4cda9d62b7d7f452edc4aeb9bcf6c5a386b7ad5a73ba5acb3f39
7
+ data.tar.gz: acb4771ed15ea3fa31629e5e4db525542c7b67ae3a2bfd1fac91b457fb0a5c2b6ca6f4e39ccef0ba88103a3995180f42f5f66c4fbe0779b07b394c47ce99e54c
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 rjurado
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,378 @@
1
+ # RailsJwtAuth
2
+
3
+ Rails authentication solution based on Warden and JWT and inspired by Devise.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rails_jwt_auth'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install rails_jwt_auth
23
+ ```
24
+
25
+ Finally execute:
26
+
27
+ ```bash
28
+ rails g rails_jwt_auth:install
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ You can edit configuration options into `config/initializers/auth_token_auth.rb` file created by generator.
34
+
35
+ | Option | Default value | Description |
36
+ | ------------------------------ | ----------------- | --------------------------------------------------------------------- |
37
+ | model_name | 'User' | Authentication model name |
38
+ | auth_field_name | 'email' | Field used to authenticate user with password |
39
+ | auth_field_email | true | Validate auth field email format |
40
+ | jwt_expiration_time | 7.days | Tokens expiration time |
41
+ | jwt_issuer | 'RailsJwtAuth' | The "iss" (issuer) claim identifies the principal that issued the JWT |
42
+ | simultaneous_sessions | 2 | Number of simultaneous sessions for an user |
43
+ | mailer_sender | | E-mail address which will be shown in RailsJwtAuth::Mailer |
44
+ | confirmation_url | confirmation_path | Url used to create email link with confirmation token |
45
+ | confirmation_expiration_time | 1.day | Confirmation token expiration time |
46
+ | reset_password_url | password_path | Url used to create email link with reset password token |
47
+ | reset_password_expiration_time | 1.day | Confirmation token expiration time |
48
+
49
+ ## Authenticatable
50
+
51
+ Hashes and stores a password in the database to validate the authenticity of a user while signing in.
52
+
53
+ ### ActiveRecord
54
+
55
+ Include `RailsJwtAuth::Authenticatable` module into your User class:
56
+
57
+ ```ruby
58
+ # app/models/user.rb
59
+ class User < ApplicationRecord
60
+ include RailsJwtAuth::Authenticatable
61
+ end
62
+ ```
63
+
64
+ and create a migration to add authenticable fields to User model:
65
+
66
+ ```ruby
67
+ # example migration
68
+ create_table :users do |t|
69
+ t.string :email
70
+ t.string :password_digest
71
+ t.string :auth_tokens
72
+ end
73
+ ```
74
+
75
+ ### Mongoid
76
+
77
+ Include `RailsJwtAuth::Authenticatable` module into your User class:
78
+
79
+ ```ruby
80
+ # app/models/user.rb
81
+ class User
82
+ include Mongoid::Document
83
+ include RailsJwtAuth::Authenticatable
84
+ end
85
+ ```
86
+
87
+ Fields are added automatically.
88
+
89
+ ## Confirmable
90
+
91
+ Sends emails with confirmation instructions and verifies whether an account is already confirmed during sign in.
92
+
93
+ ### ActiveRecord
94
+
95
+ Include `RailsJwtAuth::Confirmable` module into your User class:
96
+
97
+ ```ruby
98
+ # app/models/user.rb
99
+ class User < ApplicationRecord
100
+ include RailsJwtAuth::Authenticatable
101
+ include RailsJwtAuth::Confirmable
102
+ end
103
+ ```
104
+
105
+ and create a migration to add confirmation fields to User model:
106
+
107
+ ```ruby
108
+ # example migration
109
+ change_table :users do |t|
110
+ t.string :confirmation_token
111
+ t.datetime :confirmation_sent_at
112
+ t.datetime :confimed_at
113
+ end
114
+ ```
115
+
116
+ ### Mongoid
117
+
118
+ Include `RailsJwtAuth::Confirmable` module into your User class:
119
+
120
+ ```ruby
121
+ # app/models/user.rb
122
+ class User
123
+ include Mongoid::Document
124
+ include RailsJwtAuth::Authenticatable
125
+ include RailsJwtAuth::Confirmable
126
+ end
127
+ ```
128
+
129
+ ## Recoverable
130
+
131
+ Resets the user password and sends reset instructions
132
+
133
+ ### ActiveRecord
134
+
135
+ Include `RailsJwtAuth::Recoverable` module into your User class:
136
+
137
+ ```ruby
138
+ # app/models/user.rb
139
+ class User < ApplicationRecord
140
+ include RailsJwtAuth::Authenticatable
141
+ include RailsJwtAuth::Recoverable
142
+ end
143
+ ```
144
+
145
+ and create a migration to add recoverable fields to User model:
146
+
147
+ ```ruby
148
+ # example migration
149
+ change_table :users do |t|
150
+ t.string :reset_password_token
151
+ t.datetime :reset_password_sent_at
152
+ end
153
+ ```
154
+
155
+ ### Mongoid
156
+
157
+ Include `RailsJwtAuth::Recoverable` module into your User class:
158
+
159
+ ```ruby
160
+ # app/models/user.rb
161
+ class User
162
+ include Mongoid::Document
163
+ include RailsJwtAuth::Authenticatable
164
+ include RailsJwtAuth::Recoverable
165
+ end
166
+ ```
167
+
168
+ ## Controller helpers
169
+
170
+ RailsJwtAuth will create some helpers to use inside your controllers.
171
+
172
+ To use this helpers we need to include `WardenHelper` into `ApplicationController`:
173
+
174
+ ```ruby
175
+ # app/controllers/application_controller.rb
176
+ class ApplicationController < ActionController::API
177
+ include RailsJwtAuth::WardenHelper
178
+ end
179
+ ```
180
+
181
+ - **authenticate!**
182
+
183
+ Authenticate your controllers:
184
+
185
+ ```ruby
186
+ class MyController < ApplicationController
187
+ before_action :authenticate!
188
+ end
189
+ ```
190
+
191
+ This helper expect that token has been into **AUTHORIZATION** header.
192
+
193
+ - **current_user**
194
+
195
+ Return current signed-in user.
196
+
197
+ - **signed_in?**
198
+
199
+ Verify if a user is signed in.
200
+
201
+ ## Default Controllers API
202
+
203
+ ### Session
204
+
205
+ Session api is defined by RailsJwtAuth::SessionsController.
206
+
207
+ 1. Get session token:
208
+
209
+ ```js
210
+ {
211
+ url: host/session,
212
+ method: POST,
213
+ data: {
214
+ session: {
215
+ email: "user@email.com",
216
+ password: "12345678"
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ 2. Delete session
223
+
224
+ ```js
225
+ {
226
+ url: host/session,
227
+ method: DELETE,
228
+ headers: { 'Authorization': 'auth_token'}
229
+ }
230
+ ```
231
+
232
+ ### Registration
233
+
234
+ Registration api is defined by RailsJwtAuth::RegistrationsController.
235
+
236
+ 1. Register user:
237
+
238
+ ```js
239
+ {
240
+ url: host/registration,
241
+ method: POST,
242
+ data: {
243
+ user: {
244
+ email: "user@email.com",
245
+ password: "12345678"
246
+ }
247
+ }
248
+ }
249
+ ```
250
+
251
+ 2. Delete user:
252
+
253
+ ```js
254
+ {
255
+ url: host/registration,
256
+ method: DELETE,
257
+ headers: { 'Authorization': 'auth_token'}
258
+ }
259
+ ```
260
+
261
+ ### Confirmation
262
+
263
+ Confirmation api is defined by RailsJwtAuth::ConfirmationsController.
264
+
265
+ 1. Confirm user:
266
+
267
+ ```js
268
+ {
269
+ url: host/confirmation,
270
+ method: GET
271
+ data: {
272
+ confirmation_token: "token"
273
+ }
274
+ }
275
+ ```
276
+
277
+ 2. Create confirmation (resend confirmation email):
278
+
279
+ ```js
280
+ {
281
+ url: host/confirmation,
282
+ method: POST,
283
+ data: {
284
+ email: "user@example.com"
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### Password
290
+
291
+ Password api is defined by RailsJwtAuth::PasswordsController.
292
+
293
+ 1. Send reset password email:
294
+
295
+ ```js
296
+ {
297
+ url: host/confirmation,
298
+ method: POST,
299
+ data: {
300
+ email: "user@example.com"
301
+ }
302
+ }
303
+ ```
304
+
305
+ 2. Update password:
306
+
307
+ ```js
308
+ {
309
+ url: host/confirmation,
310
+ method: PUT,
311
+ data: {
312
+ reset_password_token: "token",
313
+ password: {
314
+ password: '1234',
315
+ password_confirmation: '1234'
316
+ }
317
+ }
318
+ }
319
+ ```
320
+
321
+ ## Custom controllers
322
+
323
+ You can overwrite RailsJwtAuth controller to edit actions, responses,
324
+ permitted parameters...
325
+
326
+ For example, if we want to change registration strong parameters we
327
+ create new registration controller inherited from default controller:
328
+
329
+ ```ruby
330
+ # app/controllers/registrations_controller.rb
331
+ class RegistrationsController < RailsJwtAuth::RegistrationsController
332
+ private
333
+
334
+ def create_params
335
+ params.require(:user).permit(:email, :name, :surname, :password, :password_confirmation)
336
+ end
337
+ end
338
+ ```
339
+
340
+ And edit route resource to use it:
341
+
342
+ ```ruby
343
+ # config/routes.rb
344
+ resource :registration, controller: 'registrations', only: [:create, :update, :destroy]
345
+ ```
346
+
347
+ ## Testing (rspec)
348
+
349
+ Require the RailsJwtAuth::Spec::Helpers helper module in `spec_helper.rb`.
350
+
351
+ ```ruby
352
+ require 'rails_jwt_auth/spec/helpers'
353
+ ...
354
+ RSpec.configure do |config|
355
+ ...
356
+ config.include RailsJwtAuth::Spec::Helpers, :type => :controller
357
+ ...
358
+ end
359
+ ```
360
+
361
+ And then in controller examples we can just call sign_in(user) to sign in as a user, or sign_out for examples that have no user signed in. Here's two quick examples:
362
+
363
+ ```ruby
364
+ it "blocks unauthenticated access" do
365
+ sign_out
366
+ expect { get :index }.to raise_error(RailsJwtAuth::Errors::NotAuthorized)
367
+ end
368
+
369
+ it "allows authenticated access" do
370
+ sign_in
371
+ get :index
372
+ expect(response).to be_success
373
+ end
374
+ ```
375
+
376
+ ## License
377
+
378
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsJwtAuth'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
@@ -0,0 +1,29 @@
1
+ class RailsJwtAuth::ConfirmationsController < ApplicationController
2
+ def show
3
+ return render json: {}, status: 400 unless params[:confirmation_token]
4
+
5
+ user = RailsJwtAuth.model.find_by!(confirmation_token: params[:confirmation_token])
6
+
7
+ if user.confirm!
8
+ render json: {}, status: 204
9
+ else
10
+ render json: show_error_response(user), status: 422
11
+ end
12
+ end
13
+
14
+ def create
15
+ user = RailsJwtAuth.model.find_by!(email: confirmation_params[:email])
16
+ user.send_confirmation_instructions
17
+ render json: {}, status: 204
18
+ end
19
+
20
+ private
21
+
22
+ def confirmation_params
23
+ params.require(:confirmation).permit(:email)
24
+ end
25
+
26
+ def show_error_response(user)
27
+ {errors: user.errors}
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ class RailsJwtAuth::PasswordsController < ApplicationController
2
+ def create
3
+ user = RailsJwtAuth.model.find_by!(email: create_password_params[:email])
4
+ user.send_reset_password_instructions
5
+ render json: {}, status: 204
6
+ end
7
+
8
+ def update
9
+ user = RailsJwtAuth.model.find_by!(reset_password_token: params[:reset_password_token])
10
+
11
+ if user.update_attributes(update_password_params)
12
+ render json: {}, status: 204
13
+ else
14
+ render json: update_error_response(user), status: 422
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def create_password_params
21
+ params.require(:password).permit(:email)
22
+ end
23
+
24
+ def update_password_params
25
+ params.require(:password).permit(:password, :password_confirmation)
26
+ end
27
+
28
+ def update_error_response(user)
29
+ {errors: user.errors}
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ class RailsJwtAuth::RegistrationsController < ApplicationController
2
+ def create
3
+ user = RailsJwtAuth.model.new(create_params)
4
+
5
+ if user.save
6
+ render json: create_success_response(user), status: 201
7
+ else
8
+ render json: create_error_response(user), status: 422
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def root
15
+ RailsJwtAuth.model_name.underscore
16
+ end
17
+
18
+ def create_success_response(user)
19
+ {
20
+ root => {
21
+ id: user.id.to_s,
22
+ RailsJwtAuth.auth_field_name => user.send(RailsJwtAuth.auth_field_name)
23
+ }
24
+ }
25
+ end
26
+
27
+ def create_error_response(user)
28
+ {errors: user.errors}
29
+ end
30
+
31
+ def create_params
32
+ params.require(root).permit(
33
+ RailsJwtAuth.auth_field_name, :password, :password_confirmation)
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ require 'rails_jwt_auth/jwt/manager'
2
+ require 'rails_jwt_auth/jwt/request'
3
+
4
+ module RailsJwtAuth
5
+ class SessionsController < ApplicationController
6
+ def create
7
+ user = RailsJwtAuth.model.where(
8
+ RailsJwtAuth.auth_field_name => create_params[RailsJwtAuth.auth_field_name].to_s.downcase
9
+ ).first
10
+
11
+ if !user
12
+ render json: create_error_response(user), status: 422
13
+ elsif user.respond_to?('confirmed?') && !user.confirmed?
14
+ render json: unconfirmed_error_response, status: 422
15
+ elsif user.authenticate(create_params[:password])
16
+ render json: create_success_response(user, get_jwt(user)), status: 201
17
+ else
18
+ render json: create_error_response(user), status: 422
19
+ end
20
+ end
21
+
22
+ def destroy
23
+ authenticate!
24
+ current_user.destroy_auth_token Jwt::Request.new(request).auth_token
25
+ end
26
+
27
+ private
28
+
29
+ def get_jwt(user)
30
+ token = user.regenerate_auth_token
31
+ RailsJwtAuth::Jwt::Manager.encode(auth_token: token)
32
+ end
33
+
34
+ def unconfirmed_error_response
35
+ {errors: {session: 'Unconfirmed email'}}
36
+ end
37
+
38
+ def create_success_response(_user, jwt)
39
+ {session: {jwt: jwt}}
40
+ end
41
+
42
+ def create_error_response(_user)
43
+ {errors: {session: "Invalid #{RailsJwtAuth.auth_field_name} / password"}}
44
+ end
45
+
46
+ def create_params
47
+ params.require(:session).permit(RailsJwtAuth.auth_field_name, :password)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ class UnauthorizedController < ActionController::Metal
2
+ def self.call(env)
3
+ @respond ||= action(:respond)
4
+ @respond.call(env)
5
+ end
6
+
7
+ def respond
8
+ self.response_body = "Unauthorized Action"
9
+ self.status = :unauthorized
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module RailsJwtAuth
2
+ module WardenHelper
3
+ def signed_in?
4
+ !current_user.nil?
5
+ end
6
+
7
+ def current_user
8
+ warden.user
9
+ end
10
+
11
+ def warden
12
+ request.env['warden']
13
+ end
14
+
15
+ def authenticate!
16
+ warden.authenticate!
17
+ end
18
+
19
+ def render_401
20
+ render json: {}, status: 401
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ if defined?(ActionMailer)
2
+ class RailsJwtAuth::Mailer < ApplicationMailer
3
+ default from: RailsJwtAuth.mailer_sender
4
+
5
+ def confirmation_instructions(user)
6
+ return unless user.confirmation_in_progress?
7
+ @user = user
8
+
9
+ if RailsJwtAuth.confirmation_url
10
+ url = URI.parse(RailsJwtAuth.confirmation_url)
11
+ url.query = [url.query, "confirmation_token=#{@user.confirmation_token}"].compact.join('&')
12
+ @confirmation_url = url.to_s
13
+ else
14
+ @confirmation_url = confirmation_url(confirmation_token: @user.confirmation_token)
15
+ end
16
+
17
+ subject = I18n.t('rails_jwt_auth.mailer.confirmation_instructions.subject')
18
+ mail(to: @user.email, subject: subject)
19
+ end
20
+
21
+ def reset_password_instructions(user)
22
+ return unless user.reset_password_in_progress?
23
+ @user = user
24
+
25
+ if RailsJwtAuth.reset_password_url
26
+ url = URI.parse(RailsJwtAuth.reset_password_url)
27
+ url.query = [url.query, "reset_password_token=#{@user.reset_password_token}"].compact.join('&')
28
+ @reset_password_url = url.to_s
29
+ else
30
+ @reset_password_url = password_url(reset_password_token: @user.reset_password_token)
31
+ end
32
+
33
+ subject = I18n.t('rails_jwt_auth.mailer.reset_password_instructions.subject')
34
+ mail(to: @user.email, subject: subject)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,54 @@
1
+ include ActiveModel::SecurePassword
2
+
3
+ module RailsJwtAuth
4
+ module Authenticatable
5
+ def regenerate_auth_token(token = nil)
6
+ new_token = SecureRandom.base58(24)
7
+
8
+ if RailsJwtAuth.simultaneous_sessions > 1
9
+ tokens = ((auth_tokens || []) - [token]).last(RailsJwtAuth.simultaneous_sessions - 1)
10
+ update_attribute(:auth_tokens, (tokens + [new_token]).uniq)
11
+ else
12
+ update_attribute(:auth_tokens, [new_token])
13
+ end
14
+
15
+ new_token
16
+ end
17
+
18
+ def destroy_auth_token(token)
19
+ if RailsJwtAuth.simultaneous_sessions > 1
20
+ tokens = auth_tokens || []
21
+ update_attribute(:auth_tokens, tokens - [token])
22
+ else
23
+ update_attribute(:auth_tokens, [])
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def get_by_token(token)
29
+ if defined?(Mongoid) && ancestors.include?(Mongoid::Document)
30
+ where(auth_tokens: token).first
31
+ elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
32
+ where('auth_tokens like ?', "%#{token}%").first
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.included(base)
38
+ if defined?(Mongoid) && base.ancestors.include?(Mongoid::Document)
39
+ base.send(:field, RailsJwtAuth.auth_field_name, type: String)
40
+ base.send(:field, :password_digest, type: String)
41
+ base.send(:field, :auth_tokens, type: Array)
42
+ elsif defined?(ActiveRecord) && base.ancestors.include?(ActiveRecord::Base)
43
+ base.send(:serialize, :auth_tokens, Array)
44
+ end
45
+
46
+ base.send(:validates, RailsJwtAuth.auth_field_name, presence: true, uniqueness: true)
47
+ base.send(:validates, RailsJwtAuth.auth_field_name, email: true) if RailsJwtAuth.auth_field_email
48
+
49
+ base.send(:has_secure_password)
50
+
51
+ base.extend(ClassMethods)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ module RailsJwtAuth
2
+ module Confirmable
3
+ def send_confirmation_instructions
4
+ return false if confirmed?
5
+
6
+ self.confirmation_token = SecureRandom.base58(24)
7
+ self.confirmation_sent_at = Time.now
8
+
9
+ Mailer.confirmation_instructions(self).deliver if save
10
+ end
11
+
12
+ def confirmed?
13
+ confirmed_at.present?
14
+ end
15
+
16
+ def confirm!
17
+ self.confirmed_at = Time.now.utc
18
+ save
19
+ end
20
+
21
+ def skip_confirmation!
22
+ self.confirmed_at = Time.now.utc
23
+ end
24
+
25
+ def confirmation_in_progress?
26
+ !confirmed_at && confirmation_token && confirmation_sent_at &&
27
+ (Time.now < (confirmation_sent_at + RailsJwtAuth.confirmation_expiration_time))
28
+ end
29
+
30
+ def self.included(base)
31
+ if base.ancestors.include? Mongoid::Document
32
+ base.send(:field, :confirmation_token, type: String)
33
+ base.send(:field, :confirmation_sent_at, type: Time)
34
+ base.send(:field, :confirmed_at, type: Time)
35
+ end
36
+
37
+ base.send(:validate, :validate_confirmation, if: :confirmed_at_changed?)
38
+
39
+ base.send(:after_create) do
40
+ send_confirmation_instructions unless confirmed_at || confirmation_sent_at
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def validate_confirmation
47
+ return unless confirmed_at
48
+
49
+ if confirmed_at_was
50
+ errors.add(:email, I18n.t('rails_jwt_auth.errors.already_confirmed'))
51
+ elsif confirmation_sent_at &&
52
+ (confirmation_sent_at < (Time.now - RailsJwtAuth.confirmation_expiration_time))
53
+ errors.add(:confirmation_token, I18n.t('rails_jwt_auth.errors.confirmation_expired'))
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,22 @@
1
+ module RailsJwtAuth
2
+ module Recoverable
3
+ def send_reset_password_instructions
4
+ self.reset_password_token = SecureRandom.base58(24)
5
+ self.reset_password_sent_at = Time.now
6
+
7
+ Mailer.reset_password_instructions(self).deliver if save
8
+ end
9
+
10
+ def reset_password_in_progress?
11
+ reset_password_token && reset_password_sent_at &&
12
+ (Time.now < (reset_password_sent_at + RailsJwtAuth.reset_password_expiration_time))
13
+ end
14
+
15
+ def self.included(base)
16
+ if base.ancestors.include? Mongoid::Document
17
+ base.send(:field, :reset_password_token, type: String)
18
+ base.send(:field, :reset_password_sent_at, type: Time)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ class EmailValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
4
+ record.errors[attribute] << (options[:message] || "is not an email")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ <p>Welcome <%= @user.email %>!</p>
2
+
3
+ <p>You can confirm your account email through the link below:</p>
4
+
5
+ <p><%= link_to 'Confirm my account', @confirmation_url %></p>
@@ -0,0 +1,8 @@
1
+ <p>Hello <%= @user.email %>!</p>
2
+
3
+ <p>Someone has requested a link to change your password. You can do this through the link below.</p>
4
+
5
+ <p><%= link_to 'Change my password', @reset_password_url %></p>
6
+
7
+ <p>If you didn't request this, please ignore this email.</p>
8
+ <p>Your password won't change until you access the link above and create a new one.</p>
@@ -0,0 +1,15 @@
1
+ en:
2
+ rails_jwt_auth:
3
+ mailer:
4
+ confirmation_instructions:
5
+ subject: "Confirmation instructions"
6
+ errors:
7
+ messages:
8
+ already_confirmed: "was already confirmed, please try signing in"
9
+ confirmation_expired: "needs to be confirmed within %{period}, please request a new one"
10
+ expired: "has expired, please request a new one"
11
+ not_found: "not found"
12
+ not_locked: "was not locked"
13
+ not_saved:
14
+ one: "1 error prohibited this %{resource} from being saved:"
15
+ other: "%{count} errors prohibited this %{resource} from being saved:"
@@ -0,0 +1,15 @@
1
+ class RailsJwtAuth::InstallGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('../../templates', __FILE__)
3
+
4
+ def create_initializer_file
5
+ copy_file "initializer.rb", "config/initializers/rails_jwt_auth.rb"
6
+ end
7
+
8
+ def create_routes
9
+ route "resource :session, controller: 'rails_jwt_auth/sessions', only: [:create, :destroy]"
10
+ route "resource :registration, controller: 'rails_jwt_auth/registrations', only: [:create, :update, :destroy]"
11
+
12
+ route "resource :confirmation, controller: 'rails_jwt_auth/confirmations', only: [:show, :create]"
13
+ route "resource :password, controller: 'rails_jwt_auth/passwords', only: [:create, :update]"
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ RailsJwtAuth.setup do |config|
2
+ # authentication model class name
3
+ #config.model_name = 'User'
4
+
5
+ # field name used to authentication with password
6
+ #config.auth_field_name = 'email'
7
+
8
+ # set to true to validate auth_field email format
9
+ #config.auth_field_email = true
10
+
11
+ # expiration time for generated tokens
12
+ #config.jwt_expiration_time = 7.days
13
+
14
+ # the "iss" (issuer) claim identifies the principal that issued the JWT
15
+ #config.jwt_issuer = 'RailsJwtAuth'
16
+
17
+ # number of simultaneously sessions for an user
18
+ #config.simultaneously_sessions = 3
19
+
20
+ # mailer sender
21
+ #config.mailer_sender = 'initialize-mailer_sender@example.com'
22
+
23
+ # url used into confirmation email to reditect with confirmation_token
24
+ #config.confirmation_url = 'http://frontend.com/confirmation'
25
+ end
@@ -0,0 +1,47 @@
1
+ require "warden"
2
+ require "bcrypt"
3
+
4
+ require "rails_jwt_auth/engine"
5
+
6
+ module RailsJwtAuth
7
+ mattr_accessor :model_name
8
+ @@model_name = 'User'
9
+
10
+ mattr_accessor :auth_field_name
11
+ @@auth_field_name = 'email'
12
+
13
+ mattr_accessor :auth_field_email
14
+ @@auth_field_email = true
15
+
16
+ mattr_accessor :jwt_expiration_time
17
+ @@jwt_expiration_time = 7.days
18
+
19
+ mattr_accessor :jwt_issuer
20
+ @@jwt_issuer = 'RailsJwtAuth'
21
+
22
+ mattr_accessor :simultaneous_sessions
23
+ @@simultaneous_sessions = 2
24
+
25
+ mattr_accessor :mailer_sender
26
+ @@mailer_sender = "initialize-mailer_sender@example.com"
27
+
28
+ mattr_accessor :confirmation_url
29
+ @@confirmation_url = nil
30
+
31
+ mattr_accessor :confirmation_expiration_time
32
+ @@confirmation_expiration_time = 1.day
33
+
34
+ mattr_accessor :reset_password_url
35
+ @@reset_password_url = nil
36
+
37
+ mattr_accessor :reset_password_expiration_time
38
+ @@reset_password_expiration_time = 1.day
39
+
40
+ def self.model
41
+ @@model_name.constantize
42
+ end
43
+
44
+ def self.setup
45
+ yield self
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ module RailsJwtAuth
2
+ class Engine < ::Rails::Engine
3
+ require 'rails_jwt_auth/strategies/jwt'
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_girl, dir: 'spec/factories'
8
+ end
9
+
10
+ initializer 'rails_jwt_auth.warden' do |app|
11
+ app.middleware.insert_after ActionDispatch::Callbacks, Warden::Manager do |manager|
12
+ manager.default_strategies :authentication_token
13
+ manager.failure_app = UnauthorizedController
14
+ end
15
+
16
+ Warden::Strategies.add(:authentication_token, Strategies::Jwt)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module RailsJwtAuth
2
+ module Errors
3
+ class NotAuthorized < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ require 'jwt'
2
+
3
+ module RailsJwtAuth
4
+ module Jwt
5
+ class Manager
6
+ # Encodes and signs JWT Payload with expiration
7
+ def self.encode(payload)
8
+ payload.reverse_merge!(meta)
9
+ JWT.encode(payload, Rails.application.secrets.secret_key_base)
10
+ end
11
+
12
+ # Decodes the JWT with the signed secret
13
+ # [{"auth_token"=>"xxx", "exp"=>148..., "iss"=>"RJA"}, {"typ"=>"JWT", "alg"=>"HS256"}]
14
+ def self.decode(token)
15
+ JWT.decode(token, Rails.application.secrets.secret_key_base)
16
+ end
17
+
18
+ # Validates the payload hash for expiration and meta claims
19
+ def self.valid_payload?(payload)
20
+ payload && !expired?(payload) && payload['iss'] == meta[:iss]
21
+ end
22
+
23
+ # Default options to be encoded in the token
24
+ def self.meta
25
+ {
26
+ exp: RailsJwtAuth.jwt_expiration_time.from_now.to_i,
27
+ iss: RailsJwtAuth.jwt_issuer
28
+ }
29
+ end
30
+
31
+ # Validates if the token is expired by exp parameter
32
+ def self.expired?(payload)
33
+ Time.at(payload['exp']) < Time.now
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module RailsJwtAuth
2
+ module Jwt
3
+ class Request
4
+ def initialize(request)
5
+ return unless (@jwt = request.env['HTTP_AUTHORIZATION'])
6
+ @jwt_info = RailsJwtAuth::Jwt::Manager.decode(@jwt)
7
+ end
8
+
9
+ def valid?
10
+ @jwt && RailsJwtAuth::Jwt::Manager.valid_payload?(payload)
11
+ end
12
+
13
+ def payload
14
+ @jwt_info ? @jwt_info[0] : nil
15
+ end
16
+
17
+ def header
18
+ @jwt_info ? @jwt_info[1] : nil
19
+ end
20
+
21
+ def auth_token
22
+ payload ? payload['auth_token'] : nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module RailsJwtAuth
2
+ module Spec
3
+ module Helpers
4
+ require 'rails_jwt_auth/errors/not_authorized'
5
+ require 'rails_jwt_auth/jwt/manager'
6
+
7
+ def sign_out
8
+ request.env['warden'] = RailsJwtAuth::Strategies::Jwt.new request.env
9
+ allow(request.env['warden']).to receive(:authenticate!).and_raise(RailsJwtAuth::Errors::NotAuthorized)
10
+ end
11
+
12
+ def sign_in(user)
13
+ request.env['warden'] = RailsJwtAuth::Strategies::Jwt.new request.env
14
+ allow(request.env['warden']).to receive(:authenticate!).and_return(user)
15
+ allow(controller).to receive(:current_user).and_return(user)
16
+
17
+ user.auth_tokens = []
18
+ token = user.regenerate_auth_token
19
+ request.env['HTTP_AUTHORIZATION'] = RailsJwtAuth::Jwt::Manager.encode(auth_token: token)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails_jwt_auth/jwt/request'
2
+
3
+ module RailsJwtAuth
4
+ module Strategies
5
+ class Jwt < ::Warden::Strategies::Base
6
+ def authenticate!
7
+ jwt = RailsJwtAuth::Jwt::Request.new(request)
8
+
9
+ if jwt.valid? && (model = RailsJwtAuth.model.get_by_token(jwt.auth_token))
10
+ success!(model)
11
+ else
12
+ fail!('strategies.authentication_token.failed')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module RailsJwtAuth
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_jwt_auth do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_jwt_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - rjurado
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: warden
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jwt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bcrypt
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.1'
69
+ description: Rails authentication solution based on Warden and JWT and inspired by
70
+ Devise.
71
+ email:
72
+ - rjurado@openmailbox.org
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - MIT-LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - app/controllers/rails_jwt_auth/confirmations_controller.rb
81
+ - app/controllers/rails_jwt_auth/passwords_controller.rb
82
+ - app/controllers/rails_jwt_auth/registrations_controller.rb
83
+ - app/controllers/rails_jwt_auth/sessions_controller.rb
84
+ - app/controllers/unauthorized_controller.rb
85
+ - app/helpers/rails_jwt_auth/warden_helper.rb
86
+ - app/mailers/rails_jwt_auth/mailer.rb
87
+ - app/models/concerns/rails_jwt_auth/authenticatable.rb
88
+ - app/models/concerns/rails_jwt_auth/confirmable.rb
89
+ - app/models/concerns/rails_jwt_auth/recoverable.rb
90
+ - app/validators/email_validator.rb
91
+ - app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb
92
+ - app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb
93
+ - config/locales/en.yml
94
+ - lib/generators/rails_jwt_auth/install_generator.rb
95
+ - lib/generators/templates/initializer.rb
96
+ - lib/rails_jwt_auth.rb
97
+ - lib/rails_jwt_auth/engine.rb
98
+ - lib/rails_jwt_auth/errors/not_authorized.rb
99
+ - lib/rails_jwt_auth/jwt/manager.rb
100
+ - lib/rails_jwt_auth/jwt/request.rb
101
+ - lib/rails_jwt_auth/spec/helpers.rb
102
+ - lib/rails_jwt_auth/strategies/jwt.rb
103
+ - lib/rails_jwt_auth/version.rb
104
+ - lib/tasks/rails_token_jwt_tasks.rake
105
+ homepage: https://github.com/rjurado01/rails-token-auth
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.5.1
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Rails jwt authentication.
129
+ test_files: []