rails_jwt_auth 1.6.1 → 2.0.1

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +187 -88
  3. data/app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb +15 -7
  4. data/app/controllers/concerns/rails_jwt_auth/params_helper.rb +18 -4
  5. data/app/controllers/concerns/rails_jwt_auth/render_helper.rb +10 -2
  6. data/app/controllers/rails_jwt_auth/confirmations_controller.rb +48 -10
  7. data/app/controllers/rails_jwt_auth/invitations_controller.rb +26 -9
  8. data/app/controllers/rails_jwt_auth/profiles_controller.rb +50 -0
  9. data/app/controllers/rails_jwt_auth/reset_passwords_controller.rb +65 -0
  10. data/app/controllers/rails_jwt_auth/sessions_controller.rb +5 -21
  11. data/app/controllers/rails_jwt_auth/{unlocks_controller.rb → unlock_accounts_controller.rb} +2 -2
  12. data/app/mailers/rails_jwt_auth/mailer.rb +29 -42
  13. data/app/models/concerns/rails_jwt_auth/authenticatable.rb +60 -19
  14. data/app/models/concerns/rails_jwt_auth/confirmable.rb +52 -33
  15. data/app/models/concerns/rails_jwt_auth/invitable.rb +42 -78
  16. data/app/models/concerns/rails_jwt_auth/lockable.rb +28 -46
  17. data/app/models/concerns/rails_jwt_auth/recoverable.rb +21 -31
  18. data/app/models/concerns/rails_jwt_auth/trackable.rb +13 -2
  19. data/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb +1 -1
  20. data/app/views/rails_jwt_auth/mailer/{email_changed.html.erb → email_change_requested_notification.html.erb} +0 -0
  21. data/app/views/rails_jwt_auth/mailer/{send_invitation.html.erb → invitation_instructions.html.erb} +1 -1
  22. data/app/views/rails_jwt_auth/mailer/password_changed_notification.html.erb +3 -0
  23. data/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb +1 -1
  24. data/app/views/rails_jwt_auth/mailer/{send_unlock_instructions.html.erb → unlock_instructions.html.erb} +1 -1
  25. data/config/locales/en.yml +6 -6
  26. data/lib/generators/rails_jwt_auth/install_generator.rb +11 -3
  27. data/lib/generators/templates/initializer.rb +43 -29
  28. data/lib/generators/templates/migration.rb +2 -1
  29. data/lib/rails_jwt_auth.rb +48 -45
  30. data/lib/rails_jwt_auth/jwt_manager.rb +2 -4
  31. data/lib/rails_jwt_auth/session.rb +132 -0
  32. data/lib/rails_jwt_auth/version.rb +1 -1
  33. metadata +10 -8
  34. data/app/controllers/rails_jwt_auth/passwords_controller.rb +0 -32
  35. data/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb +0 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsJwtAuth
2
4
  module Confirmable
3
5
  def self.included(base)
@@ -20,37 +22,12 @@ module RailsJwtAuth
20
22
  send_confirmation_instructions
21
23
  end
22
24
  end
23
-
24
- before_update do
25
- email_field = RailsJwtAuth.email_field_name!
26
-
27
- if public_send("#{email_field}_changed?") &&
28
- public_send("#{email_field}_was") &&
29
- !confirmed_at_changed? &&
30
- !self['invitation_token']
31
- self.unconfirmed_email = self[email_field]
32
- self[email_field] = public_send("#{email_field}_was")
33
-
34
- self.confirmation_token = SecureRandom.base58(24)
35
- self.confirmation_sent_at = Time.current
36
-
37
- mailer = Mailer.confirmation_instructions(self)
38
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
39
-
40
- if RailsJwtAuth.send_email_changed_notification
41
- mailer = Mailer.email_changed(self)
42
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
43
- end
44
- end
45
- end
46
25
  end
47
26
  end
48
27
 
49
28
  def send_confirmation_instructions
50
- email_field = RailsJwtAuth.email_field_name!
51
-
52
29
  if confirmed? && !unconfirmed_email
