rodauth 2.45.0 → 2.47.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.
- checksums.yaml +4 -4
- data/lib/rodauth/features/active_sessions.rb +2 -2
- data/lib/rodauth/features/audit_logging.rb +2 -1
- data/lib/rodauth/features/base.rb +65 -10
- data/lib/rodauth/features/change_password.rb +12 -2
- data/lib/rodauth/features/close_account_email.rb +17 -0
- data/lib/rodauth/features/confirm_password.rb +1 -1
- data/lib/rodauth/features/create_account.rb +2 -1
- data/lib/rodauth/features/email_auth.rb +13 -4
- data/lib/rodauth/features/email_base.rb +37 -3
- data/lib/rodauth/features/internal_request.rb +4 -0
- data/lib/rodauth/features/json.rb +5 -1
- data/lib/rodauth/features/jwt.rb +7 -1
- data/lib/rodauth/features/jwt_cors.rb +2 -0
- data/lib/rodauth/features/jwt_refresh.rb +35 -22
- data/lib/rodauth/features/lockout.rb +22 -19
- data/lib/rodauth/features/login.rb +12 -4
- data/lib/rodauth/features/login_password_requirements_base.rb +39 -9
- data/lib/rodauth/features/otp.rb +19 -10
- data/lib/rodauth/features/password_expiration.rb +0 -1
- data/lib/rodauth/features/password_grace_period.rb +7 -0
- data/lib/rodauth/features/password_pepper.rb +11 -1
- data/lib/rodauth/features/recovery_codes.rb +4 -1
- data/lib/rodauth/features/remember.rb +2 -0
- data/lib/rodauth/features/require_domain.rb +12 -0
- data/lib/rodauth/features/require_hmac_secret.rb +12 -0
- data/lib/rodauth/features/reset_password.rb +25 -20
- data/lib/rodauth/features/single_session.rb +6 -2
- data/lib/rodauth/features/sms_codes.rb +4 -1
- data/lib/rodauth/features/two_factor_base.rb +2 -1
- data/lib/rodauth/features/update_password_hash.rb +5 -1
- data/lib/rodauth/features/verify_account.rb +40 -22
- data/lib/rodauth/features/verify_login_change.rb +19 -11
- data/lib/rodauth/features/webauthn.rb +10 -5
- data/lib/rodauth/features/webauthn_login.rb +1 -5
- data/lib/rodauth/features/webauthn_verify_account.rb +0 -4
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +2 -0
- data/templates/close-account-email.str +2 -0
- metadata +6 -2
|
@@ -116,16 +116,19 @@ module Rodauth
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
r.post do
|
|
119
|
-
key =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
key = session_param(unlock_account_session_key, unlock_account_key_param)
|
|
120
|
+
select_key_for_update!
|
|
121
|
+
|
|
122
|
+
transaction do
|
|
123
|
+
unless key && account_from_unlock_key(key)
|
|
124
|
+
remove_session_value(unlock_account_session_key)
|
|
125
|
+
set_redirect_error_status invalid_key_error_status
|
|
126
|
+
set_error_reason :invalid_unlock_account_key
|
|
127
|
+
set_redirect_error_flash no_matching_unlock_account_key_error_flash
|
|
128
|
+
redirect unlock_account_request_redirect
|
|
129
|
+
end
|
|
126
130
|
|
|
127
|
-
|
|
128
|
-
transaction do
|
|
131
|
+
if !unlock_account_requires_password? || password_match?(param(password_param))
|
|
129
132
|
before_unlock_account
|
|
130
133
|
unlock_account
|
|
131
134
|
clear_tokens(:unlock_account)
|
|
@@ -133,15 +136,15 @@ module Rodauth
|
|
|
133
136
|
if unlock_account_autologin?
|
|
134
137
|
autologin_session('unlock_account')
|
|
135
138
|
end
|
|
136
|
-
end
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
remove_session_value(unlock_account_session_key)
|
|
141
|
+
unlock_account_response
|
|
142
|
+
else
|
|
143
|
+
set_response_error_reason_status(:invalid_password, invalid_password_error_status)
|
|
144
|
+
set_field_error(password_param, invalid_password_message)
|
|
145
|
+
set_error_flash unlock_account_error_flash
|
|
146
|
+
unlock_account_view
|
|
147
|
+
end
|
|
145
148
|
end
|
|
146
149
|
end
|
|
147
150
|
end
|
|
@@ -221,7 +224,7 @@ module Rodauth
|
|
|
221
224
|
end
|
|
222
225
|
|
|
223
226
|
def account_from_unlock_key(key)
|
|
224
|
-
|
|
227
|
+
set_account(_account_from_unlock_key(key), :unlock)
|
|
225
228
|
end
|
|
226
229
|
|
|
227
230
|
def unlock_account_email_link
|
|
@@ -310,7 +313,7 @@ module Rodauth
|
|
|
310
313
|
end
|
|
311
314
|
|
|
312
315
|
def _account_from_unlock_key(token)
|
|
313
|
-
account_from_key(token){|id| account_lockouts_ds(id).get(account_lockouts_key_column)}
|
|
316
|
+
account_from_key(token){|id| apply_key_for_update(account_lockouts_ds(id)).get(account_lockouts_key_column)}
|
|
314
317
|
end
|
|
315
318
|
end
|
|
316
319
|
end
|
|
@@ -15,7 +15,7 @@ module Rodauth
|
|
|
15
15
|
auth_value_method :login_error_status, 401
|
|
16
16
|
translatable_method :login_form_footer_links_heading, '<h2 class="rodauth-login-form-footer-links-heading">Other Options</h2>'
|
|
17
17
|
auth_value_method :login_return_to_requested_location?, false
|
|
18
|
-
|
|
18
|
+
def_deprecated_alias :return_to_path_max_size, :login_return_to_requested_location_max_path_size
|
|
19
19
|
auth_value_method :use_multi_phase_login?, false
|
|
20
20
|
|
|
21
21
|
session_key :login_redirect_session_key, :login_redirect
|
|
@@ -106,14 +106,14 @@ module Rodauth
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def login_required
|
|
109
|
-
if login_return_to_requested_location?
|
|
110
|
-
|
|
109
|
+
if login_return_to_requested_location?
|
|
110
|
+
set_session_return_to_path(login_redirect_session_key, login_return_to_requested_location_path)
|
|
111
111
|
end
|
|
112
112
|
super
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def login_return_to_requested_location_path
|
|
116
|
-
request.fullpath
|
|
116
|
+
request.fullpath
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def after_login_entered_during_multi_phase_login
|
|
@@ -153,6 +153,14 @@ module Rodauth
|
|
|
153
153
|
login_path
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
def post_configure
|
|
157
|
+
# RODAUTH3: Remove
|
|
158
|
+
super
|
|
159
|
+
if method(:already_logged_in).owner == Rodauth::Base
|
|
160
|
+
missing_recommended_configuration("The Rodauth 'already_logged_in' configuration method was not used. Use the configuration method to define the behavior when accessing a page while logged in that is expected to be accessed when not logged in (safest to halt or redirect).")
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
156
164
|
private
|
|
157
165
|
|
|
158
166
|
def _login_response
|
|
@@ -12,7 +12,7 @@ module Rodauth
|
|
|
12
12
|
translatable_method :logins_do_not_match_message, 'logins do not match'
|
|
13
13
|
auth_value_method :password_confirm_param, 'password-confirm'
|
|
14
14
|
auth_value_method :password_minimum_length, 6
|
|
15
|
-
auth_value_method :password_maximum_bytes, nil
|
|
15
|
+
auth_value_method :password_maximum_bytes, nil # RODAUTH3: 72 if bcrypt is used
|
|
16
16
|
auth_value_method :password_maximum_length, nil
|
|
17
17
|
translatable_method :passwords_do_not_match_message, 'passwords do not match'
|
|
18
18
|
auth_value_method :require_email_address_logins?, true
|
|
@@ -67,13 +67,39 @@ module Rodauth
|
|
|
67
67
|
def set_password(password)
|
|
68
68
|
hash = password_hash(password)
|
|
69
69
|
if account_password_hash_column
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
ds = account_ds
|
|
71
|
+
|
|
72
|
+
if @existing_password_hash_or_salt
|
|
73
|
+
ds = ds.where(account_password_hash_column => @existing_password_hash_or_salt)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
unless update_account({account_password_hash_column=>hash}, ds) == 1
|
|
77
|
+
raise SetPasswordFailure, "set_password did not update password, either due to missing account, or concurrent password change"
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
ph_ds = ds = password_hash_ds
|
|
81
|
+
if @existing_password_hash_or_salt
|
|
82
|
+
existing_expression = if use_database_authentication_functions?
|
|
83
|
+
Sequel.function(function_name(:rodauth_get_salt), :id)
|
|
84
|
+
# :nocov:
|
|
85
|
+
else
|
|
86
|
+
password_hash_column
|
|
87
|
+
# :nocov:
|
|
88
|
+
end
|
|
89
|
+
ds = ds.where(existing_expression => @existing_password_hash_or_salt)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if ds.update(password_hash_column=>hash) == 0
|
|
93
|
+
if ph_ds.empty?
|
|
94
|
+
# This shouldn't raise a uniqueness error, as the update should only fail for a new user,
|
|
95
|
+
# and an existing user should always have a valid password hash row. If this does
|
|
96
|
+
# fail, retrying it will cause problems, it will override a concurrently running update
|
|
97
|
+
# with potentially a different password.
|
|
98
|
+
db[password_hash_table].insert(password_hash_id_column=>account_id, password_hash_column=>hash)
|
|
99
|
+
else
|
|
100
|
+
raise SetPasswordFailure, "set_password did not update password due to concurrent password change"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
77
103
|
end
|
|
78
104
|
hash
|
|
79
105
|
end
|
|
@@ -174,7 +200,7 @@ module Rodauth
|
|
|
174
200
|
elsif password_maximum_length && password_maximum_length < password.length
|
|
175
201
|
set_password_requirement_error_message(:password_too_long, password_too_long_message)
|
|
176
202
|
false
|
|
177
|
-
elsif password_maximum_bytes && password_maximum_bytes < password
|
|
203
|
+
elsif password_maximum_bytes && password_maximum_bytes < password_bytesize(password)
|
|
178
204
|
set_password_requirement_error_message(:password_too_many_bytes, password_too_many_bytes_message)
|
|
179
205
|
false
|
|
180
206
|
else
|
|
@@ -182,6 +208,10 @@ module Rodauth
|
|
|
182
208
|
end
|
|
183
209
|
end
|
|
184
210
|
|
|
211
|
+
def password_bytesize(password)
|
|
212
|
+
password.bytesize
|
|
213
|
+
end
|
|
214
|
+
|
|
185
215
|
def password_does_not_contain_null_byte?(password)
|
|
186
216
|
return true unless password.include?("\0")
|
|
187
217
|
set_password_requirement_error_message(:password_contains_null_byte, contains_null_byte_message)
|
data/lib/rodauth/features/otp.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'rqrcode'
|
|
|
5
5
|
|
|
6
6
|
module Rodauth
|
|
7
7
|
Feature.define(:otp, :Otp) do
|
|
8
|
-
depends :two_factor_base
|
|
8
|
+
depends :two_factor_base, :require_domain, :require_hmac_secret
|
|
9
9
|
|
|
10
10
|
additional_form_tags 'otp_disable'
|
|
11
11
|
additional_form_tags 'otp_auth'
|
|
@@ -127,13 +127,18 @@ module Rodauth
|
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
r.post do
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
transaction do
|
|
131
|
+
otp_key_ds.for_update.first
|
|
132
|
+
|
|
133
|
+
if otp_valid_code?(param(otp_auth_param)) && otp_update_last_use
|
|
134
|
+
before_otp_authentication
|
|
135
|
+
two_factor_authenticate('totp')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
otp_record_authentication_failure
|
|
139
|
+
after_otp_authentication_failure
|
|
133
140
|
end
|
|
134
141
|
|
|
135
|
-
otp_record_authentication_failure
|
|
136
|
-
after_otp_authentication_failure
|
|
137
142
|
set_response_error_reason_status(:invalid_otp_auth_code, invalid_key_error_status)
|
|
138
143
|
set_field_error(otp_auth_param, otp_invalid_auth_code_message)
|
|
139
144
|
set_error_flash otp_auth_error_flash
|
|
@@ -265,15 +270,19 @@ module Rodauth
|
|
|
265
270
|
return false unless otp_exists?
|
|
266
271
|
ot_pass = ot_pass.gsub(/\s+/, '')
|
|
267
272
|
if drift = otp_drift
|
|
273
|
+
# :nocov:
|
|
268
274
|
if otp.respond_to?(:verify_with_drift)
|
|
269
|
-
# :nocov:
|
|
270
275
|
otp.verify_with_drift(ot_pass, drift)
|
|
271
|
-
|
|
276
|
+
# :nocov:
|
|
272
277
|
else
|
|
273
|
-
otp.verify(ot_pass, :drift_behind=>drift, :drift_ahead=>drift)
|
|
278
|
+
otp.verify(ot_pass, :drift_behind=>drift, :drift_ahead=>drift, :after=>otp_last_use)
|
|
274
279
|
end
|
|
275
|
-
|
|
280
|
+
# :nocov:
|
|
281
|
+
elsif otp.respond_to?(:verify_with_drift)
|
|
276
282
|
otp.verify(ot_pass)
|
|
283
|
+
# :nocov:
|
|
284
|
+
else
|
|
285
|
+
otp.verify(ot_pass, :after=>otp_last_use)
|
|
277
286
|
end
|
|
278
287
|
end
|
|
279
288
|
|
|
@@ -70,7 +70,6 @@ module Rodauth
|
|
|
70
70
|
return password_changed_at + require_password_change_after < Time.now.to_i
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
account_from_session
|
|
74
73
|
if password_changed_at = get_password_changed_at
|
|
75
74
|
set_session_value(password_changed_at_session_key, password_changed_at.to_i)
|
|
76
75
|
password_changed_at + require_password_change_after < Time.now
|
|
@@ -33,6 +33,13 @@ module Rodauth
|
|
|
33
33
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
|
+
def new_password_matches_current_password?(_)
|
|
37
|
+
if v = super
|
|
38
|
+
session.delete(last_password_entry_session_key)
|
|
39
|
+
end
|
|
40
|
+
v
|
|
41
|
+
end
|
|
42
|
+
|
|
36
43
|
def after_create_account
|
|
37
44
|
super if defined?(super)
|
|
38
45
|
@last_password_entry = Time.now.to_i
|
|
@@ -12,7 +12,11 @@ module Rodauth
|
|
|
12
12
|
|
|
13
13
|
def password_match?(password)
|
|
14
14
|
if (result = super) && @previous_pepper_matched && password_pepper_update?
|
|
15
|
-
|
|
15
|
+
begin
|
|
16
|
+
set_password(password)
|
|
17
|
+
rescue Rodauth::SetPasswordFailure
|
|
18
|
+
return false
|
|
19
|
+
end
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
result
|
|
@@ -24,6 +28,12 @@ module Rodauth
|
|
|
24
28
|
|
|
25
29
|
private
|
|
26
30
|
|
|
31
|
+
def password_bytesize(password)
|
|
32
|
+
n = super
|
|
33
|
+
n += password_pepper.bytesize if password_pepper
|
|
34
|
+
n
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
def password_hash_match?(hash, password)
|
|
28
38
|
return super if password_pepper.nil?
|
|
29
39
|
|
|
@@ -162,7 +162,10 @@ module Rodauth
|
|
|
162
162
|
def recovery_code_match?(code)
|
|
163
163
|
recovery_codes.each do |s|
|
|
164
164
|
if timing_safe_eql?(code, s)
|
|
165
|
-
recovery_codes_ds.where(recovery_codes_column=>code).delete
|
|
165
|
+
unless recovery_codes_ds.where(recovery_codes_column=>code).delete == 1
|
|
166
|
+
return false
|
|
167
|
+
end
|
|
168
|
+
|
|
166
169
|
if recovery_codes_primary?
|
|
167
170
|
add_recovery_code
|
|
168
171
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
Feature.define(:require_domain, :RequireDomain) do
|
|
5
|
+
def post_configure
|
|
6
|
+
super
|
|
7
|
+
if method(:domain).owner == Rodauth::Base
|
|
8
|
+
missing_recommended_configuration("The Rodauth 'domain' configuration method was not used. This can potentially leave the application vulnerable to requests that use malicious or invalid domains.")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
Feature.define(:require_hmac_secret, :RequireHmacSecret) do
|
|
5
|
+
def post_configure
|
|
6
|
+
super
|
|
7
|
+
if method(:hmac_secret).owner == Rodauth::Base
|
|
8
|
+
missing_recommended_configuration("The Rodauth 'hmac_secret' configuration method was not used. This significantly reduces Rodauth's security. Consult the Rodauth README for what this method does, and set an appropriate value.")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -120,29 +120,26 @@ module Rodauth
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
r.post do
|
|
123
|
-
key =
|
|
124
|
-
|
|
125
|
-
set_redirect_error_status(invalid_key_error_status)
|
|
126
|
-
set_error_reason :invalid_reset_password_key
|
|
127
|
-
set_redirect_error_flash reset_password_error_flash
|
|
128
|
-
redirect reset_password_email_sent_redirect
|
|
129
|
-
end
|
|
130
|
-
|
|
123
|
+
key = session_param(reset_password_session_key, reset_password_key_param)
|
|
124
|
+
select_key_for_update!
|
|
131
125
|
password = param(password_param)
|
|
126
|
+
|
|
132
127
|
catch_error do
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
end
|
|
128
|
+
transaction do
|
|
129
|
+
handle_invalid_reset_password_key unless key && account_from_reset_password_key(key)
|
|
136
130
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
unless password_meets_requirements?(password)
|
|
132
|
+
throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message)
|
|
133
|
+
end
|
|
140
134
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
if password_match?(password)
|
|
136
|
+
throw_error_reason(:same_as_existing_password, invalid_field_error_status, password_param, same_as_existing_password_message)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if require_password_confirmation? && password != param(password_confirm_param)
|
|
140
|
+
throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, password_param, passwords_do_not_match_message)
|
|
141
|
+
end
|
|
144
142
|
|
|
145
|
-
transaction do
|
|
146
143
|
before_reset_password
|
|
147
144
|
set_password(password)
|
|
148
145
|
clear_tokens(:reset_password)
|
|
@@ -184,7 +181,7 @@ module Rodauth
|
|
|
184
181
|
end
|
|
185
182
|
|
|
186
183
|
def account_from_reset_password_key(key)
|
|
187
|
-
|
|
184
|
+
set_account(_account_from_reset_password_key(key), :reset_password)
|
|
188
185
|
end
|
|
189
186
|
|
|
190
187
|
def reset_password_email_link
|
|
@@ -199,7 +196,7 @@ module Rodauth
|
|
|
199
196
|
def get_reset_password_key(id)
|
|
200
197
|
ds = password_reset_ds(id)
|
|
201
198
|
ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete
|
|
202
|
-
ds.get(reset_password_key_column)
|
|
199
|
+
apply_key_for_update(ds).get(reset_password_key_column)
|
|
203
200
|
end
|
|
204
201
|
|
|
205
202
|
def set_reset_password_email_last_sent
|
|
@@ -267,5 +264,13 @@ module Rodauth
|
|
|
267
264
|
def reset_password_account_status_value
|
|
268
265
|
account_open_status_value
|
|
269
266
|
end
|
|
267
|
+
|
|
268
|
+
def handle_invalid_reset_password_key
|
|
269
|
+
remove_session_value(reset_password_session_key)
|
|
270
|
+
set_redirect_error_status(invalid_key_error_status)
|
|
271
|
+
set_error_reason :invalid_reset_password_key
|
|
272
|
+
set_redirect_error_flash reset_password_error_flash
|
|
273
|
+
redirect reset_password_email_sent_redirect
|
|
274
|
+
end
|
|
270
275
|
end
|
|
271
276
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:single_session, :SingleSession) do
|
|
5
|
+
depends :require_hmac_secret
|
|
6
|
+
|
|
5
7
|
error_flash 'This session has been logged out as another session has become active'
|
|
6
8
|
redirect
|
|
7
9
|
|
|
@@ -29,13 +31,15 @@ module Rodauth
|
|
|
29
31
|
single_session_key = session[single_session_session_key]
|
|
30
32
|
current_key = single_session_ds.get(single_session_key_column)
|
|
31
33
|
if single_session_key.nil?
|
|
32
|
-
|
|
34
|
+
if current_key
|
|
35
|
+
false
|
|
36
|
+
else
|
|
33
37
|
# No row exists for this user, indicating the feature has never
|
|
34
38
|
# been used, so it is OK to treat the current session as a new
|
|
35
39
|
# session.
|
|
36
40
|
update_single_session_key
|
|
41
|
+
true
|
|
37
42
|
end
|
|
38
|
-
true
|
|
39
43
|
elsif current_key
|
|
40
44
|
if hmac_secret && !(valid = timing_safe_eql?(single_session_key, hmac = compute_hmac(current_key)))
|
|
41
45
|
if hmac_secret_rotation? && (valid = timing_safe_eql?(single_session_key, compute_old_hmac(current_key)))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:sms_codes, :SmsCodes) do
|
|
5
|
-
depends :two_factor_base
|
|
5
|
+
depends :two_factor_base, :require_domain
|
|
6
6
|
|
|
7
7
|
additional_form_tags 'sms_auth'
|
|
8
8
|
additional_form_tags 'sms_confirm'
|
|
@@ -176,6 +176,9 @@ module Rodauth
|
|
|
176
176
|
|
|
177
177
|
r.post do
|
|
178
178
|
transaction do
|
|
179
|
+
@sms = sms_ds.for_update.first
|
|
180
|
+
require_sms_available
|
|
181
|
+
|
|
179
182
|
if sms_code_match?(param(sms_code_param))
|
|
180
183
|
before_sms_auth
|
|
181
184
|
sms_remove_failures
|
|
@@ -158,7 +158,7 @@ module Rodauth
|
|
|
158
158
|
def require_two_factor_authenticated
|
|
159
159
|
unless two_factor_authenticated?
|
|
160
160
|
if two_factor_auth_return_to_requested_location?
|
|
161
|
-
|
|
161
|
+
set_session_return_to_path(two_factor_auth_redirect_session_key)
|
|
162
162
|
end
|
|
163
163
|
set_redirect_error_status(two_factor_need_authentication_error_status)
|
|
164
164
|
set_error_reason :two_factor_need_authentication
|
|
@@ -262,6 +262,7 @@ module Rodauth
|
|
|
262
262
|
end
|
|
263
263
|
|
|
264
264
|
def two_factor_update_session(auth_type)
|
|
265
|
+
raise Error, "attempt to have two factor authentication using same factor" if authenticated_by.include?(auth_type)
|
|
265
266
|
authenticated_by << auth_type
|
|
266
267
|
set_session_value(two_factor_setup_session_key, true)
|
|
267
268
|
end
|
|
@@ -9,7 +9,11 @@ module Rodauth
|
|
|
9
9
|
def password_match?(password)
|
|
10
10
|
if (result = super) && update_password_hash?
|
|
11
11
|
@update_password_hash = false
|
|
12
|
-
|
|
12
|
+
begin
|
|
13
|
+
set_password(password)
|
|
14
|
+
rescue Rodauth::SetPasswordFailure
|
|
15
|
+
return false
|
|
16
|
+
end
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
result
|
|
@@ -114,30 +114,30 @@ module Rodauth
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
r.post do
|
|
117
|
-
key =
|
|
118
|
-
|
|
119
|
-
set_redirect_error_status(invalid_key_error_status)
|
|
120
|
-
set_error_reason :invalid_verify_account_key
|
|
121
|
-
set_redirect_error_flash verify_account_error_flash
|
|
122
|
-
redirect verify_account_redirect
|
|
123
|
-
end
|
|
117
|
+
key = session_param(verify_account_session_key, verify_account_key_param)
|
|
118
|
+
select_key_for_update!
|
|
124
119
|
|
|
125
120
|
catch_error do
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
transaction do
|
|
122
|
+
handle_invalid_verify_account_key unless key && account_from_verify_account_key(key)
|
|
128
123
|
|
|
129
|
-
if
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
if verify_account_set_password?
|
|
125
|
+
password = param(password_param)
|
|
126
|
+
|
|
127
|
+
if require_password_confirmation? && password != param(password_confirm_param)
|
|
128
|
+
throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, password_param, passwords_do_not_match_message)
|
|
129
|
+
end
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
unless password_meets_requirements?(password)
|
|
132
|
+
throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message)
|
|
133
|
+
end
|
|
135
134
|
end
|
|
136
|
-
end
|
|
137
135
|
|
|
138
|
-
transaction do
|
|
139
136
|
before_verify_account
|
|
140
|
-
verify_account
|
|
137
|
+
unless verify_account
|
|
138
|
+
remove_verify_account_key
|
|
139
|
+
handle_invalid_verify_account_key
|
|
140
|
+
end
|
|
141
141
|
if verify_account_set_password?
|
|
142
142
|
set_password(password)
|
|
143
143
|
end
|
|
@@ -171,7 +171,8 @@ module Rodauth
|
|
|
171
171
|
end
|
|
172
172
|
|
|
173
173
|
def verify_account
|
|
174
|
-
|
|
174
|
+
ds = account_ds.where(account_status_column=>account_unverified_status_value)
|
|
175
|
+
update_account({account_status_column=>account_open_status_value}, ds) == 1
|
|
175
176
|
end
|
|
176
177
|
|
|
177
178
|
def verify_account_email_resend
|
|
@@ -196,7 +197,7 @@ module Rodauth
|
|
|
196
197
|
end
|
|
197
198
|
|
|
198
199
|
def account_from_verify_account_key(key)
|
|
199
|
-
|
|
200
|
+
set_account(_account_from_verify_account_key(key), :verify_account)
|
|
200
201
|
end
|
|
201
202
|
|
|
202
203
|
def account_initial_status_value
|
|
@@ -208,7 +209,7 @@ module Rodauth
|
|
|
208
209
|
end
|
|
209
210
|
|
|
210
211
|
def get_verify_account_key(id)
|
|
211
|
-
verify_account_ds(id).get(verify_account_key_column)
|
|
212
|
+
apply_key_for_update(verify_account_ds(id)).get(verify_account_key_column)
|
|
212
213
|
end
|
|
213
214
|
|
|
214
215
|
def skip_status_checks?
|
|
@@ -225,7 +226,7 @@ module Rodauth
|
|
|
225
226
|
end
|
|
226
227
|
|
|
227
228
|
def set_verify_account_email_last_sent
|
|
228
|
-
|
|
229
|
+
verify_account_ds.update(verify_account_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if verify_account_email_last_sent_column
|
|
229
230
|
end
|
|
230
231
|
|
|
231
232
|
def get_verify_account_email_last_sent
|
|
@@ -248,7 +249,16 @@ module Rodauth
|
|
|
248
249
|
|
|
249
250
|
def clear_tokens(reason)
|
|
250
251
|
super
|
|
251
|
-
|
|
252
|
+
if reason == :close_account
|
|
253
|
+
remove_verify_account_key
|
|
254
|
+
else
|
|
255
|
+
generate_verify_account_key_value
|
|
256
|
+
hash = {verify_account_key_column=>verify_account_key_value}
|
|
257
|
+
if verify_account_email_last_sent_column
|
|
258
|
+
hash[verify_account_email_last_sent_column] = Time.now - verify_account_skip_resend_email_within - 10
|
|
259
|
+
end
|
|
260
|
+
verify_account_ds.update(hash)
|
|
261
|
+
end
|
|
252
262
|
end
|
|
253
263
|
|
|
254
264
|
private
|
|
@@ -309,5 +319,13 @@ module Rodauth
|
|
|
309
319
|
def _account_from_verify_account_key(token)
|
|
310
320
|
account_from_key(token, account_unverified_status_value){|id| get_verify_account_key(id)}
|
|
311
321
|
end
|
|
322
|
+
|
|
323
|
+
def handle_invalid_verify_account_key
|
|
324
|
+
remove_session_value(verify_account_session_key)
|
|
325
|
+
set_redirect_error_status(invalid_key_error_status)
|
|
326
|
+
set_error_reason :invalid_verify_account_key
|
|
327
|
+
set_redirect_error_flash verify_account_error_flash
|
|
328
|
+
redirect verify_account_redirect
|
|
329
|
+
end
|
|
312
330
|
end
|
|
313
331
|
end
|