rodauth 2.35.0 → 2.36.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +17 -1
- data/README.rdoc +18 -1
- data/doc/base.rdoc +2 -0
- data/doc/otp_lockout_email.rdoc +30 -0
- data/doc/otp_modify_email.rdoc +19 -0
- data/doc/otp_unlock.rdoc +58 -0
- data/doc/release_notes/2.36.0.txt +35 -0
- data/doc/webauthn_modify_email.rdoc +19 -0
- data/lib/rodauth/features/base.rb +12 -0
- data/lib/rodauth/features/email_base.rb +1 -3
- data/lib/rodauth/features/internal_request.rb +3 -5
- data/lib/rodauth/features/json.rb +26 -0
- data/lib/rodauth/features/otp.rb +10 -2
- data/lib/rodauth/features/otp_lockout_email.rb +42 -0
- data/lib/rodauth/features/otp_modify_email.rb +23 -0
- data/lib/rodauth/features/otp_unlock.rb +250 -0
- data/lib/rodauth/features/webauthn_autofill.rb +1 -1
- data/lib/rodauth/features/webauthn_modify_email.rb +23 -0
- data/lib/rodauth/version.rb +1 -1
- data/templates/otp-disabled-email.str +2 -0
- data/templates/otp-locked-out-email.str +9 -0
- data/templates/otp-setup-email.str +2 -0
- data/templates/otp-unlock-failed-email.str +8 -0
- data/templates/otp-unlock-not-available.str +5 -0
- data/templates/otp-unlock.str +11 -0
- data/templates/otp-unlocked-email.str +2 -0
- data/templates/webauthn-authenticator-added-email.str +3 -0
- data/templates/webauthn-authenticator-removed-email.str +3 -0
- data/templates/webauthn-remove.str +1 -1
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4daf43beb9b2af129683b299f1e455e5b38fa925b1cc020ec79ae951f56f292b
|
4
|
+
data.tar.gz: a694fe203f76512691004d1138ad03cab7699d317a0a658ae6ac51732ff22b36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aef00a1d31a310ea0061f54ebcb4044ccdcc7106d2f07074f4585ac5406776784bef9c698befa050b374d4dd37dd2495d3a3e4dcd6854e387fc8442901f3f227
|
7
|
+
data.tar.gz: 85d9837f21b320f2bec1241f07e22ff150b49192017cb1ea20d2863fce93b547d5271cafe30b08baeaa582fd4f40b715ce488d9fb19bd7fe2cc43a2f565b5266
|
data/CHANGELOG
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
-
=== 2.
|
1
|
+
=== 2.36.0 (2024-07-23)
|
2
|
+
|
3
|
+
* Add webauthn_modify_email feature for emailing when a WebAuthn authenticator is added or removed (jeremyevans)
|
4
|
+
|
5
|
+
* Add account_from_id method for retrieving an account using the account id and optional status id (janko) (#431)
|
6
|
+
|
7
|
+
* Add otp_modify_email feature for emailing when TOTP authentication is setup or disabled (jeremyevans)
|
8
|
+
|
9
|
+
* Add otp_lockout_email feature for emailing when TOTP authentication is locked out or unlocked (jeremyevans)
|
10
|
+
|
11
|
+
* Add strftime_format configuration method for configuring display of Time values to users (jeremyevans)
|
12
|
+
|
13
|
+
* Add otp_unlock feature for unlocking TOTP authentication after it has been locked out (jeremyevans)
|
14
|
+
|
15
|
+
* Make internal_request feature work with Roda path_rewriter plugin (jeremyevans) (#425)
|
16
|
+
|
17
|
+
=== 2.35.0 (2024-05-28)
|
2
18
|
|
3
19
|
* Handle internal_request_configuration blocks in superclasses (jeremyevans, bjeanes)
|
4
20
|
|
data/README.rdoc
CHANGED
@@ -38,7 +38,11 @@ HTML and JSON API for all supported features.
|
|
38
38
|
* WebAuthn Login (Passwordless login via WebAuthn)
|
39
39
|
* WebAuthn Verify Account (Passwordless WebAuthn Setup)
|
40
40
|
* WebAuthn Autofill (Autofill WebAuthn credentials on login)
|
41
|
+
* WebAuthn Modify Email (Email when WebAuthn authenticator aded or removed)
|
41
42
|
* OTP (Multifactor authentication via TOTP)
|
43
|
+
* OTP Modify Email (Email when TOTP authentication setup or disabled)
|
44
|
+
* OTP Unlock (Unlock TOTP authentication after lockout)
|
45
|
+
* OTP Lockout Email (Email when TOTP authentication locked out or unlocked)
|
42
46
|
* Recovery Codes (Multifactor authentication via backup codes)
|
43
47
|
* SMS Codes (Multifactor authentication via SMS)
|
44
48
|
* Verify Login Change (Verify new login before changing login)
|
@@ -328,7 +332,7 @@ PostgreSQL 15 changed default database security so that only the database
|
|
328
332
|
owner has writable access to the public schema. Rodauth expects the
|
329
333
|
+ph+ account to have writable access to the public schema when setting
|
330
334
|
things up. Temporarily grant that access (it will be revoked after the
|
331
|
-
|
335
|
+
migration has run)
|
332
336
|
|
333
337
|
psql -U postgres -c "GRANT CREATE ON SCHEMA public TO ${DATABASE_NAME}_password" ${DATABASE_NAME}
|
334
338
|
|
@@ -587,6 +591,13 @@ Note that these migrations require Sequel 4.35.0+.
|
|
587
591
|
Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
|
588
592
|
end
|
589
593
|
|
594
|
+
# Used by the otp_unlock feature
|
595
|
+
create_table(:account_otp_unlocks) do
|
596
|
+
foreign_key :id, :accounts, primary_key: true, type: :Bignum
|
597
|
+
Integer :num_successes, null: false, default: 1
|
598
|
+
Time :next_auth_attempt_after, null: false, default: Sequel::CURRENT_TIMESTAMP
|
599
|
+
end
|
600
|
+
|
590
601
|
# Used by the recovery codes feature
|
591
602
|
create_table(:account_recovery_codes) do
|
592
603
|
foreign_key :id, :accounts, type: :Bignum
|
@@ -631,6 +642,7 @@ Note that these migrations require Sequel 4.35.0+.
|
|
631
642
|
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_webauthn_user_ids TO #{user}"
|
632
643
|
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_webauthn_keys TO #{user}"
|
633
644
|
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_otp_keys TO #{user}"
|
645
|
+
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_otp_unlocks TO #{user}"
|
634
646
|
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_recovery_codes TO #{user}"
|
635
647
|
run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_sms_codes TO #{user}"
|
636
648
|
end
|
@@ -639,6 +651,7 @@ Note that these migrations require Sequel 4.35.0+.
|
|
639
651
|
down do
|
640
652
|
drop_table(:account_sms_codes,
|
641
653
|
:account_recovery_codes,
|
654
|
+
:account_otp_unlocks,
|
642
655
|
:account_otp_keys,
|
643
656
|
:account_webauthn_keys,
|
644
657
|
:account_webauthn_user_ids,
|
@@ -920,6 +933,9 @@ view the appropriate file in the doc directory.
|
|
920
933
|
* {Login}[rdoc-ref:doc/login.rdoc]
|
921
934
|
* {Logout}[rdoc-ref:doc/logout.rdoc]
|
922
935
|
* {OTP}[rdoc-ref:doc/otp.rdoc]
|
936
|
+
* {OTP Lockout Email}[rdoc-ref:doc/otp_lockout_email.rdoc]
|
937
|
+
* {OTP Modify Email}[rdoc-ref:doc/otp_modify_email.rdoc]
|
938
|
+
* {OTP Unlock}[rdoc-ref:doc/otp_unlock.rdoc]
|
923
939
|
* {Password Complexity}[rdoc-ref:doc/password_complexity.rdoc]
|
924
940
|
* {Password Expiration}[rdoc-ref:doc/password_expiration.rdoc]
|
925
941
|
* {Password Grace Period}[rdoc-ref:doc/password_grace_period.rdoc]
|
@@ -939,6 +955,7 @@ view the appropriate file in the doc directory.
|
|
939
955
|
* {WebAuthn}[rdoc-ref:doc/webauthn.rdoc]
|
940
956
|
* {WebAuthn Autofill}[rdoc-ref:doc/webauthn_autofill.rdoc]
|
941
957
|
* {WebAuthn Login}[rdoc-ref:doc/webauthn_login.rdoc]
|
958
|
+
* {WebAuthn Modify Email}[rdoc-ref:doc/webauthn_modify_email.rdoc]
|
942
959
|
* {WebAuthn Verify Account}[rdoc-ref:doc/webauthn_verify_account.rdoc]
|
943
960
|
|
944
961
|
=== Calling Rodauth in the Routing Tree
|
data/doc/base.rdoc
CHANGED
@@ -74,6 +74,7 @@ password_param :: The parameter name to use for passwords.
|
|
74
74
|
require_login_error_flash :: The flash error to display when accessing a page that requires a login, when you are not logged in.
|
75
75
|
require_login_redirect :: A redirect to the login page.
|
76
76
|
set_deadline_values? :: Whether deadline values should be set. True by default on MySQL, as that doesn't support default values that are not constant. Can be set to true on other databases if you want to vary the value based on a request parameter.
|
77
|
+
strftime_format :: The format to pass to Time#strftime when formatting timestamps to display to the user, '%F %T' by default.
|
77
78
|
template_opts :: Any template options to pass to view/render. This can be used to set a custom layout, for example.
|
78
79
|
token_separator :: The string used to separate account id from the random key in links.
|
79
80
|
unmatched_field_error_status :: The response status to use when two field values should match but do not, 422 by default.
|
@@ -84,6 +85,7 @@ use_request_specific_csrf_tokens? :: Whether to use request-specific CSRF tokens
|
|
84
85
|
|
85
86
|
== Auth Methods
|
86
87
|
|
88
|
+
account_from_id(id, status_id=nil) :: Retrieve the account hash for the given account id and status.
|
87
89
|
account_from_login(login) :: Retrieve the account hash related to the given login or nil if no login matches.
|
88
90
|
account_from_session :: Retrieve the account hash related to the currently logged in session.
|
89
91
|
account_id :: The primary key value of the current account.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
= Documentation for OTP Lockout Email Feature
|
2
|
+
|
3
|
+
The otp_lockout_email feature emails users when:
|
4
|
+
|
5
|
+
* TOTP authentication is locked out
|
6
|
+
* TOTP authentication is unlocked
|
7
|
+
* A TOTP unlock attempt has failed
|
8
|
+
|
9
|
+
The otp_unlock_email feature depends on the otp_lockout and email_base features.
|
10
|
+
|
11
|
+
== Auth Value Methods
|
12
|
+
|
13
|
+
otp_locked_out_email_body :: Body to use for the email notifying user that TOTP authentication has been locked out.
|
14
|
+
otp_locked_out_email_subject :: Subject to use for the email notifying user that TOTP authentication has been locked out.
|
15
|
+
otp_unlock_failed_email_body :: Body to use for the email notifying user that there has been an unsuccessful attempt to unlock TOTP authentication.
|
16
|
+
otp_unlock_failed_email_subject :: Subject to use for the email notifying user that there has been an unsuccessful attempt to unlock TOTP authentication.
|
17
|
+
otp_unlocked_email_body :: Body to use for the email notifying user that TOTP authentication has been unlocked.
|
18
|
+
otp_unlocked_email_subject :: Subject to use for the email notifying user that TOTP authentication has been unlocked.
|
19
|
+
send_otp_locked_out_email? :: Whether to send an email when TOTP authentication is locked out.
|
20
|
+
send_otp_unlock_failed_email? :: Whether to send an email when there has been an unsuccessful attempt to unlock TOTP authentication.
|
21
|
+
send_otp_unlocked_email? :: Whether to send an email when TOTP authentication is unlocked.
|
22
|
+
|
23
|
+
== Auth Methods
|
24
|
+
|
25
|
+
create_otp_locked_out_email :: A Mail::Message for the email notifying user that TOTP authentication has been locked out.
|
26
|
+
create_otp_unlock_failed_email :: A Mail::Message for the email notifying user that there has been an unsuccessful attempt to unlock TOTP authentication.
|
27
|
+
create_otp_unlocked_email :: A Mail::Message for the email notifying user that TOTP authentication has been unlocked.
|
28
|
+
send_otp_locked_out_email :: Send the email notifying user that TOTP authentication has been locked out.
|
29
|
+
send_otp_unlock_failed_email :: Send the email notifying user that there has been an unsuccessful attempt to unlock TOTP authentication.
|
30
|
+
send_otp_unlocked_email :: Send the email notifying user that TOTP authentication has been unlocked.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
= Documentation for OTP Modify Email Feature
|
2
|
+
|
3
|
+
The otp_modify_email feature emails users when TOTP authentication is setup or disabled.
|
4
|
+
|
5
|
+
The otp_modify_email feature depends on the otp and email_base features.
|
6
|
+
|
7
|
+
== Auth Value Methods
|
8
|
+
|
9
|
+
otp_disabled_email_body :: Body to use for the email notifying user that TOTP authentication has been disabled.
|
10
|
+
otp_disabled_email_subject :: Subject to use for the email notifying user that TOTP authentication has been disabled.
|
11
|
+
otp_setup_email_body :: Body to use for the email notifying user that TOTP authentication has been setup.
|
12
|
+
otp_setup_email_subject :: Subject to use for the email notifying user that TOTP authentication has been setup.
|
13
|
+
|
14
|
+
== Auth Methods
|
15
|
+
|
16
|
+
create_otp_disabled_email :: A Mail::Message for the email notifying user that TOTP authentication has been disabled.
|
17
|
+
create_otp_setup_email :: A Mail::Message for the email notifying user that TOTP authentication has been setup.
|
18
|
+
send_otp_disabled_email :: Send the email notifying user that TOTP authentication has been disabled.
|
19
|
+
send_otp_setup_email :: Send the email notifying user that TOTP authentication has been setup.
|
data/doc/otp_unlock.rdoc
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= Documentation for OTP Unlock Feature
|
2
|
+
|
3
|
+
The otp_unlock feature implements unlocking of TOTP authentication after
|
4
|
+
TOTP authentication. The user must consecutively successfully authenticate
|
5
|
+
with TOTP multiple times (default: 3) within a given time period (15 minutes
|
6
|
+
per attempt) in order to unlock TOTP authentication. By requiring
|
7
|
+
consecutive successful unlocks, with a delay after failure, it is infeasible
|
8
|
+
to brute force the TOTP unlock process.
|
9
|
+
|
10
|
+
The otp_unlock feature depends on the otp feature.
|
11
|
+
|
12
|
+
== Auth Value Methods
|
13
|
+
|
14
|
+
otp_unlock_additional_form_tags :: HTML fragment containing additional form tags to use on the OTP unlock form.
|
15
|
+
otp_unlock_auth_deadline_passed_error_flash :: The flash error to show if attempting to unlock OTP after the deadline for submittal has passed.
|
16
|
+
otp_unlock_auth_deadline_passed_error_status :: The response status to use if attempting to unlock OTP after the deadline for submittal has passed, 403 by default.
|
17
|
+
otp_unlock_auth_failure_cooldown_seconds :: The number of seconds the user must wait to attempt OTP unlock again after a failed OTP unlock attempt.
|
18
|
+
otp_unlock_auth_failure_error_flash :: The flash error to show if attempting to unlock OTP using an incorrect authentication code.
|
19
|
+
otp_unlock_auth_failure_error_status :: The response status to use if attempting to unlock OTP using an incorrect authentication code, 403 by default.
|
20
|
+
otp_unlock_auth_not_yet_available_error_flash :: The flash error to show if attempting to unlock OTP when doing so is not yet available due to a recent attempt.
|
21
|
+
otp_unlock_auth_not_yet_available_error_status :: The response status to use if attempting to unlock OTP when doing so is not yet available due to a recent attempt, 403 by default.
|
22
|
+
otp_unlock_auth_success_notice_flash :: The flash notice to show upon successful unlock authentication, when additional unlock authentication is still needed.
|
23
|
+
otp_unlock_auths_required :: The number of consecutive successful authentication attempts needed to unlock OTP authentication, 3 by default.
|
24
|
+
otp_unlock_button :: Text to use for button on OTP unlock form.
|
25
|
+
otp_unlock_consecutive_successes_label :: Text to show next to the number of consecutive successful authentication attempts the user has already made.
|
26
|
+
otp_unlock_deadline_seconds :: The number of seconds between a previously successful authentication attempt and the next successful authentication attempt. This defaults to twice the amount of time of the OTP interval (30 seconds) plus twice the amount of allowed drift (30 seconds), for a total of 120 seconds. This is to make sure the same OTP code cannot be used more than one when unlocking.
|
27
|
+
otp_unlock_form_footer :: A footer to display at the bottom of the OTP unlock form.
|
28
|
+
otp_unlock_id_column :: The column in the +otp_unlock_table+ containing the account id.
|
29
|
+
otp_unlock_next_auth_attempt_after_column :: The column in the +otp_unlock_table+ containing a timestamp for when the user can next try an authentication attempt.
|
30
|
+
otp_unlock_next_auth_attempt_label :: Text to show next to the time when the next unlock authentication attempt will be allowed.
|
31
|
+
otp_unlock_next_auth_attempt_refresh_label :: Text to show explaining that the page will refresh when the next unlock authentication attempt will be allowed.
|
32
|
+
otp_unlock_next_auth_deadline_label :: Text to show next to the deadline for unlock authentication.
|
33
|
+
otp_unlock_not_available_page_title :: The page title to use on the page letting users know they need to wait to unlock OTP authentication.
|
34
|
+
otp_unlock_not_locked_out_error_flash :: The flash error to show if attempting to access the OTP unlock page when OTP authentication is not locked out.
|
35
|
+
otp_unlock_not_locked_out_error_status :: The response status to use if attempting to access the OTP unlock page when OTP authentication is not locked out, 403 by default.
|
36
|
+
otp_unlock_not_locked_out_redirect :: Where to redirect if attempting to access the OTP unlock page when OTP authentication is not locked out.
|
37
|
+
otp_unlock_num_successes_column :: The column in the +otp_unlock_table+ containing the number of consecutive successful authentications.
|
38
|
+
otp_unlock_page_title :: The page title to use on the OTP unlock form.
|
39
|
+
otp_unlock_refresh_tag :: The meta refresh tag HTML to use to force a refresh of the page. This can be overridden to use a different refresh approach.
|
40
|
+
otp_unlock_required_consecutive_successes_label :: Text to show next to the number of consecutive successful authentication attempts the user is required to make to unlock OTP authentication.
|
41
|
+
otp_unlock_route :: The route to the OTP unlock action. Defaults to +otp-unlock+.
|
42
|
+
otp_unlock_table :: The table name containing the OTP unlock information.
|
43
|
+
otp_unlocked_notice_flash :: The flash notice to show when OTP authentication is successfully fully unlocked.
|
44
|
+
otp_unlocked_redirect :: Where to redirect when OTP authentication is successfully fully unlocked.
|
45
|
+
|
46
|
+
== Auth Methods
|
47
|
+
|
48
|
+
after_otp_unlock_auth_failure :: Run arbitrary code after OTP unlock authentication failure.
|
49
|
+
after_otp_unlock_auth_success :: Run arbitrary code after OTP unlock authentication success.
|
50
|
+
after_otp_unlock_not_yet_available :: Run arbitrary code when attempting OTP unlock when it is not yet available.
|
51
|
+
before_otp_unlock_attempt :: Run arbitrary code before checking whether OTP unlock authentication code is valid.
|
52
|
+
before_otp_unlock_route :: Run arbitrary code before handling an OTP unlock route.
|
53
|
+
otp_unlock_auth_failure :: Handle a authentication failure when trying to unlock. By default, this sets the number of consecutive successful authentication attempts to 0, and forces a significant delay before the next unlock authentication attempt can be made.
|
54
|
+
otp_unlock_auth_success :: Handle a authentication failure when trying to unlock. By default, this increments the number of consecutive successful authentication attempts, and imposes a short delay before the next unlock authentication attempt can be made (to ensure the code cannot be reused).
|
55
|
+
otp_unlock_available? :: Returns whether it is possible to unlock OTP authentication. This assumes that OTP is already locked out.
|
56
|
+
otp_unlock_deadline_passed? :: Returns whether the deadline to submit an OTP unlock authentication code has passed.
|
57
|
+
otp_unlock_not_available_view :: The HTML to use for the page when the OTP unlock form is not yet available due to a recent unlock authentication attempt.
|
58
|
+
otp_unlock_view :: The HTML to use for the OTP unlock form.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An otp_unlock feature has been added, allowing a user to unlock
|
4
|
+
TOTP authentication with 3 consecutive successful TOTP
|
5
|
+
authentications. Previously, once TOTP authentication was locked
|
6
|
+
out, there was no way for the user to unlock it.
|
7
|
+
|
8
|
+
Any unsuccessful TOTP authentication during the unlock process
|
9
|
+
prevents unlocks attempts for a configurable amount of time (15
|
10
|
+
minutes by default). By default, this limits brute force attempts
|
11
|
+
to unlock TOTP authentication to less than 10^2 per day, with the
|
12
|
+
odds of a successful unlock in each attempt being 1 in 10^18.
|
13
|
+
|
14
|
+
* An otp_lockout_email feature has been added for emailing the user
|
15
|
+
when their TOTP authentication has been locked out or unlocked, and
|
16
|
+
when there has been a failed unlock attempt.
|
17
|
+
|
18
|
+
* An otp_modify_email feature has been added for emailing the user
|
19
|
+
when TOTP authentication has been setup or disabled for their
|
20
|
+
account.
|
21
|
+
|
22
|
+
* A webauthn_modify_email feature has been added for emailing the
|
23
|
+
user when a WebAuthn authenticator has been added or removed from
|
24
|
+
their account.
|
25
|
+
|
26
|
+
* An account_from_id configuration method has been added for loading
|
27
|
+
the account with the given account id.
|
28
|
+
|
29
|
+
* A strftime_format configuration method has been added for
|
30
|
+
configuring how Time values are formatted for display to the user.
|
31
|
+
|
32
|
+
= Improvements
|
33
|
+
|
34
|
+
* The internal_request feature now works with Roda's path_rewriter
|
35
|
+
plugin.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
= Documentation for WebAuthn Modify Email Feature
|
2
|
+
|
3
|
+
The webauthn_modify_email feature emails users when a WebAuthn authenticator is added to or removed from their account.
|
4
|
+
|
5
|
+
The webauthn_modify_email feature depends on the webauthn and email_base features.
|
6
|
+
|
7
|
+
== Auth Value Methods
|
8
|
+
|
9
|
+
webauthn_authenticator_added_email_body :: Body to use for the email notifying user that a WebAuthn authenticator has been added to their account.
|
10
|
+
webauthn_authenticator_added_email_subject :: Subject to use for the email notifying user that a WebAuthn authenticator has been added to their account.
|
11
|
+
webauthn_authenticator_removed_email_body :: Body to use for the email notifying user that a WebAuthn authenticator has been removed from their account.
|
12
|
+
webauthn_authenticator_removed_email_subject :: Subject to use for the email notifying user that a WebAuthn authenticator has been removed from their account.
|
13
|
+
|
14
|
+
== Auth Methods
|
15
|
+
|
16
|
+
create_webauthn_authenticator_added_email :: A Mail::Message for the email notifying user that a WebAuthn authenticator has been added to their account.
|
17
|
+
create_webauthn_authenticator_removed_email :: A Mail::Message for the email notifying user that a WebAuthn authenticator has been removed from their account.
|
18
|
+
send_webauthn_authenticator_added_email :: Send the email notifying user that a WebAuthn authenticator has been added to their account.
|
19
|
+
send_webauthn_authenticator_removed_email :: Send the email notifying user that a WebAuthn authenticator has been removed from their account.
|
@@ -59,6 +59,7 @@ module Rodauth
|
|
59
59
|
auth_value_method :mark_input_fields_with_autocomplete?, true
|
60
60
|
auth_value_method :mark_input_fields_with_inputmode?, true
|
61
61
|
auth_value_method :skip_status_checks?, true
|
62
|
+
translatable_method :strftime_format, '%F %T'
|
62
63
|
auth_value_method :template_opts, {}.freeze
|
63
64
|
auth_value_method :title_instance_variable, nil
|
64
65
|
auth_value_method :token_separator, "_"
|
@@ -115,6 +116,7 @@ module Rodauth
|
|
115
116
|
)
|
116
117
|
|
117
118
|
auth_private_methods(
|
119
|
+
:account_from_id,
|
118
120
|
:account_from_login,
|
119
121
|
:account_from_session,
|
120
122
|
:convert_token_id,
|
@@ -384,6 +386,10 @@ module Rodauth
|
|
384
386
|
@account = _account_from_session
|
385
387
|
end
|
386
388
|
|
389
|
+
def account_from_id(id, status_id=nil)
|
390
|
+
@account = _account_from_id(id, status_id)
|
391
|
+
end
|
392
|
+
|
387
393
|
def check_csrf
|
388
394
|
scope.check_csrf!(check_csrf_opts, &check_csrf_block)
|
389
395
|
end
|
@@ -739,6 +745,12 @@ module Rodauth
|
|
739
745
|
ds.first
|
740
746
|
end
|
741
747
|
|
748
|
+
def _account_from_id(id, status_id=nil)
|
749
|
+
ds = account_ds(id)
|
750
|
+
ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks?
|
751
|
+
ds.first
|
752
|
+
end
|
753
|
+
|
742
754
|
def hmac_secret_rotation?
|
743
755
|
hmac_secret && hmac_old_secret && hmac_secret != hmac_old_secret
|
744
756
|
end
|
@@ -74,9 +74,7 @@ module Rodauth
|
|
74
74
|
((!hmac_secret || allow_raw_email_token?) && timing_safe_eql?(key, actual))
|
75
75
|
return
|
76
76
|
end
|
77
|
-
|
78
|
-
ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks?
|
79
|
-
ds.first
|
77
|
+
_account_from_id(id, status_id)
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
@@ -183,9 +183,7 @@ module Rodauth
|
|
183
183
|
def account_from_key(token, status_id=nil)
|
184
184
|
return super unless session_value
|
185
185
|
return unless yield session_value
|
186
|
-
|
187
|
-
ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks?
|
188
|
-
ds.first
|
186
|
+
_account_from_id(session_value, status_id)
|
189
187
|
end
|
190
188
|
|
191
189
|
def _set_internal_request_return_value(value)
|
@@ -210,7 +208,7 @@ module Rodauth
|
|
210
208
|
end
|
211
209
|
|
212
210
|
def _set_login_param_from_account
|
213
|
-
if session_value && !params[login_param] && (account =
|
211
|
+
if session_value && !params[login_param] && (account = _account_from_id(session_value))
|
214
212
|
params[login_param] = account[login_column]
|
215
213
|
end
|
216
214
|
end
|
@@ -311,7 +309,7 @@ module Rodauth
|
|
311
309
|
|
312
310
|
env = {
|
313
311
|
'REQUEST_METHOD'=>'POST',
|
314
|
-
'PATH_INFO'=>'/',
|
312
|
+
'PATH_INFO'=>'/'.dup,
|
315
313
|
"SCRIPT_NAME" => "",
|
316
314
|
"HTTP_HOST" => INVALID_DOMAIN,
|
317
315
|
"SERVER_NAME" => INVALID_DOMAIN,
|
@@ -72,6 +72,32 @@ module Rodauth
|
|
72
72
|
|
73
73
|
private
|
74
74
|
|
75
|
+
def _set_otp_unlock_info
|
76
|
+
if use_json?
|
77
|
+
json_response[:num_successes] = otp_unlock_num_successes
|
78
|
+
json_response[:required_successes] = otp_unlock_auths_required
|
79
|
+
json_response[:next_attempt_after] = otp_unlock_next_auth_attempt_after.to_i
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def after_otp_unlock_auth_success
|
84
|
+
super if defined?(super)
|
85
|
+
if otp_locked_out?
|
86
|
+
_set_otp_unlock_info
|
87
|
+
json_response[:deadline] = otp_unlock_deadline.to_i
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def after_otp_unlock_auth_failure
|
92
|
+
super if defined?(super)
|
93
|
+
_set_otp_unlock_info
|
94
|
+
end
|
95
|
+
|
96
|
+
def after_otp_unlock_not_yet_available
|
97
|
+
super if defined?(super)
|
98
|
+
_set_otp_unlock_info
|
99
|
+
end
|
100
|
+
|
75
101
|
def before_two_factor_manage_route
|
76
102
|
super if defined?(super)
|
77
103
|
if use_json?
|
data/lib/rodauth/features/otp.rb
CHANGED
@@ -287,7 +287,7 @@ module Rodauth
|
|
287
287
|
|
288
288
|
def otp_update_last_use
|
289
289
|
otp_key_ds.
|
290
|
-
where(Sequel.date_add(otp_keys_last_use_column, :seconds=>
|
290
|
+
where(Sequel.date_add(otp_keys_last_use_column, :seconds=>_otp_interval) < Sequel::CURRENT_TIMESTAMP).
|
291
291
|
update(otp_keys_last_use_column=>Sequel::CURRENT_TIMESTAMP) == 1
|
292
292
|
end
|
293
293
|
|
@@ -346,7 +346,7 @@ module Rodauth
|
|
346
346
|
|
347
347
|
def _two_factor_auth_links
|
348
348
|
links = super
|
349
|
-
links << [20, otp_auth_path, otp_auth_link_text] if
|
349
|
+
links << [20, otp_auth_path, otp_auth_link_text] if show_otp_auth_link?
|
350
350
|
links
|
351
351
|
end
|
352
352
|
|
@@ -420,6 +420,10 @@ module Rodauth
|
|
420
420
|
@otp_key = secret
|
421
421
|
end
|
422
422
|
|
423
|
+
def _otp_interval
|
424
|
+
otp_interval || 30
|
425
|
+
end
|
426
|
+
|
423
427
|
# Called for valid OTP codes for old secrets
|
424
428
|
def _otp_valid_code_for_old_secret
|
425
429
|
end
|
@@ -447,6 +451,10 @@ module Rodauth
|
|
447
451
|
db[otp_keys_table].where(otp_keys_id_column=>session_value)
|
448
452
|
end
|
449
453
|
|
454
|
+
def show_otp_auth_link?
|
455
|
+
otp_available?
|
456
|
+
end
|
457
|
+
|
450
458
|
def use_date_arithmetic?
|
451
459
|
true
|
452
460
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:otp_lockout_email, :OtpLockoutEmail) do
|
5
|
+
depends :otp_unlock, :email_base
|
6
|
+
|
7
|
+
loaded_templates %w'otp-locked-out-email otp-unlocked-email otp-unlock-failed-email'
|
8
|
+
email :otp_locked_out, 'TOTP Authentication Locked Out', :translatable=>true
|
9
|
+
email :otp_unlocked, 'TOTP Authentication Unlocked', :translatable=>true
|
10
|
+
email :otp_unlock_failed, 'TOTP Authentication Unlocking Failed', :translatable=>true
|
11
|
+
|
12
|
+
auth_value_method :send_otp_locked_out_email?, true
|
13
|
+
auth_value_method :send_otp_unlocked_email?, true
|
14
|
+
auth_value_method :send_otp_unlock_failed_email?, true
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def after_otp_authentication_failure
|
19
|
+
super
|
20
|
+
|
21
|
+
if otp_locked_out? && send_otp_locked_out_email?
|
22
|
+
send_otp_locked_out_email
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_otp_unlock_auth_success
|
27
|
+
super
|
28
|
+
|
29
|
+
if !otp_locked_out? && send_otp_unlocked_email?
|
30
|
+
send_otp_unlocked_email
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def after_otp_unlock_auth_failure
|
35
|
+
super
|
36
|
+
|
37
|
+
if send_otp_unlock_failed_email?
|
38
|
+
send_otp_unlock_failed_email
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:otp_modify_email, :OtpModifyEmail) do
|
5
|
+
depends :otp, :email_base
|
6
|
+
|
7
|
+
loaded_templates %w'otp-setup-email otp-disabled-email'
|
8
|
+
email :otp_setup, 'TOTP Authentication Setup', :translatable=>true
|
9
|
+
email :otp_disabled, 'TOTP Authentication Disabled', :translatable=>true
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def after_otp_setup
|
14
|
+
super
|
15
|
+
send_otp_setup_email
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_otp_disable
|
19
|
+
super
|
20
|
+
send_otp_disabled_email
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:otp_unlock, :OtpUnlock) do
|
5
|
+
depends :otp
|
6
|
+
|
7
|
+
before 'otp_unlock_attempt'
|
8
|
+
after 'otp_unlock_auth_success'
|
9
|
+
after 'otp_unlock_auth_failure'
|
10
|
+
after 'otp_unlock_not_yet_available'
|
11
|
+
|
12
|
+
error_flash "TOTP authentication is not currently locked out", 'otp_unlock_not_locked_out'
|
13
|
+
error_flash "TOTP invalid authentication", 'otp_unlock_auth_failure'
|
14
|
+
error_flash "Deadline past for unlocking TOTP authentication", 'otp_unlock_auth_deadline_passed'
|
15
|
+
error_flash "TOTP unlock attempt not yet available", 'otp_unlock_auth_not_yet_available'
|
16
|
+
|
17
|
+
notice_flash "TOTP authentication unlocked", 'otp_unlocked'
|
18
|
+
notice_flash "TOTP successful authentication, more successful authentication needed to unlock", 'otp_unlock_auth_success'
|
19
|
+
|
20
|
+
redirect :otp_unlock_not_locked_out
|
21
|
+
redirect :otp_unlocked
|
22
|
+
|
23
|
+
additional_form_tags
|
24
|
+
|
25
|
+
button 'Authenticate Using TOTP to Unlock', 'otp_unlock'
|
26
|
+
|
27
|
+
auth_value_method :otp_unlock_auth_deadline_passed_error_status, 403
|
28
|
+
auth_value_method :otp_unlock_auth_failure_cooldown_seconds, 900
|
29
|
+
auth_value_method :otp_unlock_auth_failure_error_status, 403
|
30
|
+
auth_value_method :otp_unlock_auth_not_yet_available_error_status, 403
|
31
|
+
auth_value_method :otp_unlock_auths_required, 3
|
32
|
+
auth_value_method :otp_unlock_deadline_seconds, 900
|
33
|
+
auth_value_method :otp_unlock_id_column, :id
|
34
|
+
auth_value_method :otp_unlock_next_auth_attempt_after_column, :next_auth_attempt_after
|
35
|
+
auth_value_method :otp_unlock_not_locked_out_error_status, 403
|
36
|
+
auth_value_method :otp_unlock_num_successes_column, :num_successes
|
37
|
+
auth_value_method :otp_unlock_table, :account_otp_unlocks
|
38
|
+
|
39
|
+
translatable_method :otp_unlock_consecutive_successes_label, 'Consecutive successful authentications'
|
40
|
+
translatable_method :otp_unlock_form_footer, ''
|
41
|
+
translatable_method :otp_unlock_next_auth_attempt_label, 'Can attempt next authentication after'
|
42
|
+
translatable_method :otp_unlock_next_auth_attempt_refresh_label, 'Page will automatically refresh when authentication is possible.'
|
43
|
+
translatable_method :otp_unlock_next_auth_deadline_label, 'Deadline for next authentication'
|
44
|
+
translatable_method :otp_unlock_required_consecutive_successes_label, 'Required consecutive successful authentications to unlock'
|
45
|
+
|
46
|
+
loaded_templates %w'otp-unlock otp-unlock-not-available'
|
47
|
+
view 'otp-unlock', 'Unlock TOTP Authentication', 'otp_unlock'
|
48
|
+
view 'otp-unlock-not-available', 'Must Wait to Unlock TOTP Authentication', 'otp_unlock_not_available'
|
49
|
+
|
50
|
+
auth_methods(
|
51
|
+
:otp_unlock_auth_failure,
|
52
|
+
:otp_unlock_auth_success,
|
53
|
+
:otp_unlock_available?,
|
54
|
+
:otp_unlock_deadline_passed?,
|
55
|
+
:otp_unlock_refresh_tag,
|
56
|
+
)
|
57
|
+
|
58
|
+
route(:otp_unlock) do |r|
|
59
|
+
require_login
|
60
|
+
require_account_session
|
61
|
+
require_otp_setup
|
62
|
+
|
63
|
+
unless otp_locked_out?
|
64
|
+
set_response_error_reason_status(:otp_not_locked_out, otp_unlock_not_locked_out_error_status)
|
65
|
+
set_redirect_error_flash otp_unlock_not_locked_out_error_flash
|
66
|
+
redirect otp_unlock_not_locked_out_redirect
|
67
|
+
end
|
68
|
+
|
69
|
+
before_otp_unlock_route
|
70
|
+
|
71
|
+
r.get do
|
72
|
+
if otp_unlock_available?
|
73
|
+
otp_unlock_view
|
74
|
+
else
|
75
|
+
otp_unlock_not_available_view
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
r.post do
|
80
|
+
db.transaction do
|
81
|
+
if otp_unlock_deadline_passed?
|
82
|
+
set_response_error_reason_status(:otp_unlock_deadline_passed, otp_unlock_auth_deadline_passed_error_status)
|
83
|
+
set_redirect_error_flash otp_unlock_auth_deadline_passed_error_flash
|
84
|
+
elsif !otp_unlock_available?
|
85
|
+
after_otp_unlock_not_yet_available
|
86
|
+
set_response_error_reason_status(:otp_unlock_not_yet_available, otp_unlock_auth_not_yet_available_error_status)
|
87
|
+
set_redirect_error_flash otp_unlock_auth_not_yet_available_error_flash
|
88
|
+
else
|
89
|
+
before_otp_unlock_attempt
|
90
|
+
if otp_valid_code?(param(otp_auth_param))
|
91
|
+
otp_unlock_auth_success
|
92
|
+
after_otp_unlock_auth_success
|
93
|
+
|
94
|
+
unless otp_locked_out?
|
95
|
+
set_notice_flash otp_unlocked_notice_flash
|
96
|
+
redirect otp_unlocked_redirect
|
97
|
+
end
|
98
|
+
|
99
|
+
set_notice_flash otp_unlock_auth_success_notice_flash
|
100
|
+
else
|
101
|
+
otp_unlock_auth_failure
|
102
|
+
after_otp_unlock_auth_failure
|
103
|
+
set_response_error_reason_status(:otp_unlock_auth_failure, otp_unlock_auth_failure_error_status)
|
104
|
+
set_redirect_error_flash otp_unlock_auth_failure_error_flash
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
redirect request.path
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def otp_unlock_available?
|
114
|
+
if otp_unlock_data
|
115
|
+
next_auth_attempt_after = otp_unlock_next_auth_attempt_after
|
116
|
+
current_timestamp = Time.now
|
117
|
+
|
118
|
+
if (next_auth_attempt_after < current_timestamp - otp_unlock_deadline_seconds)
|
119
|
+
# Unlock process not fully completed within deadline, reset process
|
120
|
+
otp_unlock_reset
|
121
|
+
true
|
122
|
+
else
|
123
|
+
if next_auth_attempt_after > current_timestamp
|
124
|
+
# If next auth attempt after timestamp is in the future, that means the next
|
125
|
+
# unlock attempt cannot happen until then.
|
126
|
+
false
|
127
|
+
else
|
128
|
+
if otp_unlock_num_successes == 0
|
129
|
+
# 0 value indicates previous attempt was a failure. Since failure cooldown
|
130
|
+
# period has passed, reset process so user gets full deadline period
|
131
|
+
otp_unlock_reset
|
132
|
+
end
|
133
|
+
true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
else
|
137
|
+
# No row means no unlock attempts yet (or previous attempt was more than the
|
138
|
+
# deadline account, so unlocking is available
|
139
|
+
true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def otp_unlock_auth_failure
|
144
|
+
h = {
|
145
|
+
otp_unlock_num_successes_column=>0,
|
146
|
+
otp_unlock_next_auth_attempt_after_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_auth_failure_cooldown_seconds)
|
147
|
+
}
|
148
|
+
|
149
|
+
if otp_unlock_ds.update(h) == 0
|
150
|
+
h[otp_unlock_id_column] = session_value
|
151
|
+
|
152
|
+
# If row already exists when inserting, no need to do anything
|
153
|
+
raises_uniqueness_violation?{otp_unlock_ds.insert(h)}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def otp_unlock_auth_success
|
158
|
+
deadline = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_success_cooldown_seconds)
|
159
|
+
|
160
|
+
# Add WHERE to avoid possible race condition when multiple unlock auth requests
|
161
|
+
# are sent at the same time (only the first should increment num successes).
|
162
|
+
if otp_unlock_ds.
|
163
|
+
where(Sequel[otp_unlock_next_auth_attempt_after_column] < Sequel::CURRENT_TIMESTAMP).
|
164
|
+
update(
|
165
|
+
otp_unlock_num_successes_column=>Sequel[otp_unlock_num_successes_column]+1,
|
166
|
+
otp_unlock_next_auth_attempt_after_column=>deadline
|
167
|
+
) == 0
|
168
|
+
|
169
|
+
# Ignore uniqueness errors when inserting after a failed update,
|
170
|
+
# which could be caused due to the race condition mentioned above.
|
171
|
+
raises_uniqueness_violation? do
|
172
|
+
otp_unlock_ds.insert(
|
173
|
+
otp_unlock_id_column=>session_value,
|
174
|
+
otp_unlock_next_auth_attempt_after_column=>deadline
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
@otp_unlock_data = nil
|
180
|
+
# :nocov:
|
181
|
+
if otp_unlock_data
|
182
|
+
# :nocov:
|
183
|
+
if otp_unlock_num_successes >= otp_unlock_auths_required
|
184
|
+
# At least the requisite number of consecutive successful unlock
|
185
|
+
# authentications. Unlock OTP authentication.
|
186
|
+
otp_key_ds.update(otp_keys_failures_column => 0)
|
187
|
+
|
188
|
+
# Remove OTP unlock metadata when unlocking OTP authentication
|
189
|
+
otp_unlock_reset
|
190
|
+
# else
|
191
|
+
# # Still need additional consecutive successful unlock attempts.
|
192
|
+
end
|
193
|
+
# else
|
194
|
+
# # if row isn't available, probably the process was reset during this,
|
195
|
+
# # and it's safe to do nothing in that case.
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def otp_unlock_deadline_passed?
|
200
|
+
otp_unlock_data ? (otp_unlock_next_auth_attempt_after < Time.now - otp_unlock_deadline_seconds) : false
|
201
|
+
end
|
202
|
+
|
203
|
+
def otp_unlock_refresh_tag
|
204
|
+
"<meta http-equiv=\"refresh\" content=\"#{(otp_unlock_next_auth_attempt_after - Time.now).to_i + 1}\">"
|
205
|
+
end
|
206
|
+
|
207
|
+
def otp_lockout_redirect
|
208
|
+
otp_unlock_path
|
209
|
+
end
|
210
|
+
|
211
|
+
def otp_unlock_next_auth_attempt_after
|
212
|
+
if otp_unlock_data
|
213
|
+
convert_timestamp(otp_unlock_data[otp_unlock_next_auth_attempt_after_column])
|
214
|
+
else
|
215
|
+
Time.now
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def otp_unlock_deadline
|
220
|
+
otp_unlock_next_auth_attempt_after + otp_unlock_deadline_seconds
|
221
|
+
end
|
222
|
+
|
223
|
+
def otp_unlock_num_successes
|
224
|
+
otp_unlock_data ? otp_unlock_data[otp_unlock_num_successes_column] : 0
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def show_otp_auth_link?
|
230
|
+
super || (otp_exists? && otp_locked_out?)
|
231
|
+
end
|
232
|
+
|
233
|
+
def otp_unlock_data
|
234
|
+
@otp_unlock_data ||= otp_unlock_ds.first
|
235
|
+
end
|
236
|
+
|
237
|
+
def otp_unlock_success_cooldown_seconds
|
238
|
+
(_otp_interval+(otp_drift||0))*2
|
239
|
+
end
|
240
|
+
|
241
|
+
def otp_unlock_reset
|
242
|
+
otp_unlock_ds.delete
|
243
|
+
@otp_unlock_data = nil
|
244
|
+
end
|
245
|
+
|
246
|
+
def otp_unlock_ds
|
247
|
+
db[otp_unlock_table].where(otp_unlock_id_column=>session_value)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -53,7 +53,7 @@ module Rodauth
|
|
53
53
|
throw_error_reason(:invalid_webauthn_id, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_webauthn_id_message)
|
54
54
|
end
|
55
55
|
|
56
|
-
|
56
|
+
account_from_id(account_id)
|
57
57
|
end
|
58
58
|
|
59
59
|
def webauthn_login_options?
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:webauthn_modify_email, :WebauthnModifyEmail) do
|
5
|
+
depends :webauthn, :email_base
|
6
|
+
|
7
|
+
loaded_templates %w'webauthn-authenticator-added-email webauthn-authenticator-removed-email'
|
8
|
+
email :webauthn_authenticator_added, 'WebAuthn Authenticator Added', :translatable=>true
|
9
|
+
email :webauthn_authenticator_removed, 'WebAuthn Authenticator Removed', :translatable=>true
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def after_webauthn_setup
|
14
|
+
super
|
15
|
+
send_webauthn_authenticator_added_email
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_webauthn_remove
|
19
|
+
super
|
20
|
+
send_webauthn_authenticator_removed_email
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rodauth/version.rb
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
TOTP authentication has been locked out on your account due to too many
|
2
|
+
consecutive authentication failures. You can attempt to unlock TOTP
|
3
|
+
authentication for your account by consecutively authenticating via
|
4
|
+
TOTP multiple times.
|
5
|
+
|
6
|
+
If you did not initiate the TOTP authentication failures that
|
7
|
+
caused TOTP authentication to be locked out, that means someone already
|
8
|
+
has partial access to your account, but is unable to use TOTP
|
9
|
+
authentication to fully authenticate themselves.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Someone (hopefully you) attempted to unlock TOTP authentication for the
|
2
|
+
account associated to this email address, but failed as the
|
3
|
+
authentication code submitted was not correct.
|
4
|
+
|
5
|
+
If you did not initiate the TOTP authentication failure that generated
|
6
|
+
this email, that means someone already has partial access to your
|
7
|
+
account, but is unable to use TOTP authentication to fully authenticate
|
8
|
+
themselves.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<p>#{rodauth.otp_unlock_consecutive_successes_label}: #{rodauth.otp_unlock_num_successes}</p>
|
2
|
+
<p>#{rodauth.otp_unlock_required_consecutive_successes_label}: #{rodauth.otp_unlock_auths_required}</p>
|
3
|
+
<p>#{rodauth.otp_unlock_next_auth_attempt_label}: #{rodauth.otp_unlock_next_auth_attempt_after.strftime(rodauth.strftime_format)}</p>
|
4
|
+
<p>#{rodauth.otp_unlock_next_auth_attempt_refresh_label}</p>
|
5
|
+
#{rodauth.otp_unlock_refresh_tag}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<form method="post" class="rodauth" role="form" id="otp-unlock-form">
|
2
|
+
#{rodauth.otp_unlock_additional_form_tags}
|
3
|
+
#{rodauth.csrf_tag}
|
4
|
+
<p>#{rodauth.otp_unlock_consecutive_successes_label}: #{rodauth.otp_unlock_num_successes}</p>
|
5
|
+
<p>#{rodauth.otp_unlock_required_consecutive_successes_label}: #{rodauth.otp_unlock_auths_required}</p>
|
6
|
+
<p>#{rodauth.otp_unlock_next_auth_deadline_label}: #{rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format)}</p>
|
7
|
+
#{rodauth.render('otp-auth-code-field')}
|
8
|
+
#{rodauth.button(rodauth.otp_unlock_button)}
|
9
|
+
</form>
|
10
|
+
|
11
|
+
#{rodauth.otp_unlock_form_footer}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
#{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
|
5
5
|
<fieldset class="form-group mb-3">
|
6
6
|
#{(usage = rodauth.account_webauthn_usage; last_id = usage.keys.last; usage;).map do |id, last_use|
|
7
|
-
last_use = last_use.strftime(
|
7
|
+
last_use = last_use.strftime(rodauth.strftime_format) if last_use.is_a?(Time)
|
8
8
|
input = rodauth.input_field_string(rodauth.webauthn_remove_param, "webauthn-remove-#{h id}", :type=>'radio', :class=>"form-check-input", :skip_error_message=>true, :value=>id, :required=>false)
|
9
9
|
label = "<label class=\"rodauth-webauthn-id form-check-label\" for=\"webauthn-remove-#{h id}\">Last Use: #{last_use}</label>"
|
10
10
|
error = rodauth.formatted_field_error(rodauth.webauthn_remove_param) if id == last_id
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.36.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -278,6 +278,9 @@ extra_rdoc_files:
|
|
278
278
|
- doc/login_password_requirements_base.rdoc
|
279
279
|
- doc/logout.rdoc
|
280
280
|
- doc/otp.rdoc
|
281
|
+
- doc/otp_lockout_email.rdoc
|
282
|
+
- doc/otp_modify_email.rdoc
|
283
|
+
- doc/otp_unlock.rdoc
|
281
284
|
- doc/password_complexity.rdoc
|
282
285
|
- doc/password_expiration.rdoc
|
283
286
|
- doc/password_grace_period.rdoc
|
@@ -298,6 +301,7 @@ extra_rdoc_files:
|
|
298
301
|
- doc/webauthn.rdoc
|
299
302
|
- doc/webauthn_autofill.rdoc
|
300
303
|
- doc/webauthn_login.rdoc
|
304
|
+
- doc/webauthn_modify_email.rdoc
|
301
305
|
- doc/webauthn_verify_account.rdoc
|
302
306
|
- doc/release_notes/1.0.0.txt
|
303
307
|
- doc/release_notes/1.1.0.txt
|
@@ -353,6 +357,7 @@ extra_rdoc_files:
|
|
353
357
|
- doc/release_notes/2.33.0.txt
|
354
358
|
- doc/release_notes/2.34.0.txt
|
355
359
|
- doc/release_notes/2.35.0.txt
|
360
|
+
- doc/release_notes/2.36.0.txt
|
356
361
|
- doc/release_notes/2.4.0.txt
|
357
362
|
- doc/release_notes/2.5.0.txt
|
358
363
|
- doc/release_notes/2.6.0.txt
|
@@ -416,6 +421,9 @@ files:
|
|
416
421
|
- doc/login_password_requirements_base.rdoc
|
417
422
|
- doc/logout.rdoc
|
418
423
|
- doc/otp.rdoc
|
424
|
+
- doc/otp_lockout_email.rdoc
|
425
|
+
- doc/otp_modify_email.rdoc
|
426
|
+
- doc/otp_unlock.rdoc
|
419
427
|
- doc/password_complexity.rdoc
|
420
428
|
- doc/password_expiration.rdoc
|
421
429
|
- doc/password_grace_period.rdoc
|
@@ -476,6 +484,7 @@ files:
|
|
476
484
|
- doc/release_notes/2.33.0.txt
|
477
485
|
- doc/release_notes/2.34.0.txt
|
478
486
|
- doc/release_notes/2.35.0.txt
|
487
|
+
- doc/release_notes/2.36.0.txt
|
479
488
|
- doc/release_notes/2.4.0.txt
|
480
489
|
- doc/release_notes/2.5.0.txt
|
481
490
|
- doc/release_notes/2.6.0.txt
|
@@ -496,6 +505,7 @@ files:
|
|
496
505
|
- doc/webauthn.rdoc
|
497
506
|
- doc/webauthn_autofill.rdoc
|
498
507
|
- doc/webauthn_login.rdoc
|
508
|
+
- doc/webauthn_modify_email.rdoc
|
499
509
|
- doc/webauthn_verify_account.rdoc
|
500
510
|
- javascript/webauthn_auth.js
|
501
511
|
- javascript/webauthn_autofill.js
|
@@ -528,6 +538,9 @@ files:
|
|
528
538
|
- lib/rodauth/features/login_password_requirements_base.rb
|
529
539
|
- lib/rodauth/features/logout.rb
|
530
540
|
- lib/rodauth/features/otp.rb
|
541
|
+
- lib/rodauth/features/otp_lockout_email.rb
|
542
|
+
- lib/rodauth/features/otp_modify_email.rb
|
543
|
+
- lib/rodauth/features/otp_unlock.rb
|
531
544
|
- lib/rodauth/features/password_complexity.rb
|
532
545
|
- lib/rodauth/features/password_expiration.rb
|
533
546
|
- lib/rodauth/features/password_grace_period.rb
|
@@ -548,6 +561,7 @@ files:
|
|
548
561
|
- lib/rodauth/features/webauthn.rb
|
549
562
|
- lib/rodauth/features/webauthn_autofill.rb
|
550
563
|
- lib/rodauth/features/webauthn_login.rb
|
564
|
+
- lib/rodauth/features/webauthn_modify_email.rb
|
551
565
|
- lib/rodauth/features/webauthn_verify_account.rb
|
552
566
|
- lib/rodauth/migrations.rb
|
553
567
|
- lib/rodauth/version.rb
|
@@ -573,7 +587,14 @@ files:
|
|
573
587
|
- templates/otp-auth-code-field.str
|
574
588
|
- templates/otp-auth.str
|
575
589
|
- templates/otp-disable.str
|
590
|
+
- templates/otp-disabled-email.str
|
591
|
+
- templates/otp-locked-out-email.str
|
592
|
+
- templates/otp-setup-email.str
|
576
593
|
- templates/otp-setup.str
|
594
|
+
- templates/otp-unlock-failed-email.str
|
595
|
+
- templates/otp-unlock-not-available.str
|
596
|
+
- templates/otp-unlock.str
|
597
|
+
- templates/otp-unlocked-email.str
|
577
598
|
- templates/password-changed-email.str
|
578
599
|
- templates/password-confirm-field.str
|
579
600
|
- templates/password-field.str
|
@@ -602,6 +623,8 @@ files:
|
|
602
623
|
- templates/verify-login-change-email.str
|
603
624
|
- templates/verify-login-change.str
|
604
625
|
- templates/webauthn-auth.str
|
626
|
+
- templates/webauthn-authenticator-added-email.str
|
627
|
+
- templates/webauthn-authenticator-removed-email.str
|
605
628
|
- templates/webauthn-autofill.str
|
606
629
|
- templates/webauthn-remove.str
|
607
630
|
- templates/webauthn-setup.str
|
@@ -636,7 +659,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
636
659
|
- !ruby/object:Gem::Version
|
637
660
|
version: '0'
|
638
661
|
requirements: []
|
639
|
-
rubygems_version: 3.5.
|
662
|
+
rubygems_version: 3.5.11
|
640
663
|
signing_key:
|
641
664
|
specification_version: 4
|
642
665
|
summary: Authentication and Account Management Framework for Rack Applications
|