53
- errors.add(email_field, :already_confirmed)
30
+ errors.add(RailsJwtAuth.email_field_name, :already_confirmed)
54
31
  return false
55
32
  end
56
33
 
@@ -58,8 +35,7 @@ module RailsJwtAuth
58
35
  self.confirmation_sent_at = Time.current
59
36
  return false unless save
60
37
 
61
- mailer = Mailer.confirmation_instructions(self)
62
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
38
+ RailsJwtAuth.send_email(:confirmation_instructions, self)
63
39
  true
64
40
  end
65
41
 
@@ -67,35 +43,68 @@ module RailsJwtAuth
67
43
  confirmed_at.present?
68
44
  end
69
45
 
70
- def confirm!
46
+ def confirm
71
47
  self.confirmed_at = Time.current
72
48
  self.confirmation_token = nil
73
49
 
74
50
  if unconfirmed_email
75
- email_field = RailsJwtAuth.email_field_name!
51
+ email_field = RailsJwtAuth.email_field_name
76
52
 
77
53
  self[email_field] = unconfirmed_email
78
54
  self.unconfirmed_email = nil
79
55
 
80
56
  # supports email confirmation attr_accessor validation
81
57
  if respond_to?("#{email_field}_confirmation")
82
- self.instance_variable_set("@#{email_field}_confirmation", self[email_field])
58
+ instance_variable_set("@#{email_field}_confirmation", self[email_field])
83
59
  end
84
60
  end
85
61
 
86
62
  save
87
63
  end
88
64
 
89
- def skip_confirmation!
65
+ def skip_confirmation
90
66
  self.confirmed_at = Time.current
91
67
  self.confirmation_token = nil
92
68
  end
93
69
 
70
+ def update_email(params)
71
+ email_field = RailsJwtAuth.email_field_name.to_sym
72
+ params = HashWithIndifferentAccess.new(params)
73
+
74
+ # email change must be protected by password
75
+ password_error = if (password = params[:password]).blank?
76
+ :blank
77
+ elsif !authenticate(password)
78
+ :invalid
79
+ end
80
+
81
+ self.email = params[email_field]
82
+ self.confirmation_token = SecureRandom.base58(24)
83
+ self.confirmation_sent_at = Time.current
84
+
85
+ valid? # validates first other fields
86
+ errors.add(:password, password_error) if password_error
87
+ errors.add(email_field, :not_change) unless email_changed?
88
+
89
+ return false unless errors.empty?
90
+
91
+ # move email to unconfirmed_email field and restore
92
+ self.unconfirmed_email = email
93
+ self.email = email_was
94
+
95
+ return false unless save
96
+
97
+ deliver_email_changed_emails
98
+
99
+ true
100
+ end
101
+
94
102
  protected
95
103
 
96
104
  def validate_confirmation
97
105
  return true unless confirmed_at
98
- email_field = RailsJwtAuth.email_field_name!
106
+
107
+ email_field = RailsJwtAuth.email_field_name
99
108
 
100
109
  if confirmed_at_was && !public_send("#{email_field}_changed?")
101
110
  errors.add(email_field, :already_confirmed)
@@ -104,5 +113,15 @@ module RailsJwtAuth
104
113
  errors.add(:confirmation_token, :expired)
105
114
  end
106
115
  end
116
+
117
+ def deliver_email_changed_emails
118
+ # send confirmation to new email
119
+ RailsJwtAuth.send_email(:confirmation_instructions, self)
120
+
121
+ # send notify to old email
122
+ if RailsJwtAuth.send_email_change_requested_notification
123
+ RailsJwtAuth.send_email(:email_change_requested_notification, self)
124
+ end
125
+ end
107
126
  end
108
127
  end
@@ -11,116 +11,80 @@ module RailsJwtAuth
11
11
  field :invitation_token, type: String
12
12
  field :invitation_sent_at, type: Time
13
13
  field :invitation_accepted_at, type: Time
14
- field :invitation_created_at, type: Time
15
14
  end
16
15
  end
17
16
  end
18
17
 
19
18
  module ClassMethods
20
19
  # Creates an user and sends an invitation to him.
