rodauth 2.31.0 → 2.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +26 -0
  3. data/README.rdoc +1 -1
  4. data/doc/argon2.rdoc +9 -5
  5. data/doc/base.rdoc +1 -0
  6. data/doc/change_login.rdoc +1 -0
  7. data/doc/change_password.rdoc +1 -0
  8. data/doc/close_account.rdoc +1 -0
  9. data/doc/confirm_password.rdoc +1 -0
  10. data/doc/create_account.rdoc +1 -0
  11. data/doc/email_auth.rdoc +1 -0
  12. data/doc/jwt.rdoc +1 -0
  13. data/doc/lockout.rdoc +4 -2
  14. data/doc/login.rdoc +2 -1
  15. data/doc/logout.rdoc +1 -0
  16. data/doc/otp.rdoc +3 -0
  17. data/doc/release_notes/2.32.0.txt +65 -0
  18. data/doc/remember.rdoc +1 -0
  19. data/doc/reset_password.rdoc +2 -0
  20. data/doc/sms_codes.rdoc +5 -0
  21. data/doc/two_factor_base.rdoc +2 -0
  22. data/doc/verify_account.rdoc +2 -0
  23. data/doc/verify_login_change.rdoc +1 -0
  24. data/doc/webauthn.rdoc +2 -0
  25. data/lib/rodauth/features/active_sessions.rb +10 -4
  26. data/lib/rodauth/features/argon2.rb +26 -6
  27. data/lib/rodauth/features/base.rb +39 -4
  28. data/lib/rodauth/features/change_login.rb +2 -2
  29. data/lib/rodauth/features/change_password.rb +2 -2
  30. data/lib/rodauth/features/close_account.rb +2 -2
  31. data/lib/rodauth/features/confirm_password.rb +2 -2
  32. data/lib/rodauth/features/create_account.rb +2 -2
  33. data/lib/rodauth/features/email_auth.rb +7 -12
  34. data/lib/rodauth/features/email_base.rb +4 -6
  35. data/lib/rodauth/features/jwt.rb +17 -1
  36. data/lib/rodauth/features/jwt_refresh.rb +4 -2
  37. data/lib/rodauth/features/lockout.rb +6 -6
  38. data/lib/rodauth/features/login.rb +13 -4
  39. data/lib/rodauth/features/logout.rb +2 -2
  40. data/lib/rodauth/features/otp.rb +35 -7
  41. data/lib/rodauth/features/remember.rb +15 -11
  42. data/lib/rodauth/features/reset_password.rb +5 -5
  43. data/lib/rodauth/features/single_session.rb +4 -3
  44. data/lib/rodauth/features/sms_codes.rb +23 -10
  45. data/lib/rodauth/features/two_factor_base.rb +8 -6
  46. data/lib/rodauth/features/update_password_hash.rb +2 -1
  47. data/lib/rodauth/features/verify_account.rb +7 -12
  48. data/lib/rodauth/features/verify_login_change.rb +2 -2
  49. data/lib/rodauth/features/webauthn.rb +6 -6
  50. data/lib/rodauth/version.rb +1 -1
  51. data/lib/rodauth.rb +16 -0
  52. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34f1a4bc530025482c6550375de67bf9dcbe4ea75ca9f4f775d179805629e115
4
- data.tar.gz: 75339b39caf60463c076459e1fc21fd4d2291e4bc756c4cc5107edf7462c0186
3
+ metadata.gz: ee670a5f0a59467c2043ca7649284c04665663fa4859b52bea1200722cf69256
4
+ data.tar.gz: 0a270ebc549934096225c47c0ea37e218658133cd7a5a10036c60e5d6a2f3c5c
5
5
  SHA512:
