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.
- checksums.yaml +4 -4
- data/README.md +187 -88
- data/app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb +15 -7
- data/app/controllers/concerns/rails_jwt_auth/params_helper.rb +18 -4
- data/app/controllers/concerns/rails_jwt_auth/render_helper.rb +10 -2
- data/app/controllers/rails_jwt_auth/confirmations_controller.rb +48 -10
- data/app/controllers/rails_jwt_auth/invitations_controller.rb +26 -9
- data/app/controllers/rails_jwt_auth/profiles_controller.rb +50 -0
- data/app/controllers/rails_jwt_auth/reset_passwords_controller.rb +65 -0
- data/app/controllers/rails_jwt_auth/sessions_controller.rb +5 -21
- data/app/controllers/rails_jwt_auth/{unlocks_controller.rb → unlock_accounts_controller.rb} +2 -2
- data/app/mailers/rails_jwt_auth/mailer.rb +29 -42
- data/app/models/concerns/rails_jwt_auth/authenticatable.rb +60 -19
- data/app/models/concerns/rails_jwt_auth/confirmable.rb +52 -33
- data/app/models/concerns/rails_jwt_auth/invitable.rb +42 -78
- data/app/models/concerns/rails_jwt_auth/lockable.rb +28 -46
- data/app/models/concerns/rails_jwt_auth/recoverable.rb +21 -31
- data/app/models/concerns/rails_jwt_auth/trackable.rb +13 -2
- data/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb +1 -1
- data/app/views/rails_jwt_auth/mailer/{email_changed.html.erb → email_change_requested_notification.html.erb} +0 -0
- data/app/views/rails_jwt_auth/mailer/{send_invitation.html.erb → invitation_instructions.html.erb} +1 -1
- data/app/views/rails_jwt_auth/mailer/password_changed_notification.html.erb +3 -0
- data/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb +1 -1
- data/app/views/rails_jwt_auth/mailer/{send_unlock_instructions.html.erb → unlock_instructions.html.erb} +1 -1
- data/config/locales/en.yml +6 -6
- data/lib/generators/rails_jwt_auth/install_generator.rb +11 -3
- data/lib/generators/templates/initializer.rb +43 -29
- data/lib/generators/templates/migration.rb +2 -1
- data/lib/rails_jwt_auth.rb +48 -45
- data/lib/rails_jwt_auth/jwt_manager.rb +2 -4
- data/lib/rails_jwt_auth/session.rb +132 -0
- data/lib/rails_jwt_auth/version.rb +1 -1
- metadata +10 -8
- data/app/controllers/rails_jwt_auth/passwords_controller.rb +0 -32
- 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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
79
|
-
|
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
|
-
|
89
|
-
self
|
90
|
-
end
|
45
|
+
return false unless save_without_password
|
91
46
|
|
92
|
-
|
93
|
-
|
47
|
+
RailsJwtAuth.send_email(:invitation_instructions, self)
|
48
|
+
true
|
49
|
+
ensure
|
50
|
+
@inviting = false
|
94
51
|
end
|
95
52
|
|
96
|
-
|
97
|
-
|
98
|
-
|
53
|
+
# Finishes invitation process setting user password
|
54
|
+
def accept_invitation(params)
|
55
|
+
return false unless invitation_token.present?
|
99
56
|
|
100
|
-
|
101
|
-
invited? && invitation_period_valid?
|
102
|
-
end
|
57
|
+
self.assign_attributes(params)
|
103
58
|
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
111
|
-
|
72
|
+
def inviting?
|
73
|
+
@inviting || false
|
112
74
|
end
|
113
75
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
76
|
+
def valid_for_invite?
|
77
|
+
@inviting = true
|
78
|
+
valid_without_password?
|
79
|
+
ensure
|
80
|
+
@inviting = false
|
118
81
|
end
|
119
82
|
|
120
|
-
def
|
121
|
-
time = invitation_sent_at || invitation_created_at
|
83
|
+
def expired_invitation_token?
|
122
84
|
expiration_time = RailsJwtAuth.invitation_expiration_time
|
123
|
-
|
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.
|
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
|
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
|
-
|
27
|
+
reset_attempts
|
29
28
|
end
|
30
29
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
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
|
38
|
-
|
36
|
+
def access_locked?
|
37
|
+
locked_at && !lock_expired?
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
def failed_attempt
|
41
|
+
return if access_locked?
|
42
42
|
|
43
|
-
if
|
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
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
88
|
-
self.failed_attempts
|
89
|
-
self.
|
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
|
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 =
|
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
|
-
|
44
|
-
RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
|
34
|
+
RailsJwtAuth.send_email(:reset_password_instructions, self)
|
45
35
|
end
|
46
36
|
|
47
|
-
def
|
48
|
-
|
49
|
-
return if password.present?
|
37
|
+
def set_reset_password(params)
|
38
|
+
self.assign_attributes(params)
|
50
39
|
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
-
self.reset_password_sent_at = Time.current
|
57
|
-
return false unless save
|
44
|
+
return false unless errors.empty?
|
58
45
|
|
59
|
-
|
60
|
-
|
61
|
-
|
46
|
+
clean_reset_password
|
47
|
+
self.auth_tokens = [] # reset all sessions
|
48
|
+
save
|
62
49
|
end
|
63
50
|
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|