21
- # If the user is already invited and pending of completing registration
22
- # the invitation is resent by email.
23
- # If the user is already registered, it returns the user with a
24
- # <tt>:taken</tt> on the email field.
25
- #
26
- # @param [Hash] attributes Hash containing user's attributes to be filled.
27
- # Must contain an email key.
28
- #
29
- # @return [user] The user created or found by email.
30
- def invite!(attributes={})
20
+ def invite(attributes={})
31
21
  attrs = ActiveSupport::HashWithIndifferentAccess.new(attributes.to_h)
32
- auth_field = RailsJwtAuth.auth_field_name!
22
+ auth_field = RailsJwtAuth.auth_field_name
33
23
  auth_attribute = attrs.delete(auth_field)
34
24
 
35
- raise ArgumentError unless auth_attribute
36
-
37
25
  record = RailsJwtAuth.model.find_or_initialize_by(auth_field => auth_attribute)
38
26
  record.assign_attributes(attrs)
39
27
 
40
- record.invite!
28
+ record.invite
41
29
  record
42
30
  end
43
31
  end
44
32
 
45
- # Accept an invitation by clearing token and setting invitation_accepted_at
46
- def accept_invitation
47
- self.invitation_accepted_at = Time.current
48
- self.invitation_token = nil
49
- end
50
-
51
- def accept_invitation!
52
- return unless invited?
53
-
54
- if valid_invitation?
55
- accept_invitation
56
- self.confirmed_at = Time.current if respond_to?(:confirmed_at) && confirmed_at.nil?
57
- else
58
- errors.add(:invitation_token, :invalid)
59
- end
60
- end
61
-
62
- def invite!
63
- self.invitation_created_at = Time.current if new_record?
64
-
65
- unless password || password_digest
66
- passw = SecureRandom.base58(16)
67
- self.password = passw
68
- self.password_confirmation = passw
69
- end
70
-
71
- valid?
72
-
73
- # users that are registered and were not invited are not reinvitable
74
- if !new_record? && !invited?
75
- errors.add(RailsJwtAuth.auth_field_name!, :taken)
33
+ # Sends an invitation to user
34
+ # If the user has pending invitation, new one is sent
35
+ def invite
36
+ if persisted? && !invitation_token
37
+ errors.add(RailsJwtAuth.auth_field_name, :registered)
38
+ return false
76
39
  end
77
40
 
78
- # users that have already accepted an invitation are not reinvitable
79
- if !new_record? && invited? && invitation_accepted_at.present?
80
- errors.add(RailsJwtAuth.auth_field_name!, :taken)
81
- end
82
-
83
- return self unless errors.empty?
84
-
85
- generate_invitation_token if invitation_token.nil?
41
+ @inviting = true
42
+ self.invitation_token = RailsJwtAuth.friendly_token
86
43
  self.invitation_sent_at = Time.current
87
44
 
88
- send_invitation_mail if save(validate: false)
89
- self
90
- end
45
+ return false unless save_without_password
91
46
 
92
- def invited?
93
- (persisted? && invitation_token.present?)
47
+ RailsJwtAuth.send_email(:invitation_instructions, self)
48
+ true
49
+ ensure
50
+ @inviting = false
94
51
  end
95
52
 
96
- def generate_invitation_token!
97
- generate_invitation_token && save(validate: false)
98
- end
53
+ # Finishes invitation process setting user password
54
+ def accept_invitation(params)
55
+ return false unless invitation_token.present?
99
56
 
100
- def valid_invitation?
101
- invited? && invitation_period_valid?
102
- end
57
+ self.assign_attributes(params)
103
58
 
104
- def accepted_invitation?
105
- invitation_token.nil? && invitation_accepted_at.present?
106
- end
59
+ valid?
60
+ errors.add(:password, :blank) if params[:password].blank?
61
+ errors.add(:invitation_token, :expired) if expired_invitation_token?
62
+
63
+ return false unless errors.empty?
107
64
 
108
- protected
65
+ self.invitation_accepted_at = Time.current
66
+ self.invitation_token = nil
67
+ self.invitation_sent_at = nil
68
+ self.confirmed_at = Time.current if respond_to?(:confirmed_at) && confirmed_at.nil?
69
+ save
70
+ end
109
71
 
