rodauth 1.22.0 → 2.3.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/CHANGELOG +190 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +210 -80
- data/doc/account_expiration.rdoc +12 -26
- data/doc/active_sessions.rdoc +49 -0
- data/doc/audit_logging.rdoc +44 -0
- data/doc/base.rdoc +75 -128
- data/doc/change_login.rdoc +7 -14
- data/doc/change_password.rdoc +9 -13
- data/doc/change_password_notify.rdoc +2 -2
- data/doc/close_account.rdoc +9 -16
- data/doc/confirm_password.rdoc +12 -5
- data/doc/create_account.rdoc +11 -22
- data/doc/disallow_password_reuse.rdoc +6 -13
- data/doc/email_auth.rdoc +15 -14
- data/doc/email_base.rdoc +6 -15
- data/doc/guides/admin_activation.rdoc +46 -0
- data/doc/guides/already_authenticated.rdoc +10 -0
- data/doc/guides/alternative_login.rdoc +46 -0
- data/doc/guides/create_account_programmatically.rdoc +38 -0
- data/doc/guides/delay_password.rdoc +25 -0
- data/doc/guides/email_only.rdoc +16 -0
- data/doc/guides/i18n.rdoc +26 -0
- data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
- data/doc/guides/links.rdoc +12 -0
- data/doc/guides/login_return.rdoc +37 -0
- data/doc/guides/password_column.rdoc +25 -0
- data/doc/guides/password_confirmation.rdoc +37 -0
- data/doc/guides/password_requirements.rdoc +30 -0
- data/doc/guides/paths.rdoc +36 -0
- data/doc/guides/query_params.rdoc +9 -0
- data/doc/guides/redirects.rdoc +17 -0
- data/doc/guides/registration_field.rdoc +68 -0
- data/doc/guides/require_mfa.rdoc +30 -0
- data/doc/guides/reset_password_autologin.rdoc +21 -0
- data/doc/guides/status_column.rdoc +28 -0
- data/doc/guides/totp_or_recovery.rdoc +16 -0
- data/doc/http_basic_auth.rdoc +10 -1
- data/doc/jwt.rdoc +22 -22
- data/doc/jwt_cors.rdoc +2 -3
- data/doc/jwt_refresh.rdoc +23 -8
- data/doc/lockout.rdoc +17 -15
- data/doc/login.rdoc +17 -2
- data/doc/login_password_requirements_base.rdoc +18 -37
- data/doc/logout.rdoc +2 -2
- data/doc/otp.rdoc +25 -19
- data/doc/password_complexity.rdoc +10 -26
- data/doc/password_expiration.rdoc +11 -25
- data/doc/password_grace_period.rdoc +16 -2
- data/doc/recovery_codes.rdoc +18 -12
- data/doc/release_notes/1.23.0.txt +32 -0
- data/doc/release_notes/2.0.0.txt +361 -0
- data/doc/release_notes/2.1.0.txt +31 -0
- data/doc/release_notes/2.2.0.txt +39 -0
- data/doc/release_notes/2.3.0.txt +37 -0
- data/doc/remember.rdoc +40 -64
- data/doc/reset_password.rdoc +12 -9
- data/doc/session_expiration.rdoc +1 -0
- data/doc/single_session.rdoc +16 -25
- data/doc/sms_codes.rdoc +24 -14
- data/doc/two_factor_base.rdoc +60 -22
- data/doc/verify_account.rdoc +14 -12
- data/doc/verify_account_grace_period.rdoc +6 -2
- data/doc/verify_login_change.rdoc +9 -8
- data/doc/webauthn.rdoc +115 -0
- data/doc/webauthn_login.rdoc +15 -0
- data/doc/webauthn_verify_account.rdoc +9 -0
- data/javascript/webauthn_auth.js +45 -0
- data/javascript/webauthn_setup.js +35 -0
- data/lib/roda/plugins/rodauth.rb +1 -1
- data/lib/rodauth.rb +36 -28
- data/lib/rodauth/features/account_expiration.rb +5 -5
- data/lib/rodauth/features/active_sessions.rb +158 -0
- data/lib/rodauth/features/audit_logging.rb +98 -0
- data/lib/rodauth/features/base.rb +144 -43
- data/lib/rodauth/features/change_password_notify.rb +2 -2
- data/lib/rodauth/features/close_account.rb +8 -6
- data/lib/rodauth/features/confirm_password.rb +40 -2
- data/lib/rodauth/features/create_account.rb +8 -13
- data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
- data/lib/rodauth/features/disallow_password_reuse.rb +1 -1
- data/lib/rodauth/features/email_auth.rb +31 -30
- data/lib/rodauth/features/email_base.rb +9 -4
- data/lib/rodauth/features/http_basic_auth.rb +55 -35
- data/lib/rodauth/features/jwt.rb +63 -16
- data/lib/rodauth/features/jwt_cors.rb +15 -15
- data/lib/rodauth/features/jwt_refresh.rb +42 -13
- data/lib/rodauth/features/lockout.rb +12 -14
- data/lib/rodauth/features/login.rb +64 -15
- data/lib/rodauth/features/login_password_requirements_base.rb +13 -8
- data/lib/rodauth/features/otp.rb +77 -80
- data/lib/rodauth/features/password_complexity.rb +8 -13
- data/lib/rodauth/features/password_expiration.rb +2 -2
- data/lib/rodauth/features/password_grace_period.rb +17 -10
- data/lib/rodauth/features/recovery_codes.rb +49 -53
- data/lib/rodauth/features/remember.rb +11 -27
- data/lib/rodauth/features/reset_password.rb +26 -26
- data/lib/rodauth/features/session_expiration.rb +7 -10
- data/lib/rodauth/features/single_session.rb +8 -6
- data/lib/rodauth/features/sms_codes.rb +62 -72
- data/lib/rodauth/features/two_factor_base.rb +134 -30
- data/lib/rodauth/features/verify_account.rb +29 -21
- data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
- data/lib/rodauth/features/verify_login_change.rb +12 -11
- data/lib/rodauth/features/webauthn.rb +505 -0
- data/lib/rodauth/features/webauthn_login.rb +70 -0
- data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
- data/lib/rodauth/migrations.rb +16 -5
- data/lib/rodauth/version.rb +2 -2
- data/templates/button.str +1 -3
- data/templates/change-login.str +1 -2
- data/templates/change-password.str +3 -5
- data/templates/close-account.str +2 -2
- data/templates/confirm-password.str +1 -1
- data/templates/create-account.str +1 -1
- data/templates/email-auth-request-form.str +2 -3
- data/templates/email-auth.str +1 -1
- data/templates/global-logout-field.str +6 -0
- data/templates/login-confirm-field.str +2 -4
- data/templates/login-display.str +3 -2
- data/templates/login-field.str +2 -4
- data/templates/login-form-footer.str +6 -0
- data/templates/login-form.str +7 -0
- data/templates/login.str +1 -9
- data/templates/logout.str +1 -1
- data/templates/multi-phase-login.str +3 -0
- data/templates/otp-auth-code-field.str +5 -3
- data/templates/otp-auth.str +1 -1
- data/templates/otp-disable.str +1 -1
- data/templates/otp-setup.str +3 -3
- data/templates/password-confirm-field.str +2 -4
- data/templates/password-field.str +2 -4
- data/templates/recovery-auth.str +3 -6
- data/templates/recovery-codes.str +1 -1
- data/templates/remember.str +15 -20
- data/templates/reset-password-request.str +3 -3
- data/templates/reset-password.str +1 -2
- data/templates/sms-auth.str +1 -1
- data/templates/sms-code-field.str +5 -3
- data/templates/sms-confirm.str +1 -2
- data/templates/sms-disable.str +1 -2
- data/templates/sms-request.str +1 -1
- data/templates/sms-setup.str +6 -4
- data/templates/two-factor-auth.str +5 -0
- data/templates/two-factor-disable.str +6 -0
- data/templates/two-factor-manage.str +16 -0
- data/templates/unlock-account-request.str +4 -4
- data/templates/unlock-account.str +1 -1
- data/templates/verify-account-resend.str +3 -3
- data/templates/verify-account.str +1 -2
- data/templates/verify-login-change.str +1 -1
- data/templates/webauthn-auth.str +11 -0
- data/templates/webauthn-remove.str +14 -0
- data/templates/webauthn-setup.str +12 -0
- metadata +94 -54
- data/Rakefile +0 -179
- data/doc/verify_change_login.rdoc +0 -11
- data/lib/rodauth/features/verify_change_login.rb +0 -20
- data/spec/account_expiration_spec.rb +0 -225
- data/spec/all.rb +0 -1
- data/spec/change_login_spec.rb +0 -156
- data/spec/change_password_notify_spec.rb +0 -33
- data/spec/change_password_spec.rb +0 -202
- data/spec/close_account_spec.rb +0 -162
- data/spec/confirm_password_spec.rb +0 -70
- data/spec/create_account_spec.rb +0 -127
- data/spec/disallow_common_passwords_spec.rb +0 -93
- data/spec/disallow_password_reuse_spec.rb +0 -179
- data/spec/email_auth_spec.rb +0 -285
- data/spec/http_basic_auth_spec.rb +0 -143
- data/spec/jwt_cors_spec.rb +0 -57
- data/spec/jwt_refresh_spec.rb +0 -256
- data/spec/jwt_spec.rb +0 -235
- data/spec/lockout_spec.rb +0 -250
- data/spec/login_spec.rb +0 -328
- data/spec/migrate/001_tables.rb +0 -184
- data/spec/migrate/002_account_password_hash_column.rb +0 -11
- data/spec/migrate_password/001_tables.rb +0 -73
- data/spec/migrate_travis/001_tables.rb +0 -141
- data/spec/password_complexity_spec.rb +0 -109
- data/spec/password_expiration_spec.rb +0 -244
- data/spec/password_grace_period_spec.rb +0 -93
- data/spec/remember_spec.rb +0 -451
- data/spec/reset_password_spec.rb +0 -229
- data/spec/rodauth_spec.rb +0 -343
- data/spec/session_expiration_spec.rb +0 -58
- data/spec/single_session_spec.rb +0 -127
- data/spec/spec_helper.rb +0 -327
- data/spec/two_factor_spec.rb +0 -1462
- data/spec/update_password_hash_spec.rb +0 -40
- data/spec/verify_account_grace_period_spec.rb +0 -171
- data/spec/verify_account_spec.rb +0 -240
- data/spec/verify_change_login_spec.rb +0 -46
- data/spec/verify_login_change_spec.rb +0 -232
- data/spec/views/layout-other.str +0 -11
- data/spec/views/layout.str +0 -11
- data/spec/views/login.str +0 -21
|
@@ -28,28 +28,32 @@ module Rodauth
|
|
|
28
28
|
button 'Send SMS Code', 'sms_request'
|
|
29
29
|
button 'Setup SMS Backup Number', 'sms_setup'
|
|
30
30
|
|
|
31
|
-
error_flash "Error authenticating via SMS code
|
|
31
|
+
error_flash "Error authenticating via SMS code", 'sms_invalid_code'
|
|
32
32
|
error_flash "Error disabling SMS authentication", 'sms_disable'
|
|
33
33
|
error_flash "Error setting up SMS authentication", 'sms_setup'
|
|
34
|
-
error_flash "Invalid or out of date SMS confirmation code used, must setup SMS authentication again
|
|
34
|
+
error_flash "Invalid or out of date SMS confirmation code used, must setup SMS authentication again", 'sms_invalid_confirmation_code'
|
|
35
35
|
error_flash "No current SMS code for this account", 'no_current_sms_code'
|
|
36
|
-
error_flash "SMS authentication has been locked out
|
|
37
|
-
error_flash "SMS authentication has already been setup
|
|
38
|
-
error_flash "SMS authentication has not been setup yet
|
|
39
|
-
error_flash "SMS authentication needs confirmation
|
|
36
|
+
error_flash "SMS authentication has been locked out", 'sms_lockout'
|
|
37
|
+
error_flash "SMS authentication has already been setup", 'sms_already_setup'
|
|
38
|
+
error_flash "SMS authentication has not been setup yet", 'sms_not_setup'
|
|
39
|
+
error_flash "SMS authentication needs confirmation", 'sms_needs_confirmation'
|
|
40
40
|
|
|
41
|
-
notice_flash "SMS authentication code has been sent
|
|
42
|
-
notice_flash "SMS authentication has been disabled
|
|
43
|
-
notice_flash "SMS authentication has been setup
|
|
41
|
+
notice_flash "SMS authentication code has been sent", 'sms_request'
|
|
42
|
+
notice_flash "SMS authentication has been disabled", 'sms_disable'
|
|
43
|
+
notice_flash "SMS authentication has been setup", 'sms_confirm'
|
|
44
|
+
|
|
45
|
+
translatable_method :sms_auth_link_text, "Authenticate Using SMS Code"
|
|
46
|
+
translatable_method :sms_setup_link_text, "Setup Backup SMS Authentication"
|
|
47
|
+
translatable_method :sms_disable_link_text, "Disable SMS Authentication"
|
|
44
48
|
|
|
45
49
|
redirect :sms_already_setup
|
|
46
50
|
redirect :sms_confirm
|
|
47
51
|
redirect :sms_disable
|
|
48
|
-
redirect(:sms_auth){
|
|
49
|
-
redirect(:sms_needs_confirmation){
|
|
50
|
-
redirect(:sms_needs_setup){
|
|
51
|
-
redirect(:sms_request){
|
|
52
|
-
redirect(:sms_lockout){
|
|
52
|
+
redirect(:sms_auth){sms_auth_path}
|
|
53
|
+
redirect(:sms_needs_confirmation){sms_confirm_path}
|
|
54
|
+
redirect(:sms_needs_setup){sms_setup_path}
|
|
55
|
+
redirect(:sms_request){sms_request_path}
|
|
56
|
+
redirect(:sms_lockout){two_factor_auth_required_redirect}
|
|
53
57
|
|
|
54
58
|
loaded_templates %w'sms-auth sms-confirm sms-disable sms-request sms-setup sms-code-field password-field'
|
|
55
59
|
view 'sms-auth', 'Authenticate via SMS Code', 'sms_auth'
|
|
@@ -64,18 +68,19 @@ module Rodauth
|
|
|
64
68
|
auth_value_method :sms_auth_code_length, 6
|
|
65
69
|
auth_value_method :sms_code_allowed_seconds, 300
|
|
66
70
|
auth_value_method :sms_code_column, :code
|
|
67
|
-
|
|
71
|
+
translatable_method :sms_code_label, 'SMS Code'
|
|
68
72
|
auth_value_method :sms_code_param, 'sms-code'
|
|
69
73
|
auth_value_method :sms_codes_table, :account_sms_codes
|
|
70
74
|
auth_value_method :sms_confirm_code_length, 12
|
|
71
75
|
auth_value_method :sms_failure_limit, 5
|
|
72
76
|
auth_value_method :sms_failures_column, :num_failures
|
|
73
77
|
auth_value_method :sms_id_column, :id
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
translatable_method :sms_invalid_code_message, "invalid SMS code"
|
|
79
|
+
translatable_method :sms_invalid_phone_message, "invalid SMS phone number"
|
|
76
80
|
auth_value_method :sms_issued_at_column, :code_issued_at
|
|
77
81
|
auth_value_method :sms_phone_column, :phone_number
|
|
78
|
-
|
|
82
|
+
translatable_method :sms_phone_label, 'Phone Number'
|
|
83
|
+
auth_value_method :sms_phone_input_type, 'tel'
|
|
79
84
|
auth_value_method :sms_phone_min_length, 7
|
|
80
85
|
auth_value_method :sms_phone_param, 'sms-phone'
|
|
81
86
|
|
|
@@ -110,7 +115,7 @@ module Rodauth
|
|
|
110
115
|
route(:sms_request) do |r|
|
|
111
116
|
require_login
|
|
112
117
|
require_account_session
|
|
113
|
-
require_two_factor_not_authenticated
|
|
118
|
+
require_two_factor_not_authenticated('sms_code')
|
|
114
119
|
require_sms_available
|
|
115
120
|
before_sms_request_route
|
|
116
121
|
|
|
@@ -133,7 +138,7 @@ module Rodauth
|
|
|
133
138
|
route(:sms_auth) do |r|
|
|
134
139
|
require_login
|
|
135
140
|
require_account_session
|
|
136
|
-
require_two_factor_not_authenticated
|
|
141
|
+
require_two_factor_not_authenticated('sms_code')
|
|
137
142
|
require_sms_available
|
|
138
143
|
|
|
139
144
|
unless sms_current_auth?
|
|
@@ -157,7 +162,7 @@ module Rodauth
|
|
|
157
162
|
if sms_code_match?(param(sms_code_param))
|
|
158
163
|
before_sms_auth
|
|
159
164
|
sms_remove_failures
|
|
160
|
-
two_factor_authenticate(
|
|
165
|
+
two_factor_authenticate('sms_code')
|
|
161
166
|
else
|
|
162
167
|
sms_record_failure
|
|
163
168
|
after_sms_failure
|
|
@@ -238,8 +243,8 @@ module Rodauth
|
|
|
238
243
|
before_sms_confirm
|
|
239
244
|
sms_confirm
|
|
240
245
|
after_sms_confirm
|
|
241
|
-
|
|
242
|
-
|
|
246
|
+
unless two_factor_authenticated?
|
|
247
|
+
two_factor_update_session('sms_code')
|
|
243
248
|
end
|
|
244
249
|
end
|
|
245
250
|
|
|
@@ -268,8 +273,8 @@ module Rodauth
|
|
|
268
273
|
transaction do
|
|
269
274
|
before_sms_disable
|
|
270
275
|
sms_disable
|
|
271
|
-
if
|
|
272
|
-
two_factor_remove_session
|
|
276
|
+
if two_factor_login_type_match?('sms_code')
|
|
277
|
+
two_factor_remove_session('sms_code')
|
|
273
278
|
end
|
|
274
279
|
after_sms_disable
|
|
275
280
|
end
|
|
@@ -284,18 +289,6 @@ module Rodauth
|
|
|
284
289
|
end
|
|
285
290
|
end
|
|
286
291
|
|
|
287
|
-
def two_factor_need_setup_redirect
|
|
288
|
-
super || (sms_needs_setup_redirect if sms_codes_primary?)
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def two_factor_auth_required_redirect
|
|
292
|
-
super || (sms_request_redirect if sms_codes_primary? && sms_available?)
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
def two_factor_auth_fallback_redirect
|
|
296
|
-
sms_available? ? sms_request_redirect : super
|
|
297
|
-
end
|
|
298
|
-
|
|
299
292
|
def two_factor_remove
|
|
300
293
|
super
|
|
301
294
|
sms_disable
|
|
@@ -306,37 +299,6 @@ module Rodauth
|
|
|
306
299
|
sms_remove_failures
|
|
307
300
|
end
|
|
308
301
|
|
|
309
|
-
def two_factor_authentication_setup?
|
|
310
|
-
super || (sms_codes_primary? && sms_setup?)
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def otp_auth_form_footer
|
|
314
|
-
"#{super if defined?(super)}#{"<p><a href=\"#{sms_request_route}\">Authenticate using SMS code</a></p>" if sms_available?}"
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def otp_lockout_redirect
|
|
318
|
-
if sms_available?
|
|
319
|
-
sms_request_redirect
|
|
320
|
-
else
|
|
321
|
-
super if defined?(super)
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
def otp_lockout_error_flash
|
|
326
|
-
msg = super if defined?(super)
|
|
327
|
-
if sms_available?
|
|
328
|
-
msg = "#{msg} Can use SMS code to unlock."
|
|
329
|
-
end
|
|
330
|
-
msg
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
def otp_remove
|
|
334
|
-
super if defined?(super)
|
|
335
|
-
unless sms_codes_primary?
|
|
336
|
-
sms_disable
|
|
337
|
-
end
|
|
338
|
-
end
|
|
339
|
-
|
|
340
302
|
def require_sms_setup
|
|
341
303
|
unless sms_setup?
|
|
342
304
|
set_redirect_error_status(two_factor_not_setup_error_status)
|
|
@@ -375,7 +337,6 @@ module Rodauth
|
|
|
375
337
|
def sms_disable
|
|
376
338
|
sms_ds.delete
|
|
377
339
|
@sms = nil
|
|
378
|
-
super if defined?(super)
|
|
379
340
|
end
|
|
380
341
|
|
|
381
342
|
def sms_confirm_failure
|
|
@@ -415,11 +376,11 @@ module Rodauth
|
|
|
415
376
|
end
|
|
416
377
|
|
|
417
378
|
def sms_auth_message(code)
|
|
418
|
-
"SMS authentication code for #{
|
|
379
|
+
"SMS authentication code for #{domain} is #{code}"
|
|
419
380
|
end
|
|
420
381
|
|
|
421
382
|
def sms_confirm_message(code)
|
|
422
|
-
"SMS confirmation code for #{
|
|
383
|
+
"SMS confirmation code for #{domain} is #{code}"
|
|
423
384
|
end
|
|
424
385
|
|
|
425
386
|
def sms_set_code(code)
|
|
@@ -468,10 +429,39 @@ module Rodauth
|
|
|
468
429
|
sms_code && sms_code_issued_at + sms_code_allowed_seconds > Time.now
|
|
469
430
|
end
|
|
470
431
|
|
|
432
|
+
def possible_authentication_methods
|
|
433
|
+
methods = super
|
|
434
|
+
methods << 'sms_code' if sms_setup?
|
|
435
|
+
methods
|
|
436
|
+
end
|
|
437
|
+
|
|
471
438
|
private
|
|
472
439
|
|
|
440
|
+
def _two_factor_auth_links
|
|
441
|
+
links = super
|
|
442
|
+
links << [30, sms_request_path, sms_auth_link_text] if sms_available?
|
|
443
|
+
links
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def _two_factor_setup_links
|
|
447
|
+
links = super
|
|
448
|
+
links << [30, sms_setup_path, sms_setup_link_text] if !sms_setup? && (sms_codes_primary? || uses_two_factor_authentication?)
|
|
449
|
+
links
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def _two_factor_remove_links
|
|
453
|
+
links = super
|
|
454
|
+
links << [30, sms_disable_path, sms_disable_link_text] if sms_setup?
|
|
455
|
+
links
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def _two_factor_remove_all_from_session
|
|
459
|
+
two_factor_remove_session('sms_codes')
|
|
460
|
+
super
|
|
461
|
+
end
|
|
462
|
+
|
|
473
463
|
def sms_codes_primary?
|
|
474
|
-
|
|
464
|
+
(features & [:otp, :webauthn]).empty?
|
|
475
465
|
end
|
|
476
466
|
|
|
477
467
|
def sms_normalize_phone(phone)
|
|
@@ -2,25 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:two_factor_base, :TwoFactorBase) do
|
|
5
|
+
loaded_templates %w'two-factor-manage two-factor-auth two-factor-disable'
|
|
6
|
+
|
|
7
|
+
view 'two-factor-manage', 'Manage Multifactor Authentication', 'two_factor_manage'
|
|
8
|
+
view 'two-factor-auth', 'Authenticate Using Additional Factor', 'two_factor_auth'
|
|
9
|
+
view 'two-factor-disable', 'Remove All Multifactor Authentication Methods', 'two_factor_disable'
|
|
10
|
+
|
|
11
|
+
before :two_factor_disable
|
|
12
|
+
|
|
5
13
|
after :two_factor_authentication
|
|
14
|
+
after :two_factor_disable
|
|
15
|
+
|
|
16
|
+
additional_form_tags :two_factor_disable
|
|
17
|
+
|
|
18
|
+
button "Remove All Multifactor Authentication Methods", :two_factor_disable
|
|
6
19
|
|
|
7
|
-
redirect
|
|
8
|
-
redirect
|
|
20
|
+
redirect(:two_factor_auth)
|
|
21
|
+
redirect(:two_factor_already_authenticated)
|
|
22
|
+
redirect(:two_factor_disable)
|
|
23
|
+
redirect(:two_factor_need_setup){two_factor_manage_path}
|
|
24
|
+
redirect(:two_factor_auth_required){two_factor_auth_path}
|
|
9
25
|
|
|
10
|
-
notice_flash "You have been authenticated
|
|
26
|
+
notice_flash "You have been multifactor authenticated", "two_factor_auth"
|
|
27
|
+
notice_flash "All multifactor authentication methods have been disabled", "two_factor_disable"
|
|
11
28
|
|
|
12
|
-
error_flash "This account has not been setup for
|
|
13
|
-
error_flash "
|
|
14
|
-
error_flash "You need to authenticate via
|
|
29
|
+
error_flash "This account has not been setup for multifactor authentication", 'two_factor_not_setup'
|
|
30
|
+
error_flash "You have already been multifactor authenticated", 'two_factor_already_authenticated'
|
|
31
|
+
error_flash "You need to authenticate via an additional factor before continuing", 'two_factor_need_authentication'
|
|
32
|
+
error_flash "Unable to remove all multifactor authentication methods", "two_factor_disable"
|
|
15
33
|
|
|
16
34
|
auth_value_method :two_factor_already_authenticated_error_status, 403
|
|
17
35
|
auth_value_method :two_factor_need_authentication_error_status, 401
|
|
18
36
|
auth_value_method :two_factor_not_setup_error_status, 403
|
|
19
37
|
|
|
20
|
-
session_key :two_factor_session_key, :two_factor_auth
|
|
21
38
|
session_key :two_factor_setup_session_key, :two_factor_auth_setup
|
|
22
|
-
|
|
23
|
-
|
|
39
|
+
session_key :two_factor_auth_redirect_session_key, :two_factor_auth_redirect
|
|
40
|
+
|
|
41
|
+
translatable_method :two_factor_setup_heading, "<h2>Setup Multifactor Authentication</h2>"
|
|
42
|
+
translatable_method :two_factor_remove_heading, "<h2>Remove Multifactor Authentication</h2>"
|
|
43
|
+
translatable_method :two_factor_disable_link_text, "Remove All Multifactor Authentication Methods"
|
|
44
|
+
auth_value_method :two_factor_auth_return_to_requested_location?, false
|
|
45
|
+
|
|
46
|
+
auth_cached_method :two_factor_auth_links
|
|
47
|
+
auth_cached_method :two_factor_setup_links
|
|
48
|
+
auth_cached_method :two_factor_remove_links
|
|
24
49
|
|
|
25
50
|
auth_value_methods :two_factor_modifications_require_password?
|
|
26
51
|
|
|
@@ -32,6 +57,62 @@ module Rodauth
|
|
|
32
57
|
:two_factor_update_session
|
|
33
58
|
)
|
|
34
59
|
|
|
60
|
+
route(:two_factor_manage, 'multifactor-manage') do |r|
|
|
61
|
+
require_account
|
|
62
|
+
before_two_factor_manage_route
|
|
63
|
+
|
|
64
|
+
r.get do
|
|
65
|
+
all_links = two_factor_setup_links + two_factor_remove_links
|
|
66
|
+
if all_links.length == 1
|
|
67
|
+
redirect all_links[0][1]
|
|
68
|
+
end
|
|
69
|
+
two_factor_manage_view
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
route(:two_factor_auth, 'multifactor-auth') do |r|
|
|
74
|
+
require_login
|
|
75
|
+
require_account_session
|
|
76
|
+
require_two_factor_setup
|
|
77
|
+
require_two_factor_not_authenticated
|
|
78
|
+
before_two_factor_auth_route
|
|
79
|
+
|
|
80
|
+
r.get do
|
|
81
|
+
if two_factor_auth_links.length == 1
|
|
82
|
+
redirect two_factor_auth_links[0][1]
|
|
83
|
+
end
|
|
84
|
+
two_factor_auth_view
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
route(:two_factor_disable, 'multifactor-disable') do |r|
|
|
89
|
+
require_account
|
|
90
|
+
require_two_factor_setup
|
|
91
|
+
before_two_factor_disable_route
|
|
92
|
+
|
|
93
|
+
r.get do
|
|
94
|
+
two_factor_disable_view
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
r.post do
|
|
98
|
+
if two_factor_password_match?(param(password_param))
|
|
99
|
+
transaction do
|
|
100
|
+
before_two_factor_disable
|
|
101
|
+
two_factor_remove
|
|
102
|
+
_two_factor_remove_all_from_session
|
|
103
|
+
after_two_factor_disable
|
|
104
|
+
end
|
|
105
|
+
set_notice_flash two_factor_disable_notice_flash
|
|
106
|
+
redirect two_factor_disable_redirect
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
set_response_error_status(invalid_password_error_status)
|
|
110
|
+
set_field_error(password_param, invalid_password_message)
|
|
111
|
+
set_error_flash two_factor_disable_error_flash
|
|
112
|
+
two_factor_disable_view
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
35
116
|
def two_factor_modifications_require_password?
|
|
36
117
|
modifications_require_password?
|
|
37
118
|
end
|
|
@@ -44,7 +125,7 @@ module Rodauth
|
|
|
44
125
|
return true if two_factor_authenticated?
|
|
45
126
|
|
|
46
127
|
# True if authenticated via single factor and 2nd factor not setup
|
|
47
|
-
!
|
|
128
|
+
!uses_two_factor_authentication?
|
|
48
129
|
end
|
|
49
130
|
|
|
50
131
|
def require_authentication
|
|
@@ -53,7 +134,7 @@ module Rodauth
|
|
|
53
134
|
# Avoid database query if already authenticated via 2nd factor
|
|
54
135
|
return if two_factor_authenticated?
|
|
55
136
|
|
|
56
|
-
require_two_factor_authenticated if
|
|
137
|
+
require_two_factor_authenticated if uses_two_factor_authentication?
|
|
57
138
|
end
|
|
58
139
|
|
|
59
140
|
def require_two_factor_setup
|
|
@@ -67,8 +148,8 @@ module Rodauth
|
|
|
67
148
|
redirect two_factor_need_setup_redirect
|
|
68
149
|
end
|
|
69
150
|
|
|
70
|
-
def require_two_factor_not_authenticated
|
|
71
|
-
if two_factor_authenticated?
|
|
151
|
+
def require_two_factor_not_authenticated(auth_type = nil)
|
|
152
|
+
if two_factor_authenticated? || (auth_type && two_factor_login_type_match?(auth_type))
|
|
72
153
|
set_redirect_error_status(two_factor_already_authenticated_error_status)
|
|
73
154
|
set_redirect_error_flash two_factor_already_authenticated_error_flash
|
|
74
155
|
redirect two_factor_already_authenticated_redirect
|
|
@@ -77,9 +158,12 @@ module Rodauth
|
|
|
77
158
|
|
|
78
159
|
def require_two_factor_authenticated
|
|
79
160
|
unless two_factor_authenticated?
|
|
161
|
+
if two_factor_auth_return_to_requested_location?
|
|
162
|
+
set_session_value(two_factor_auth_redirect_session_key, request.fullpath)
|
|
163
|
+
end
|
|
80
164
|
set_redirect_error_status(two_factor_need_authentication_error_status)
|
|
81
165
|
set_redirect_error_flash two_factor_need_authentication_error_flash
|
|
82
|
-
redirect
|
|
166
|
+
redirect two_factor_auth_required_redirect
|
|
83
167
|
end
|
|
84
168
|
end
|
|
85
169
|
|
|
@@ -87,10 +171,6 @@ module Rodauth
|
|
|
87
171
|
nil
|
|
88
172
|
end
|
|
89
173
|
|
|
90
|
-
def two_factor_auth_fallback_redirect
|
|
91
|
-
nil
|
|
92
|
-
end
|
|
93
|
-
|
|
94
174
|
def two_factor_password_match?(password)
|
|
95
175
|
if two_factor_modifications_require_password?
|
|
96
176
|
password_match?(password)
|
|
@@ -100,25 +180,45 @@ module Rodauth
|
|
|
100
180
|
end
|
|
101
181
|
|
|
102
182
|
def two_factor_authenticated?
|
|
103
|
-
|
|
183
|
+
authenticated_by && authenticated_by.length >= 2
|
|
104
184
|
end
|
|
105
185
|
|
|
106
186
|
def two_factor_authentication_setup?
|
|
107
|
-
|
|
187
|
+
possible_authentication_methods.length >= 2
|
|
108
188
|
end
|
|
109
189
|
|
|
110
190
|
def uses_two_factor_authentication?
|
|
111
191
|
return false unless logged_in?
|
|
112
|
-
|
|
192
|
+
set_session_value(two_factor_setup_session_key, two_factor_authentication_setup?) unless session.has_key?(two_factor_setup_session_key)
|
|
113
193
|
session[two_factor_setup_session_key]
|
|
114
194
|
end
|
|
115
195
|
|
|
196
|
+
def two_factor_login_type_match?(type)
|
|
197
|
+
authenticated_by && authenticated_by.include?(type)
|
|
198
|
+
end
|
|
199
|
+
|
|
116
200
|
def two_factor_remove
|
|
117
201
|
nil
|
|
118
202
|
end
|
|
119
203
|
|
|
120
204
|
private
|
|
121
205
|
|
|
206
|
+
def _two_factor_auth_links
|
|
207
|
+
(super if defined?(super)) || []
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def _two_factor_setup_links
|
|
211
|
+
[]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def _two_factor_remove_links
|
|
215
|
+
[]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def _two_factor_remove_all_from_session
|
|
219
|
+
nil
|
|
220
|
+
end
|
|
221
|
+
|
|
122
222
|
def after_close_account
|
|
123
223
|
super if defined?(super)
|
|
124
224
|
two_factor_remove
|
|
@@ -129,21 +229,25 @@ module Rodauth
|
|
|
129
229
|
two_factor_remove_auth_failures
|
|
130
230
|
after_two_factor_authentication
|
|
131
231
|
set_notice_flash two_factor_auth_notice_flash
|
|
132
|
-
|
|
232
|
+
redirect_two_factor_authenticated
|
|
133
233
|
end
|
|
134
234
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
235
|
+
def redirect_two_factor_authenticated
|
|
236
|
+
saved_two_factor_auth_redirect = remove_session_value(two_factor_auth_redirect_session_key)
|
|
237
|
+
redirect saved_two_factor_auth_redirect || two_factor_auth_redirect
|
|
138
238
|
end
|
|
139
239
|
|
|
140
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
240
|
+
def two_factor_remove_session(type)
|
|
241
|
+
authenticated_by.delete(type)
|
|
242
|
+
remove_session_value(two_factor_setup_session_key)
|
|
243
|
+
if authenticated_by.empty?
|
|
244
|
+
clear_session
|
|
245
|
+
end
|
|
143
246
|
end
|
|
144
247
|
|
|
145
|
-
def
|
|
146
|
-
|
|
248
|
+
def two_factor_update_session(auth_type)
|
|
249
|
+
authenticated_by << auth_type
|
|
250
|
+
set_session_value(two_factor_setup_session_key, true)
|
|
147
251
|
end
|
|
148
252
|
end
|
|
149
253
|
end
|