6
- metadata.gz: 9e66e012fca7c52bd25206fd7711921148e8372a249047687e5429074dd9ea64461dcbbda1a0412b16920270814fef9fa5954dda701ae30af66e7a78a7975855
7
- data.tar.gz: f7c9ec1aac7150a3fa1b4d2af3e8278f16ef3982d5cd84b6b2984d41b5f5b70f60e9f7c0e099d338055201d820bd099093609b2683086c5a8ec9fdf53dee2e58
6
+ metadata.gz: 8e36ee900735384b6179c44f9549c10719cd24055b04b2b4a46daf7e499aa5e9057abb205b37bb4be2ab0d43eddff88314f945d6b8e47dde4bb2b21da1160947
7
+ data.tar.gz: 90970c54c4f2492ff0956a8c8bdb91eb475487e5e45b71fad06a39403c658234656a644b871de4d18c74d14c3288fef5db5932ee0fc5c8434cc69788348c606c
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ === 2.32.0 (2023-10-23)
2
+
3
+ * Remove use of Base64 in argon2 feature (jeremyevans)
4
+
5
+ * Add sms_needs_confirmation_notice_flash configuration method, supporting different flash notice for successful submission (jeremyevans)
6
+
7
+ * Support *_response configuration methods for overriding common notice flash/redirect handling in many features (HoneyryderChuck, jeremyevans) (#369)
8
+
9
+ * Support hmac_secret rotation in the otp feature (jeremyevans) (#365)
10
+
11
+ * Support hmac_secret rotation in the email_base feature (jeremyevans) (#365)
12
+
13
+ * Support hmac_secret rotation in the webauthn feature (jeremyevans) (#365)
14
+
15
+ * Support hmac_secret rotation in the jwt_refresh feature (jeremyevans) (#365)
16
+
17
+ * Support hmac_secret rotation in the single_session feature (jeremyevans) (#365)
18
+
19
+ * Support hmac_secret rotation in the remember feature (jeremyevans) (#365)
20
+
21
+ * Support hmac_secret rotation via hmac_old_secret configuration method in the active_sessions feature (jeremyevans) (#365)
22
+
23
+ * Support argon2 secret rotation via argon2_old_secret configuration method and the update_password_hash feature (jeremyevans) (#365)
24
+
25
+ * Support jwt secret rotation via jwt_old_secret configuration method, if using jwt 2.4+ (jeremyevans) (#365)
26
+
1
27
  === 2.31.0 (2023-08-22)
2
28
 
3
29
  * 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
- +forward_response_headers: true+ option, so that +Set-Cookie+ header changes
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, if you are storing
6
- password hashes in a table that the database user does not have access to
7
- (the recommended way to use Rodauth), argon2 does not offer significant
8
- security advantages over bcrypt.
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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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
- login_view :: The HTML to use for the login form.
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.
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.
@@ -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
@@ -63,6 +63,7 @@ sms_invalid_phone_message :: The error message to show when an invalid SMS phone
63
63
  sms_issued_at_column :: The column in the +sms_codes_table+ containing the time the SMS code was issued.
64
64
  sms_lockout_error_flash :: The flash error to show when SMS authentication has been locked out due to repeated failures.
65
65
  sms_lockout_redirect :: Where to redirect after SMS authentication has been locked out.
66
+ 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
67
  sms_needs_confirmation_error_flash :: The flash error to show on SMS authentication pages when SMS authentication setup needs confirmation.
67
68
  sms_needs_confirmation_error_status :: The response status to use on SMS authentication pages when SMS authentication setup needs confirmation, 403 by default.
68
69
  sms_needs_confirmation_redirect :: Where to redirect after SMS setup, when confirmation is required.
@@ -109,19 +110,23 @@ sms_available? :: Whether SMS authentication is ready for use.
109
110
  sms_code_issued_at :: The timestamp the current SMS code was issued at.
110
111
  sms_code_match?(code) :: Whether there is an active SMS authentication code for the current account and the given code matches it.
111
112
  sms_confirm_message(code) :: The SMS message to use for the given confirmation code.
113
+ sms_confirm_response :: Return a response after successfully confirming SMS code during SMS setup. By default, redirects to +sms_confirm_redirect+.
112
114
  sms_confirm_view :: The HTML to use for the form to authenticate via SMS code.
113
115
  sms_confirmation_match?(code) :: Whether there is an active SMS confirmation code for the current account and the given code matches it.
114
116
  sms_current_auth? :: Whether there is a active SMS authentication code for the current account.
115
117
  sms_disable :: Action to take to disable SMS authentication for the account.
118
+ sms_disable_response :: Return a response after successfully disabling SMS. By default, redirects to +sms_disable_redirect+.
116
119
  sms_disable_view :: The HTML to use for the form to disable SMS authentication.
117
120
  sms_failures :: The number of SMS authentication failures since the last successfully SMS authentication for this account.
118
121
  sms_locked_out? :: Whether SMS authentication has been locked out for the current account.
119
122
  sms_needs_confirmation? :: Whether SMS authentication has been setup but not confirmed for the current account.
123
+ sms_needs_confirmation_response :: Return a response after successfully providing SMS number during SMS setup. By default, redirects to +sms_needs_confirmation_redirect+.
120
124
  sms_new_auth_code :: A new SMS authentication code that can be used for the account.
121
125
  sms_new_confirm_code :: A new SMS confirmation code that can be used for the account.
122
126
  sms_normalize_phone(phone) :: A normalized version of the given phone number, by default removing everything except 0-9.
123
127
  sms_record_failure :: Record an SMS authentication failure for the current account.
124
128
  sms_remove_failures :: Reset the SMS authentication failure counter for the current account, used after a successful multifactor authentication.
129
+ sms_request_response :: Return a response after a successful SMS request during SMS authentication. By default, redirects to +sms_auth_redirect+.
125
130
  sms_request_view :: The HTML to use for the form to request an SMS authentication code.
126
131
  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
132
  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.
@@ -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.
@@ -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 => compute_hmac(session_id))
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(compute_hmac(session_id))
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 => compute_hmac(prev_key)).
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] = Base64.decode64(salt.split('$').last)
59
- argon2_params[:secret] = argon2_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: 3, p_cost: 1 }
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: 3 }
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
- s = [compute_raw_hmac(data)].pack('m').chomp!("=\n")
246
- s.tr!('+/', '-_')
247
- s
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
- OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, hmac_secret, data)
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
@@ -14,6 +14,7 @@ module Rodauth
14
14
  additional_form_tags
15
15
  button 'Change Login'
16
16
  redirect
17
+ response
17
18
 
18
19
  auth_value_methods :change_login_requires_password?
19
20
 
@@ -51,9 +52,8 @@ module Rodauth
51
52
  end
52
53
 
53
54
  after_change_login
54
- set_notice_flash change_login_notice_flash
55
- redirect change_login_redirect
56
55
  end
56
+ change_login_response
57
57
  end
58
58
 
59
59
  set_error_flash change_login_error_flash
@@ -13,6 +13,7 @@ module Rodauth
13
13
  additional_form_tags
14
14
  button 'Change Password'
15
15
  redirect
16
+ response
16
17
 
17
18
  translatable_method :new_password_label, 'New Password'
18
19
  auth_value_method :new_password_param, 'new-password'
@@ -56,8 +57,7 @@ module Rodauth
56
57
  set_password(password)
57
58
  after_change_password
58
59
  end
59
- set_notice_flash change_password_notice_flash
60
- redirect change_password_redirect
60
+ change_password_response
61
61
  end
62
62
 
63
63
  set_error_flash change_password_error_flash
@@ -11,6 +11,7 @@ module Rodauth
11
11
  after
12
12
  before
13
13
  redirect
14
+ response
14
15
 
15
16
  auth_value_method :account_closed_status_value, 3
16
17
 
@@ -50,8 +51,7 @@ module Rodauth
50
51
  end
51
52
  clear_session
52
53
 
53
- set_notice_flash close_account_notice_flash
54
- redirect close_account_redirect
54
+ close_account_response
55
55
  end
56
56
 
57
57
  set_error_flash close_account_error_flash