110
- def generate_invitation_token
111
- self.invitation_token = SecureRandom.base58(128)
72
+ def inviting?
73
+ @inviting || false
112
74
  end
113
75
 
114
- def send_invitation_mail
115
- RailsJwtAuth.email_field_name! # ensure email field es valid
116
- mailer = Mailer.send_invitation(self)
117
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
76
+ def valid_for_invite?
77
+ @inviting = true
78
+ valid_without_password?
79
+ ensure
80
+ @inviting = false
118
81
  end
119
82
 
120
- def invitation_period_valid?
121
- time = invitation_sent_at || invitation_created_at
83
+ def expired_invitation_token?
122
84
  expiration_time = RailsJwtAuth.invitation_expiration_time
123
- time && (expiration_time.to_i.zero? || time >= expiration_time.ago)
85
+ return false if expiration_time.to_i.zero?
86
+
87
+ invitation_sent_at && invitation_sent_at < expiration_time.ago
124
88
  end
125
89
  end
126
90
  end
@@ -13,52 +13,41 @@ module RailsJwtAuth
13
13
  end
14
14
  end
15
15
 
16
- def lock_access!
17
- self.locked_at = Time.now.utc
16
+ def lock_access
17
+ self.locked_at = Time.current
18
+
18
19
  save(validate: false).tap do |result|
19
20
  send_unlock_instructions if result && unlock_strategy_enabled?(:email)
20
21
  end
21
22
  end
22
23
 
23
- def unlock_access!
24
+ def clean_lock
24
25
  self.locked_at = nil
25
- self.failed_attempts = 0
26
- self.first_failed_attempt_at = nil
27
26
  self.unlock_token = nil
28
- save(validate: false)
27
+ reset_attempts
29
28
  end
30
29
 
31
- def reset_attempts!
32
- self.failed_attempts = 0
33
- self.first_failed_attempt_at = nil
34
- save(validate: false)
30
+ def unlock_access
31
+ clean_lock
32
+
33
+ save(validate: false) if changed?
35
34
  end
36
35
 
37
- def authentication?(pass)
38
- return super(pass) unless lock_strategy_enabled?(:failed_attempts)
36
+ def access_locked?
37
+ locked_at && !lock_expired?
38
+ end
39
39
 
40
- reset_attempts! if !access_locked? && attempts_expired?
41
- unlock_access! if lock_expired?
40
+ def failed_attempt
41
+ return if access_locked?
42
42
 
43
- if access_locked?
44
- false
45
- elsif super(pass)
46
- unlock_access!
47
- self
48
- else
49
- failed_attempt!
50
- lock_access! if attempts_exceeded?
51
- false
52
- end
53
- end
43
+ reset_attempts if attempts_expired?
54
44
 
55
- def unauthenticated_error
56
- return super unless lock_strategy_enabled?(:failed_attempts)
45
+ self.failed_attempts ||= 0
46
+ self.failed_attempts += 1
47
+ self.first_failed_attempt_at = Time.current if failed_attempts == 1
57
48
 
58
- if access_locked?
59
- {error: :locked}
60
- else
61
- {error: :invalid_session, remaining_attempts: remaining_attempts}
49
+ save(validate: false).tap do |result|
50
+ lock_access if result && attempts_exceeded?
62
51
  end
63
52
  end
64
53
 
@@ -68,12 +57,7 @@ module RailsJwtAuth
68
57
  self.unlock_token = SecureRandom.base58(24)
69
58
  save(validate: false)
70
59
 
71
- mailer = Mailer.send_unlock_instructions(self)
72
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
73
- end
74
-
75
- def access_locked?
76
- locked_at && !lock_expired?
60
+ RailsJwtAuth.send_email(:unlock_instructions, self)
77
61
  end
78
62
 
79
63
  def lock_expired?
@@ -84,21 +68,19 @@ module RailsJwtAuth
84
68
  end
85
69
  end
86
70
 
