rails_jwt_auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []