rodauth 2.31.0 → 2.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +36 -0
- data/README.rdoc +1 -1
- data/doc/argon2.rdoc +9 -5
- data/doc/base.rdoc +1 -0
- data/doc/change_login.rdoc +1 -0
- data/doc/change_password.rdoc +1 -0
- data/doc/close_account.rdoc +1 -0
- data/doc/confirm_password.rdoc +1 -0
- data/doc/create_account.rdoc +1 -0
- data/doc/email_auth.rdoc +1 -0
- data/doc/jwt.rdoc +1 -0
- data/doc/lockout.rdoc +4 -2
- data/doc/login.rdoc +2 -1
- data/doc/logout.rdoc +1 -0
- data/doc/otp.rdoc +3 -0
- data/doc/release_notes/2.32.0.txt +65 -0
- data/doc/release_notes/2.33.0.txt +18 -0
- data/doc/remember.rdoc +1 -0
- data/doc/reset_password.rdoc +2 -0
- data/doc/sms_codes.rdoc +7 -0
- data/doc/two_factor_base.rdoc +2 -0
- data/doc/verify_account.rdoc +2 -0
- data/doc/verify_login_change.rdoc +1 -0
- data/doc/webauthn.rdoc +2 -0
- data/lib/rodauth/features/active_sessions.rb +10 -4
- data/lib/rodauth/features/argon2.rb +26 -6
- data/lib/rodauth/features/base.rb +39 -4
- data/lib/rodauth/features/change_login.rb +2 -2
- data/lib/rodauth/features/change_password.rb +2 -2
- data/lib/rodauth/features/close_account.rb +2 -2
- data/lib/rodauth/features/confirm_password.rb +2 -2
- data/lib/rodauth/features/create_account.rb +3 -3
- data/lib/rodauth/features/email_auth.rb +13 -20
- data/lib/rodauth/features/email_base.rb +4 -6
- data/lib/rodauth/features/jwt.rb +17 -1
- data/lib/rodauth/features/jwt_refresh.rb +4 -2
- data/lib/rodauth/features/lockout.rb +12 -14
- data/lib/rodauth/features/login.rb +13 -4
- data/lib/rodauth/features/logout.rb +2 -2
- data/lib/rodauth/features/otp.rb +35 -7
- data/lib/rodauth/features/remember.rb +15 -11
- data/lib/rodauth/features/reset_password.rb +11 -13
- data/lib/rodauth/features/single_session.rb +4 -3
- data/lib/rodauth/features/sms_codes.rb +42 -13
- data/lib/rodauth/features/two_factor_base.rb +8 -6
- data/lib/rodauth/features/update_password_hash.rb +2 -1
- data/lib/rodauth/features/verify_account.rb +13 -20
- data/lib/rodauth/features/verify_login_change.rb +8 -10
- data/lib/rodauth/features/webauthn.rb +6 -6
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +16 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57dd78525df05b6947a84692c0f061b435dc2d07c072f689dcda121c788c59dd
|
4
|
+
data.tar.gz: 984dde3de0dd4329505eaaf04e8a49b8e5c4b8347e8dea454d6aa586ab0f5846
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0abb1ad9eedc3f0d23db686ac490483a86ba73c8f5a9e33bd375e5a93b98790a133b2e1da6599bfdd3759f0a56e4c890ac1cbf0f89bf7edc69e8159de7c173ed
|
7
|
+
data.tar.gz: 2d82400b1298b9a372ef8fd4aea5f9e1ba490e6572113a112f15e3dd1a3ed0655d4d934c84d18f61909bc6d0ed54c6bc2e1486f945da14cedbe35f1ea1614c10
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,39 @@
|
|
1
|
+
=== 2.33.0 (2023-12-21)
|
2
|
+
|
3
|
+
* Expire SMS confirm code after 24 hours by default (jeremyevans)
|
4
|
+
|
5
|
+
* Do not accidentally confirm SMS phone number on successful authentication of other second factor (Bertg) (#376, #377)
|
6
|
+
|
7
|
+
* Return error response instead of 404 response for requests to valid pages with missing tokens (janko) (#375)
|
8
|
+
|
9
|
+
* Do not override existing primary key value in the cached account when inserting a new account (janko) (#372)
|
10
|
+
|
11
|
+
=== 2.32.0 (2023-10-23)
|
12
|
+
|
13
|
+
* Remove use of Base64 in argon2 feature (jeremyevans)
|
14
|
+
|
15
|
+
* Add sms_needs_confirmation_notice_flash configuration method, supporting different flash notice for successful submission (jeremyevans)
|
16
|
+
|
17
|
+
* Support *_response configuration methods for overriding common notice flash/redirect handling in many features (HoneyryderChuck, jeremyevans) (#369)
|
18
|
+
|
19
|
+
* Support hmac_secret rotation in the otp feature (jeremyevans) (#365)
|
20
|
+
|
21
|
+
* Support hmac_secret rotation in the email_base feature (jeremyevans) (#365)
|
22
|
+
|
23
|
+
* Support hmac_secret rotation in the webauthn feature (jeremyevans) (#365)
|
24
|
+
|
25
|
+
* Support hmac_secret rotation in the jwt_refresh feature (jeremyevans) (#365)
|
26
|
+
|
27
|
+
* Support hmac_secret rotation in the single_session feature (jeremyevans) (#365)
|
28
|
+
|
29
|
+
* Support hmac_secret rotation in the remember feature (jeremyevans) (#365)
|
30
|
+
|
31
|
+
* Support hmac_secret rotation via hmac_old_secret configuration method in the active_sessions feature (jeremyevans) (#365)
|
32
|
+
|
33
|
+
* Support argon2 secret rotation via argon2_old_secret configuration method and the update_password_hash feature (jeremyevans) (#365)
|
34
|
+
|
35
|
+
* Support jwt secret rotation via jwt_old_secret configuration method, if using jwt 2.4+ (jeremyevans) (#365)
|
36
|
+
|
1
37
|
=== 2.31.0 (2023-08-22)
|
2
38
|
|
3
39
|
* Make clear_session work correctly for internal requests (janko) (#359)
|
data/README.rdoc
CHANGED
@@ -1323,7 +1323,7 @@ application to call Rodauth methods.
|
|
1323
1323
|
|
1324
1324
|
If you're using the remember feature with +extend_remember_deadline?+ set to
|
1325
1325
|
true, you'll want to load roda's middleware plugin with
|
1326
|
-
|
1326
|
+
<tt>forward_response_headers: true</tt> option, so that +Set-Cookie+ header changes
|
1327
1327
|
from the +load_memory+ call in the route block are propagated when the request
|
1328
1328
|
is forwarded to the main app.
|
1329
1329
|
|
data/doc/argon2.rdoc
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
The argon2 feature adds the ability to replace the bcrypt password hash
|
4
4
|
algorithm with argon2 (specifically, argon2id). Argon2 is an alternative to
|
5
|
-
bcrypt that offers the ability to be memory-hard. However,
|
6
|
-
|
7
|
-
|
8
|
-
security
|
5
|
+
bcrypt that offers the ability to be memory-hard. However, argon2 is weaker
|
6
|
+
than bcrypt for interactive login environments (e.g. password check times
|
7
|
+
under a second), so for the vast majority of web applications, using the
|
8
|
+
argon2 feature will weaken the application's security. You should not use
|
9
|
+
the argon2 feature unless the usage of argon2 is required or you are a
|
10
|
+
cryptographer and understand why argon2 would be better than bcrypt for your
|
11
|
+
application.
|
9
12
|
|
10
13
|
If you are using this feature with Rodauth's database authentication functions,
|
11
14
|
you need to make sure that the database authentication functions are configured
|
@@ -32,7 +35,7 @@ In this example, +DB+ should be your Sequel::Database object:
|
|
32
35
|
|
33
36
|
The argon2 feature provides the ability to allow for a gradual migration
|
34
37
|
from transitioning from bcrypt to argon2 and vice-versa, if you are using the
|
35
|
-
update_password_hash.
|
38
|
+
update_password_hash feature.
|
36
39
|
|
37
40
|
Argon2 is more configurable than bcrypt in terms of password hash cost
|
38
41
|
speficiation. Instead of specifying the password_hash_cost value as
|
@@ -46,5 +49,6 @@ memory.
|
|
46
49
|
|
47
50
|
== Auth Value Methods
|
48
51
|
|
52
|
+
argon2_old_secret :: The previous secret key used as input at hashing time, used for argon2_secret rotation. In order to rotate the argon2_secret, you must also use the update_password_hash feature, and rotation will not be finished until all users have logged in using the new secret.
|
49
53
|
argon2_secret :: A secret key used as input at hashing time, folded into the value of the hash.
|
50
54
|
use_argon2? :: Whether to use the argon2 password hash algorithm for new passwords (true by default). The only reason to set this to false is if you have existing passwords using argon2 that you want to support, but want to use bcrypt for new passwords.
|
data/doc/base.rdoc
CHANGED
@@ -43,6 +43,7 @@ field_error_attributes(field) :: The attributes to use for the input field tags
|
|
43
43
|
flash_error_key :: The flash key to use for error messages (default: +:error+ or <tt>'error'</tt> depending on session support for symbols).
|
44
44
|
flash_notice_key :: The flash key to use for notice messages (default: +:notice+ or <tt>'notice'</tt> depending on session support for symbols).
|
45
45
|
formatted_field_error(field, error) :: HTML to use for error messages for the field (parameter name), if the field has an error. By default, uses a span tag for the error message.
|
46
|
+
hmac_old_secret :: This sets the previous secret used for Rodauth's HMACs, to allow for secret rotation.
|
46
47
|
hook_action(hook_type, action) :: Arbitrary action to take on all hook processing, with hook type being +:before+ or +:after+, and action being symbol for related action.
|
47
48
|
input_field_error_class :: The CSS class to use for input fields with errors. Can be a space separated string for multiple CSS classes.
|
48
49
|
input_field_error_message_class :: The CSS class to use for error messages. Can be a space separated string for multiple CSS classes.
|
data/doc/change_login.rdoc
CHANGED
@@ -21,4 +21,5 @@ after_change_login :: Run arbitrary code after successful login change.
|
|
21
21
|
before_change_login :: Run arbitrary code before changing a login.
|
22
22
|
before_change_login_route :: Run arbitrary code before handling a change login route.
|
23
23
|
change_login(login) :: Change the users login to the given login, or return nil/false if the login cannot be changed to the given login.
|
24
|
+
change_login_response :: Return a response after a successful login change. By default, redirects to +change_login_redirect+.
|
24
25
|
change_login_view :: The HTML to use for the change login form.
|
data/doc/change_password.rdoc
CHANGED
@@ -22,4 +22,5 @@ new_password_param :: The parameter name to use for new passwords.
|
|
22
22
|
after_change_password :: Run arbitrary code after successful password change.
|
23
23
|
before_change_password :: Run arbitrary code before changing the password for an account.
|
24
24
|
before_change_password_route :: Run arbitrary code before handling a change password route.
|
25
|
+
change_password_response :: Return a response after a successful password change. By default, redirects to +change_password_redirect+.
|
25
26
|
change_password_view :: The HTML to use for the change password form.
|
data/doc/close_account.rdoc
CHANGED
@@ -21,5 +21,6 @@ after_close_account :: Run arbitrary code after closing the account.
|
|
21
21
|
before_close_account :: Run arbitrary code before closing an account.
|
22
22
|
before_close_account_route :: Run arbitrary code before handling a close account route.
|
23
23
|
close_account :: Close the account, by default setting the account status to closed.
|
24
|
+
close_account_response :: Return a response after successfully closing the account . By default, redirects to +close_account_redirect+.
|
24
25
|
close_account_view :: The HTML to use for the close account form.
|
25
26
|
delete_account :: If +delete_account_on_close?+ is true, delete the account when closing it.
|
data/doc/confirm_password.rdoc
CHANGED
@@ -28,4 +28,5 @@ after_confirm_password :: Run arbitrary code after successful confirmation of pa
|
|
28
28
|
before_confirm_password :: Run arbitrary code before setting that the password has been confirmed.
|
29
29
|
before_confirm_password_route :: Run arbitrary code before handling the password confirmation route.
|
30
30
|
confirm_password :: Update the session to reflect the password has been confirmed.
|
31
|
+
confirm_password_response :: Return a response after successful password confirmation. By default, redirects to +confirm_password_redirect+.
|
31
32
|
confirm_password_view :: The HTML to use for the confirm password form.
|
data/doc/create_account.rdoc
CHANGED
@@ -20,6 +20,7 @@ before_create_account :: Run arbitrary code before creating the account.
|
|
20
20
|
before_create_account_route :: Run arbitrary code before handling a create account route.
|
21
21
|
create_account_autologin? :: Whether to autologin the user upon successful account creation, true by default unless verifying accounts.
|
22
22
|
create_account_link_text :: The text to use for a link to the create account form.
|
23
|
+
create_account_response :: Return a response after successful account creation. By default, redirects to +create_account_redirect+.
|
23
24
|
create_account_view :: The HTML to use for the create account form.
|
24
25
|
new_account(login) :: Instantiate a new account hash for the given login, without saving it.
|
25
26
|
save_account :: Insert the account into the database, or return nil/false if that was not successful.
|
data/doc/email_auth.rdoc
CHANGED
@@ -43,6 +43,7 @@ create_email_auth_email :: A Mail::Message for the email auth email.
|
|
43
43
|
create_email_auth_key :: Add the email auth key data to the database.
|
44
44
|
email_auth_email_body :: The body to use for the email auth email.
|
45
45
|
email_auth_email_link :: The link to the email auth form in the email auth email.
|
46
|
+
email_auth_email_sent_response :: Return a response after successfully sending an email auth email. By default, redirects to +email_auth_email_sent_redirect+.
|
46
47
|
email_auth_key_insert_hash :: The hash to insert into the +email_auth_table+.
|
47
48
|
email_auth_key_value :: The email auth key for the current account.
|
48
49
|
email_auth_request_form :: The HTML to use for a form to request an email auth email, shown on the login page after the user submits their login, if +force_email_auth?+ is false and email authentication is not the only possible for of authentication for the user.
|
data/doc/jwt.rdoc
CHANGED
@@ -38,6 +38,7 @@ jwt_algorithm :: The JWT algorithm to use, +HS256+ by default.
|
|
38
38
|
jwt_authorization_ignore :: A regexp matched against the Authorization header, which skips JWT processing if it matches. By default, HTTP Basic and Digest authentication are ignored.
|
39
39
|
jwt_authorization_remove :: A regexp to remove from the Authorization header before processing the JWT. By default, a Bearer prefix is removed.
|
40
40
|
jwt_decode_opts :: An optional hash to pass to +JWT.decode+. Can be used to set JWT verifiers.
|
41
|
+
jwt_old_secret :: The previous JWT secret used, to support JWT secret rotation (only supported when using jwt 2.4+). Access to this should be protected the same as a session secret.
|
41
42
|
jwt_secret :: The JWT secret to use. Access to this should be protected the same as a session secret.
|
42
43
|
jwt_session_key :: A key to nest the session hash under in the JWT payload. nil by default, for no nesting.
|
43
44
|
jwt_symbolize_deeply? :: Whether to symbolize the session hash deeply. false by default.
|
data/doc/lockout.rdoc
CHANGED
@@ -46,14 +46,14 @@ unlock_account_skip_resend_email_within :: The number of seconds before sending
|
|
46
46
|
|
47
47
|
== Auth Methods
|
48
48
|
|
49
|
-
account_from_unlock_key(key) :: Retrieve the account using the given verify account key, or return nil if no account matches.
|
49
|
+
account_from_unlock_key(key) :: Retrieve the account using the given verify account key, or return nil if no account matches.
|
50
50
|
after_account_lockout :: Run arbitrary code after an account has been locked out.
|
51
51
|
after_unlock_account :: Run arbitrary code after a successful account unlock.
|
52
52
|
after_unlock_account_request :: Run arbitrary code after a successful account unlock request.
|
53
53
|
before_unlock_account :: Run arbitrary code before unlocking an account.
|
54
54
|
before_unlock_account_request :: Run arbitrary code before sending an account unlock email.
|
55
55
|
before_unlock_account_request_route :: Run arbitrary code before handling an account unlock request route.
|
56
|
-
before_unlock_account_route :: Run arbitrary code before handling an unlock account route.
|
56
|
+
before_unlock_account_route :: Run arbitrary code before handling an unlock account route.
|
57
57
|
clear_invalid_login_attempts :: Clear any stored login failures or lockouts for the current account.
|
58
58
|
create_unlock_account_email :: A Mail::Message for the account unlock email to send.
|
59
59
|
generate_unlock_account_key :: A random string to use for a new unlock account key.
|
@@ -67,5 +67,7 @@ unlock_account :: Unlock the account.
|
|
67
67
|
unlock_account_email_body :: The body to use for the unlock account email.
|
68
68
|
unlock_account_email_link :: The link to the unlock account form to include in the unlock account email.
|
69
69
|
unlock_account_key :: The unlock account key for the current account.
|
70
|
+
unlock_account_request_response :: Return a response after successfully requesting an account unlock. By default, redirects to +unlock_account_request_redirect+.
|
70
71
|
unlock_account_request_view :: The HTML to use for the unlock account request form.
|
72
|
+
unlock_account_response :: Return a response after successfully unlocking an account. By default, redirects to +unlock_account_redirect+.
|
71
73
|
unlock_account_view :: The HTML to use for the unlock account form.
|
data/doc/login.rdoc
CHANGED
@@ -33,6 +33,7 @@ use_multi_phase_login? :: Whether to ask for login first, and only ask for passw
|
|
33
33
|
== Auth Methods
|
34
34
|
|
35
35
|
before_login_route :: Run arbitrary code before handling a login route.
|
36
|
-
|
36
|
+
login_response :: Return a response after a successful login. By default, redirects to +login_redirect+ (or the requested location if +login_return_to_requested_location?+ is true).
|
37
37
|
login_return_to_requested_location_path :: If +login_return_to_requested_location?+ is true, the path to use as the requested location. By default, uses the full path of the request for GET requests, and is nil for non-GET requests (in which case the default +login_redirect+ will be used).
|
38
|
+
login_view :: The HTML to use for the login form.
|
38
39
|
multi_phase_login_view :: The HTML to use for the login form after login has been entered when using multi phase login.
|
data/doc/logout.rdoc
CHANGED
@@ -18,4 +18,5 @@ after_logout :: Run arbitrary code after logout.
|
|
18
18
|
before_logout :: Run arbitrary code before logout.
|
19
19
|
before_logout_route :: Run arbitrary code before handling a logout route.
|
20
20
|
logout :: Log the user out, by default clearing the session.
|
21
|
+
logout_response :: Return a response after a successful logout. By default, redirects to +logout_redirect+.
|
21
22
|
logout_view :: The HTML to use for the logout form.
|
data/doc/otp.rdoc
CHANGED
@@ -71,6 +71,7 @@ otp :: The object used for verifying OTP authentication attempts.
|
|
71
71
|
otp_add_key(secret) :: Add an OTP key for the current account with the given secret.
|
72
72
|
otp_auth_view :: The HTML to use for the OTP authentication form.
|
73
73
|
otp_available? :: Whether OTP authentication is ready for use.
|
74
|
+
otp_disable_response :: Return a response after successfully disabling OTP . By default, redirects to +otp_disable_redirect+.
|
74
75
|
otp_disable_view :: The HTML to use for the OTP disable form.
|
75
76
|
otp_exists? :: Whether the current account has setup OTP.
|
76
77
|
otp_key :: The stored OTP secret for the account.
|
@@ -83,8 +84,10 @@ otp_qr_code :: The QR code containing the otp_provisioning_uri, by default an SV
|
|
83
84
|
otp_record_authentication_failure :: Record an OTP authentication failure.
|
84
85
|
otp_remove :: Removes all stored OTP data for the current account.
|
85
86
|
otp_remove_auth_failures :: Removes OTP authentication failures for the current account, used after successful multifactor authentication.
|
87
|
+
otp_setup_response :: Return a response after successful OTP setup. By default, redirects to +otp_setup_redirect+.
|
86
88
|
otp_setup_view :: The HTML to use for the form to setup OTP authentication.
|
87
89
|
otp_tmp_key(secret) :: Set the secret to use for the temporary OTP key, during OTP setup.
|
88
90
|
otp_update_last_use :: Update the last time OTP authentication was successful for the account. Return true if the authentication should be allowed, or false if it should not be allowed because the last authentication was too recent and indicates the possible reuse of a TOTP authentication code.
|
91
|
+
otp_valid_code_for_old_secret :: Called when valid OTP authentication is performed using hmac_old_secret. This indicates the OTP needs to be rotated before support for the previous hmac secret value is removed. You can use this to track users who need their OTP rotated, and take appropriate action.
|
89
92
|
otp_valid_code?(auth_code) :: Whether the given code is the currently valid OTP auth code for the account.
|
90
93
|
otp_valid_key?(secret) :: Whether the given secret is a valid OTP secret.
|
@@ -0,0 +1,65 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Rodauth now supports secret rotation using the following
|
4
|
+
configuration methods:
|
5
|
+
|
6
|
+
* hmac_old_secret
|
7
|
+
* argon2_old_secret (argon2 feature)
|
8
|
+
* jwt_old_secret (jwt feature)
|
9
|
+
|
10
|
+
You can use these methods to specify the previous secret when
|
11
|
+
rotating secrets. Note that full secret rotation (where you can
|
12
|
+
remove use of the old secret) may not be simple. Here are some
|
13
|
+
cases that require additional work:
|
14
|
+
|
15
|
+
* Rotating the argon2 secret requires the use of the
|
16
|
+
update_password_hash feature. You cannot remove the use of
|
17
|
+
argon2_old_secret unless every user who created a password under
|
18
|
+
the old secret has logged in after the new secret was added.
|
19
|
+
Removing the old secret before a user has logged in after the new
|
20
|
+
secret was added will invalidate the password for the user. Thus,
|
21
|
+
full rotation of the argon2 secret requires invalidating passwords
|
22
|
+
for inactive accounts.
|
23
|
+
|
24
|
+
* Full rotating of the hmac secret when using the remember feature
|
25
|
+
requires that all remember cookies created under the previous
|
26
|
+
secret has been removed. By default, remember cookies expire in
|
27
|
+
2 weeks, but it is possible to set them much longer.
|
28
|
+
|
29
|
+
* Full rotation of the hmac secret when using the verify_account
|
30
|
+
feature requires invalidating old verify account links, since
|
31
|
+
verify account links do not have a deadline. However, after old
|
32
|
+
verify account links have been invalidated, a user can request a
|
33
|
+
new verify account link, which will work.
|
34
|
+
|
35
|
+
* Full rotation of the hmac secret when using the otp feature
|
36
|
+
requires disabling otp and reenabling otp. The
|
37
|
+
otp_valid_code_for_old_secret configuration method has been added,
|
38
|
+
which can be used to handle cases where a user successfully
|
39
|
+
authenticated via TOTP using the old secret. This can be used
|
40
|
+
to direct them to a page to remove the TOTP authenticator and
|
41
|
+
then setup a new TOTP authenicator.
|
42
|
+
|
43
|
+
* Many *_response configuration methods have been added, which allow
|
44
|
+
users to override Rodauth's default behavior in successful cases of
|
45
|
+
setting a flash notice and then redirecting. Note that using these
|
46
|
+
configuration methods correctly requires that they halt request
|
47
|
+
processing. You cannot just have them return a response body. You
|
48
|
+
can use the return_response method to set the response body and
|
49
|
+
halt processing.
|
50
|
+
|
51
|
+
* An sms_needs_confirmation_notice_flash configuration method has been
|
52
|
+
added, for setting the flash notice when setting up SMS
|
53
|
+
authentication. By default, it uses the
|
54
|
+
sms_needs_confirmation_error_flash value.
|
55
|
+
|
56
|
+
= Other Improvements
|
57
|
+
|
58
|
+
* The argon2 feature no longer uses the Base64 constant. Previously,
|
59
|
+
it uses the library without attempting to require the base64 library,
|
60
|
+
which would break if the base64 library was not already required.
|
61
|
+
|
62
|
+
* Rodauth's documentation now recommends against the use of the argon2
|
63
|
+
feature, because for typical interactive login uses (targetting
|
64
|
+
sub-200ms response times), argon2 provides significantly worse
|
65
|
+
security than bcrypt.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* Rodauth no longer accidentally confirms an SMS number upon valid
|
4
|
+
authentication by an alternative second factor.
|
5
|
+
|
6
|
+
* Rodauth now automatically expires SMS confirmation codes after 24
|
7
|
+
hours by default. You can use the sms_confirm_deadline
|
8
|
+
configuration method to adjust the deadline. Previously, if an
|
9
|
+
invalid SMS number was submitted, or the SMS confirm code was never
|
10
|
+
received, it was not possible to continue SMS setup without
|
11
|
+
administrative intervention.
|
12
|
+
|
13
|
+
* Rodauth no longer overwrites existing primary key values when
|
14
|
+
inserting new accounts. This fixes cases such as setting account
|
15
|
+
primary key values to UUIDs before inserting.
|
16
|
+
|
17
|
+
* When submitting a request to a valid endpoint with a missing token,
|
18
|
+
Rodauth now returns an error response instead of a 404 response.
|
data/doc/remember.rdoc
CHANGED
@@ -74,5 +74,6 @@ logged_in_via_remember_key? :: Whether the current session was logged in via a r
|
|
74
74
|
remembered_session_id :: The session_id which is validly remembered, if any.
|
75
75
|
remember_key_value :: The current value of the remember key/token.
|
76
76
|
remember_login :: Set the cookie containing the remember token, so that future sessions will be autologged in.
|
77
|
+
remember_response :: Return a response after successfully changing remember settings. By default, redirects to +remember_redirect+.
|
77
78
|
remember_view :: The HTML to use for the change remember settings form.
|
78
79
|
remove_remember_key(id_value=account_id) :: Delete the related remember key from the database.
|
data/doc/reset_password.rdoc
CHANGED
@@ -56,9 +56,11 @@ login_failed_reset_password_request_form :: The HTML to use for a form to reques
|
|
56
56
|
remove_reset_password_key :: Remove the reset password key for the current account, run after successful password reset.
|
57
57
|
reset_password_email_body :: The body to use for the reset password request email.
|
58
58
|
reset_password_email_link :: The link to the reset password form in the reset password request email.
|
59
|
+
reset_password_email_sent_response :: Return a response after successfully sending a password reset email. By default, redirects to +reset_password_email_sent_redirect+.
|
59
60
|
reset_password_key_insert_hash :: The hash to insert into the +reset_password_table+.
|
60
61
|
reset_password_key_value :: The reset password key for the current account.
|
61
62
|
reset_password_request_view :: The HTML to use for the reset password request form.
|
63
|
+
reset_password_response :: Return a response after successfully resetting a password. By default, redirects to +reset_password_redirect+.
|
62
64
|
reset_password_view :: The HTML to use for the reset password form.
|
63
65
|
send_reset_password_email :: Send the reset password request email.
|
64
66
|
set_reset_password_email_last_sent :: Set the last time a reset password request email is sent.
|
data/doc/sms_codes.rdoc
CHANGED
@@ -41,6 +41,7 @@ sms_codes_table :: The name of the table storing SMS code data.
|
|
41
41
|
sms_confirm_additional_form_tags :: HTML fragment containing additional form tags when confirming SMS setup.
|
42
42
|
sms_confirm_button :: Text to use for button on the form to confirm SMS setup.
|
43
43
|
sms_confirm_code_length :: The length of SMS confirmation codes, 12 by default, as there is no lockout.
|
44
|
+
sms_confirm_deadline :: The number of seconds before an SMS confirmation code expires (86400 seconds by default).
|
44
45
|
sms_confirm_notice_flash :: The flash notice to show when SMS authentication setup has been confirmed.
|
45
46
|
sms_confirm_page_title :: The page title to use on the form to authenticate via SMS code.
|
46
47
|
sms_confirm_redirect :: Where to redirect after SMS authentication setup has been confirmed.
|
@@ -63,6 +64,7 @@ sms_invalid_phone_message :: The error message to show when an invalid SMS phone
|
|
63
64
|
sms_issued_at_column :: The column in the +sms_codes_table+ containing the time the SMS code was issued.
|
64
65
|
sms_lockout_error_flash :: The flash error to show when SMS authentication has been locked out due to repeated failures.
|
65
66
|
sms_lockout_redirect :: Where to redirect after SMS authentication has been locked out.
|
67
|
+
sms_needs_confirmation_notice_flash :: The flash notice to show on SMS authentication pages when SMS authentication setup needs confirmation (uses +sms_needs_confirmation_error_flash+ by default).
|
66
68
|
sms_needs_confirmation_error_flash :: The flash error to show on SMS authentication pages when SMS authentication setup needs confirmation.
|
67
69
|
sms_needs_confirmation_error_status :: The response status to use on SMS authentication pages when SMS authentication setup needs confirmation, 403 by default.
|
68
70
|
sms_needs_confirmation_redirect :: Where to redirect after SMS setup, when confirmation is required.
|
@@ -109,19 +111,24 @@ sms_available? :: Whether SMS authentication is ready for use.
|
|
109
111
|
sms_code_issued_at :: The timestamp the current SMS code was issued at.
|
110
112
|
sms_code_match?(code) :: Whether there is an active SMS authentication code for the current account and the given code matches it.
|
111
113
|
sms_confirm_message(code) :: The SMS message to use for the given confirmation code.
|
114
|
+
sms_confirm_response :: Return a response after successfully confirming SMS code during SMS setup. By default, redirects to +sms_confirm_redirect+.
|
112
115
|
sms_confirm_view :: The HTML to use for the form to authenticate via SMS code.
|
113
116
|
sms_confirmation_match?(code) :: Whether there is an active SMS confirmation code for the current account and the given code matches it.
|
114
117
|
sms_current_auth? :: Whether there is a active SMS authentication code for the current account.
|
115
118
|
sms_disable :: Action to take to disable SMS authentication for the account.
|
119
|
+
sms_disable_response :: Return a response after successfully disabling SMS. By default, redirects to +sms_disable_redirect+.
|
116
120
|
sms_disable_view :: The HTML to use for the form to disable SMS authentication.
|
117
121
|
sms_failures :: The number of SMS authentication failures since the last successfully SMS authentication for this account.
|
118
122
|
sms_locked_out? :: Whether SMS authentication has been locked out for the current account.
|
119
123
|
sms_needs_confirmation? :: Whether SMS authentication has been setup but not confirmed for the current account.
|
124
|
+
sms_needs_confirmation_response :: Return a response after successfully providing SMS number during SMS setup. By default, redirects to +sms_needs_confirmation_redirect+.
|
120
125
|
sms_new_auth_code :: A new SMS authentication code that can be used for the account.
|
121
126
|
sms_new_confirm_code :: A new SMS confirmation code that can be used for the account.
|
122
127
|
sms_normalize_phone(phone) :: A normalized version of the given phone number, by default removing everything except 0-9.
|
123
128
|
sms_record_failure :: Record an SMS authentication failure for the current account.
|
129
|
+
sms_remove_expired_confirm_code :: Remove an expired SMS confirm code, allowing setup of a new sms confirm code.
|
124
130
|
sms_remove_failures :: Reset the SMS authentication failure counter for the current account, used after a successful multifactor authentication.
|
131
|
+
sms_request_response :: Return a response after a successful SMS request during SMS authentication. By default, redirects to +sms_auth_redirect+.
|
125
132
|
sms_request_view :: The HTML to use for the form to request an SMS authentication code.
|
126
133
|
sms_send(phone, message) :: Send the given message to the given phone number via SMS. By default a NotImplementedError is raised, this is the only method that must be overridden.
|
127
134
|
sms_set_code(code) :: Set the SMS authentication code for the current account to the given code. The code can be nil to specify that no SMS authentication code is currently valid.
|
data/doc/two_factor_base.rdoc
CHANGED
@@ -56,8 +56,10 @@ before_two_factor_disable :: Any actions to take before disabling of all multifa
|
|
56
56
|
before_two_factor_disable_route :: Run arbitrary code before handling the multifactor disable route.
|
57
57
|
before_two_factor_manage_route :: Run arbitrary code before handling the multifactor manage route.
|
58
58
|
two_factor_auth_links :: An array of entries for links to show on the multifactor auth page. Each entry is an array of three elements, sort order (integer), link href, and link text.
|
59
|
+
two_factor_auth_response :: Return a response after successful multifactor authentication. By default, redirects to +two_factor_auth_redirect+ (or the requested location if +two_factor_auth_return_to_requested_location?+ is true).
|
59
60
|
two_factor_auth_view :: The HTML to use for the page linking to other multifactor authentication pages.
|
60
61
|
two_factor_authenticated? :: Whether the current session has already been multifactor authenticated.
|
62
|
+
two_factor_disable_response :: Return a response after successfully disabling multifactor authentication. By default, redirects to +two_factor_disable_redirect+.
|
61
63
|
two_factor_disable_view :: The HTML to use for the page for disabling all multifactor authentication.
|
62
64
|
two_factor_manage_view :: The HTML to use for the page linking to other multifactor setup and remove pages.
|
63
65
|
two_factor_remove :: Any action to take to remove multifactor authentication, called when closing accounts.
|
data/doc/verify_account.rdoc
CHANGED
@@ -60,6 +60,8 @@ set_verify_account_email_last_sent :: Set the last time a verify account email i
|
|
60
60
|
verify_account :: Verify the account by changing the status from unverified to open.
|
61
61
|
verify_account_email_body :: The body to use for the verify account email.
|
62
62
|
verify_account_email_link :: The link to the verify account form in the verify account email.
|
63
|
+
verify_account_email_sent_response :: Return a response after successfully sending an verify account email. By default, redirects to +verify_account_email_sent_redirect+.
|
63
64
|
verify_account_key_insert_hash :: The hash to insert into the +verify_account_table+.
|
64
65
|
verify_account_key_value :: The value of the verify account key.
|
66
|
+
verify_account_response :: Return a response after successfully verifying an account. By default, redirects to +verify_account_redirect+.
|
65
67
|
verify_account_view :: The HTML to use for the verify account form.
|
@@ -55,4 +55,5 @@ verify_login_change_key_insert_hash(login) :: The hash to insert into the +verif
|
|
55
55
|
verify_login_change_key_value :: The value of the verify login change key.
|
56
56
|
verify_login_change_new_login :: The new login to use when the login change is verified.
|
57
57
|
verify_login_change_old_login :: The old login to display in the verify login change email.
|
58
|
+
verify_login_change_response :: Return a response after successfully verifying a login change. By default, redirects to +verify_login_change_redirect+.
|
58
59
|
verify_login_change_view :: The HTML to use for the verify login change form.
|
data/doc/webauthn.rdoc
CHANGED
@@ -109,8 +109,10 @@ webauthn_auth_view :: The HTML to use for the page for authenticating via WebAut
|
|
109
109
|
webauthn_credential_options_for_get :: WebAuthn credential options to provide to the client during WebAuthn authentication.
|
110
110
|
webauthn_key_insert_hash(webauthn_credential) :: The hash to insert into the +webauthn_keys_table+.
|
111
111
|
webauthn_remove_authenticated_session :: Remove the authenticated WebAuthn ID, used when removing the WebAuthn credential with the ID after authenticating with it.
|
112
|
+
webauthn_remove_response :: Return a response after successfully removing a WebAuthn authenticator. By default, redirects to +webauthn_remove_redirect+.
|
112
113
|
webauthn_remove_view :: The HTML to use for the page for removing an existing WebAuthn authenticator.
|
113
114
|
webauthn_setup_js_path :: The path to the WebAuthn registration javascript.
|
115
|
+
webauthn_setup_response :: Return a response after successfully setting up a WebAuthn authenticator. By default, redirects to +webauthn_setup_redirect+.
|
114
116
|
webauthn_setup_view :: The HTML to use for the page for registering a new WebAuthn authenticator.
|
115
117
|
webauthn_update_session(webauthn_id) :: Set the authenticated WebAuthn ID after authenticating via WebAuthn.
|
116
118
|
webauthn_user_name :: The user name to use when registering a new WebAuthn credential, the user's email by default.
|
@@ -40,7 +40,7 @@ module Rodauth
|
|
40
40
|
|
41
41
|
remove_inactive_sessions
|
42
42
|
ds = active_sessions_ds.
|
43
|
-
where(active_sessions_session_id_column =>
|
43
|
+
where(active_sessions_session_id_column => compute_hmacs(session_id))
|
44
44
|
|
45
45
|
if update_current_session?
|
46
46
|
ds.update(active_sessions_update_hash) == 1
|
@@ -83,7 +83,7 @@ module Rodauth
|
|
83
83
|
|
84
84
|
def remove_current_session
|
85
85
|
if session_id = session[session_id_session_key]
|
86
|
-
remove_active_session(
|
86
|
+
remove_active_session(compute_hmacs(session_id))
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
@@ -119,7 +119,7 @@ module Rodauth
|
|
119
119
|
key = generate_active_sessions_key
|
120
120
|
set_session_value(session_id_session_key, key)
|
121
121
|
active_sessions_ds.
|
122
|
-
where(active_sessions_session_id_column =>
|
122
|
+
where(active_sessions_session_id_column => compute_hmacs(prev_key)).
|
123
123
|
update(active_sessions_session_id_column => compute_hmac(key))
|
124
124
|
end
|
125
125
|
end
|
@@ -150,7 +150,13 @@ module Rodauth
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def active_sessions_update_hash
|
153
|
-
{active_sessions_last_use_column => Sequel::CURRENT_TIMESTAMP}
|
153
|
+
h = {active_sessions_last_use_column => Sequel::CURRENT_TIMESTAMP}
|
154
|
+
|
155
|
+
if hmac_secret_rotation?
|
156
|
+
h[active_sessions_session_id_column] = compute_hmac(session[session_id_session_key])
|
157
|
+
end
|
158
|
+
|
159
|
+
h
|
154
160
|
end
|
155
161
|
|
156
162
|
def session_inactivity_deadline_condition
|
@@ -12,6 +12,7 @@ module Rodauth
|
|
12
12
|
Feature.define(:argon2, :Argon2) do
|
13
13
|
depends :login_password_requirements_base
|
14
14
|
|
15
|
+
auth_value_method :argon2_old_secret, nil
|
15
16
|
auth_value_method :argon2_secret, nil
|
16
17
|
auth_value_method :use_argon2?, true
|
17
18
|
|
@@ -53,16 +54,19 @@ module Rodauth
|
|
53
54
|
|
54
55
|
def password_hash_using_salt(password, salt)
|
55
56
|
return super unless argon2_hash_algorithm?(salt)
|
57
|
+
argon2_password_hash_using_salt_and_secret(password, salt, argon2_secret)
|
58
|
+
end
|
56
59
|
|
60
|
+
def argon2_password_hash_using_salt_and_secret(password, salt, secret)
|
57
61
|
argon2_params = Hash[extract_password_hash_cost(salt)]
|
58
|
-
argon2_params[argon2_salt_option] =
|
59
|
-
argon2_params[:secret] =
|
62
|
+
argon2_params[argon2_salt_option] = salt.split('$').last.unpack("m")[0]
|
63
|
+
argon2_params[:secret] = secret
|
60
64
|
::Argon2::Password.new(argon2_params).create(password)
|
61
65
|
end
|
62
66
|
|
63
67
|
if Argon2::VERSION >= '2.1'
|
64
68
|
def extract_password_hash_cost(hash)
|
65
|
-
return super unless argon2_hash_algorithm?(hash
|
69
|
+
return super unless argon2_hash_algorithm?(hash)
|
66
70
|
|
67
71
|
/\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+),p=(\d+)/ =~ hash
|
68
72
|
{ t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i, p_cost: $3.to_i }
|
@@ -70,7 +74,7 @@ module Rodauth
|
|
70
74
|
|
71
75
|
if ENV['RACK_ENV'] == 'test'
|
72
76
|
def argon2_hash_cost
|
73
|
-
{ t_cost: 1, m_cost:
|
77
|
+
{ t_cost: 1, m_cost: 5, p_cost: 1 }
|
74
78
|
end
|
75
79
|
# :nocov:
|
76
80
|
else
|
@@ -88,7 +92,7 @@ module Rodauth
|
|
88
92
|
|
89
93
|
if ENV['RACK_ENV'] == 'test'
|
90
94
|
def argon2_hash_cost
|
91
|
-
{ t_cost: 1, m_cost:
|
95
|
+
{ t_cost: 1, m_cost: 5 }
|
92
96
|
end
|
93
97
|
else
|
94
98
|
def argon2_hash_cost
|
@@ -103,7 +107,23 @@ module Rodauth
|
|
103
107
|
end
|
104
108
|
|
105
109
|
def argon2_password_hash_match?(hash, password)
|
106
|
-
::Argon2::Password.verify_password(password, hash, argon2_secret)
|
110
|
+
ret = ::Argon2::Password.verify_password(password, hash, argon2_secret)
|
111
|
+
|
112
|
+
if ret == false && argon2_old_secret != argon2_secret && (ret = ::Argon2::Password.verify_password(password, hash, argon2_old_secret))
|
113
|
+
@update_password_hash = true
|
114
|
+
end
|
115
|
+
|
116
|
+
ret
|
117
|
+
end
|
118
|
+
|
119
|
+
def database_function_password_match?(name, hash_id, password, salt)
|
120
|
+
return true if super
|
121
|
+
|
122
|
+
if use_argon2? && argon2_hash_algorithm?(salt) && argon2_old_secret != argon2_secret && (ret = db.get(Sequel.function(function_name(name), hash_id, argon2_password_hash_using_salt_and_secret(password, salt, argon2_old_secret))))
|
123
|
+
@update_password_hash = true
|
124
|
+
end
|
125
|
+
|
126
|
+
!!ret
|
107
127
|
end
|
108
128
|
end
|
109
129
|
end
|
@@ -27,6 +27,7 @@ module Rodauth
|
|
27
27
|
auth_value_method :convert_token_id_to_integer?, nil
|
28
28
|
flash_key :flash_error_key, :error
|
29
29
|
flash_key :flash_notice_key, :notice
|
30
|
+
auth_value_method :hmac_old_secret, nil
|
30
31
|
auth_value_method :hmac_secret, nil
|
31
32
|
translatable_method :input_field_label_suffix, ''
|
32
33
|
auth_value_method :input_field_error_class, 'error is-invalid'
|
@@ -242,9 +243,24 @@ module Rodauth
|
|
242
243
|
|
243
244
|
# Return urlsafe base64 HMAC for data, assumes hmac_secret is set.
|
244
245
|
def compute_hmac(data)
|
245
|
-
|
246
|
-
|
247
|
-
|
246
|
+
_process_raw_hmac(compute_raw_hmac(data))
|
247
|
+
end
|
248
|
+
|
249
|
+
# Return urlsafe base64 HMAC for data using hmac_old_secret, assumes hmac_old_secret is set.
|
250
|
+
def compute_old_hmac(data)
|
251
|
+
_process_raw_hmac(compute_raw_hmac_with_secret(data, hmac_old_secret))
|
252
|
+
end
|
253
|
+
|
254
|
+
# Return array of hmacs. Array has two strings if hmac_old_secret
|
255
|
+
# is set, or one string otherwise.
|
256
|
+
def compute_hmacs(data)
|
257
|
+
hmacs = [compute_hmac(data)]
|
258
|
+
|
259
|
+
if hmac_old_secret
|
260
|
+
hmacs << compute_old_hmac(data)
|
261
|
+
end
|
262
|
+
|
263
|
+
hmacs
|
248
264
|
end
|
249
265
|
|
250
266
|
def account_id
|
@@ -515,6 +531,13 @@ module Rodauth
|
|
515
531
|
yield
|
516
532
|
end
|
517
533
|
|
534
|
+
def _process_raw_hmac(hmac)
|
535
|
+
s = [hmac].pack('m')
|
536
|
+
s.chomp!("=\n")
|
537
|
+
s.tr!('+/', '-_')
|
538
|
+
s
|
539
|
+
end
|
540
|
+
|
518
541
|
def database_function_password_match?(name, hash_id, password, salt)
|
519
542
|
db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
|
520
543
|
end
|
@@ -711,10 +734,17 @@ module Rodauth
|
|
711
734
|
ds.first
|
712
735
|
end
|
713
736
|
|
737
|
+
def hmac_secret_rotation?
|
738
|
+
hmac_secret && hmac_old_secret && hmac_secret != hmac_old_secret
|
739
|
+
end
|
740
|
+
|
714
741
|
def compute_raw_hmac(data)
|
715
742
|
raise ArgumentError, "hmac_secret not set" unless hmac_secret
|
743
|
+
compute_raw_hmac_with_secret(data, hmac_secret)
|
744
|
+
end
|
716
745
|
|
717
|
-
|
746
|
+
def compute_raw_hmac_with_secret(data, secret)
|
747
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, data)
|
718
748
|
end
|
719
749
|
|
720
750
|
def _field_attributes(field)
|
@@ -822,6 +852,11 @@ module Rodauth
|
|
822
852
|
false
|
823
853
|
end
|
824
854
|
|
855
|
+
def require_response(meth)
|
856
|
+
send(meth)
|
857
|
+
raise RuntimeError, "#{meth.to_s.sub(/\A_/, '')} overridden without returning a response (should use redirect or request.halt). This is a bug in your Rodauth configuration, not a bug in Rodauth itself."
|
858
|
+
end
|
859
|
+
|
825
860
|
def set_session_value(key, value)
|
826
861
|
session[key] = value
|
827
862
|
end
|