87
- def failed_attempt!
88
- self.failed_attempts ||= 0
89
- self.failed_attempts += 1
90
- self.first_failed_attempt_at = Time.now.utc if failed_attempts == 1
91
- save(validate: false)
92
- end
93
-
94
- def attempts_exceeded?
95
- failed_attempts && failed_attempts >= RailsJwtAuth.maximum_attempts
71
+ def reset_attempts
72
+ self.failed_attempts = 0
73
+ self.first_failed_attempt_at = nil
96
74
  end
97
75
 
98
76
  def remaining_attempts
99
77
  RailsJwtAuth.maximum_attempts - failed_attempts.to_i
100
78
  end
101
79
 
80
+ def attempts_exceeded?
81
+ !remaining_attempts.positive?
82
+ end
83
+
102
84
  def attempts_expired?
103
85
  first_failed_attempt_at && first_failed_attempt_at < RailsJwtAuth.reset_attempts_in.ago
104
86
  end
@@ -10,20 +10,11 @@ module RailsJwtAuth
10
10
  field :reset_password_token, type: String
11
11
  field :reset_password_sent_at, type: Time
12
12
  end
13
-
14
- validate :validate_reset_password_token, if: :password_digest_changed?
15
-
16
- before_update do
17
- if password_digest_changed? && reset_password_token
18
- self.reset_password_token = nil
19
- self.auth_tokens = []
20
- end
21
- end
22
13
  end
23
14
  end
24
15
 
25
16
  def send_reset_password_instructions
26
- email_field = RailsJwtAuth.email_field_name! # ensure email field es valid
17
+ email_field = RailsJwtAuth.email_field_name # ensure email field es valid
27
18
 
28
19
  if self.class.ancestors.include?(RailsJwtAuth::Confirmable) && !confirmed?
29
20
  errors.add(email_field, :unconfirmed)
@@ -36,38 +27,37 @@ module RailsJwtAuth
36
27
  return false
37
28
  end
38
29
 
39
- self.reset_password_token = SecureRandom.base58(24)
30
+ self.reset_password_token = RailsJwtAuth.friendly_token
40
31
  self.reset_password_sent_at = Time.current
41
32
  return false unless save
42
33
 
43
- mailer = Mailer.reset_password_instructions(self)
44
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
34
+ RailsJwtAuth.send_email(:reset_password_instructions, self)
45
35
  end
46
36
 
47
- def set_and_send_password_instructions
48
- RailsJwtAuth.email_field_name! # ensure email field es valid
49
- return if password.present?
37
+ def set_reset_password(params)
38
+ self.assign_attributes(params)
50
39
 
51
- self.password = SecureRandom.base58(48)
52
- self.password_confirmation = self.password
53
- self.skip_confirmation! if self.class.ancestors.include?(RailsJwtAuth::Confirmable)
40
+ valid?
41
+ errors.add(:password, :blank) if params[:password].blank?
42
+ errors.add(:reset_password_token, :expired) if expired_reset_password_token?
54
43
 
55
- self.reset_password_token = SecureRandom.base58(24)
56
- self.reset_password_sent_at = Time.current
57
- return false unless save
44
+ return false unless errors.empty?
58
45
 
59
- mailer = Mailer.set_password_instructions(self)
60
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
61
- true
46
+ clean_reset_password
47
+ self.auth_tokens = [] # reset all sessions
48
+ save
62
49
  end
63
50
 
64
- protected
51
+ def expired_reset_password_token?
52
+ expiration_time = RailsJwtAuth.reset_password_expiration_time
53
+ return false if expiration_time.to_i.zero?
65
54
 
66
- def validate_reset_password_token
67
- if reset_password_sent_at &&
68
- (reset_password_sent_at < (Time.current - RailsJwtAuth.reset_password_expiration_time))
69
- errors.add(:reset_password_token, :expired)
70
- end
55
+ reset_password_sent_at && reset_password_sent_at < expiration_time.ago
56
+ end
57
+
58
+ def clean_reset_password
59
+ self.reset_password_sent_at = nil
60
+ self.reset_password_token = nil
71
61
  end
72
62
  end
73
63
  end