rodauth 1.19.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +72 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +100 -7
  5. data/doc/base.rdoc +25 -0
  6. data/doc/email_auth.rdoc +1 -1
  7. data/doc/email_base.rdoc +5 -1
  8. data/doc/internals.rdoc +2 -2
  9. data/doc/jwt_refresh.rdoc +35 -0
  10. data/doc/lockout.rdoc +3 -0
  11. data/doc/login_password_requirements_base.rdoc +4 -1
  12. data/doc/otp.rdoc +22 -39
  13. data/doc/recovery_codes.rdoc +15 -28
  14. data/doc/release_notes/1.20.0.txt +175 -0
  15. data/doc/remember.rdoc +3 -0
  16. data/doc/reset_password.rdoc +2 -1
  17. data/doc/single_session.rdoc +3 -0
  18. data/doc/verify_account.rdoc +4 -3
  19. data/doc/verify_login_change.rdoc +1 -1
  20. data/lib/rodauth.rb +33 -4
  21. data/lib/rodauth/features/base.rb +93 -10
  22. data/lib/rodauth/features/change_login.rb +1 -1
  23. data/lib/rodauth/features/confirm_password.rb +1 -1
  24. data/lib/rodauth/features/create_account.rb +2 -2
  25. data/lib/rodauth/features/disallow_password_reuse.rb +5 -3
  26. data/lib/rodauth/features/email_auth.rb +4 -2
  27. data/lib/rodauth/features/email_base.rb +12 -6
  28. data/lib/rodauth/features/jwt.rb +9 -0
  29. data/lib/rodauth/features/jwt_refresh.rb +142 -0
  30. data/lib/rodauth/features/lockout.rb +8 -4
  31. data/lib/rodauth/features/login_password_requirements_base.rb +1 -0
  32. data/lib/rodauth/features/otp.rb +63 -6
  33. data/lib/rodauth/features/recovery_codes.rb +1 -0
  34. data/lib/rodauth/features/remember.rb +20 -2
  35. data/lib/rodauth/features/reset_password.rb +5 -2
  36. data/lib/rodauth/features/single_session.rb +15 -2
  37. data/lib/rodauth/features/verify_account.rb +11 -6
  38. data/lib/rodauth/features/verify_login_change.rb +5 -3
  39. data/lib/rodauth/version.rb +2 -2
  40. data/spec/disallow_password_reuse_spec.rb +115 -28
  41. data/spec/email_auth_spec.rb +2 -2
  42. data/spec/jwt_refresh_spec.rb +256 -0
  43. data/spec/lockout_spec.rb +4 -4
  44. data/spec/login_spec.rb +52 -11
  45. data/spec/migrate/001_tables.rb +10 -0
  46. data/spec/migrate_travis/001_tables.rb +8 -0
  47. data/spec/remember_spec.rb +27 -0
  48. data/spec/reset_password_spec.rb +2 -2
  49. data/spec/rodauth_spec.rb +25 -1
  50. data/spec/single_session_spec.rb +20 -0
  51. data/spec/spec_helper.rb +29 -0
  52. data/spec/two_factor_spec.rb +57 -3
  53. data/spec/verify_account_spec.rb +18 -1
  54. data/spec/verify_login_change_spec.rb +2 -2
  55. data/templates/add-recovery-codes.str +1 -1
  56. data/templates/change-password.str +2 -2
  57. data/templates/login-confirm-field.str +2 -2
  58. data/templates/login-field.str +2 -2
  59. data/templates/otp-auth-code-field.str +2 -2
  60. data/templates/otp-setup.str +4 -3
  61. data/templates/password-confirm-field.str +2 -2
  62. data/templates/password-field.str +2 -2
  63. data/templates/recovery-auth.str +2 -2
  64. data/templates/reset-password-request.str +1 -1
  65. data/templates/sms-code-field.str +2 -2
  66. data/templates/sms-setup.str +2 -2
  67. data/templates/unlock-account-request.str +1 -1
  68. data/templates/unlock-account.str +1 -1
  69. data/templates/verify-account-resend.str +1 -1
  70. metadata +15 -5
@@ -13,38 +13,27 @@ of them being required due to a missing / lost device.
13
13
 
14
14
  add_recovery_codes_button :: Text to use for button on form to add recovery codes.
15
15
  add_recovery_codes_error_flash :: The flash error to show when adding recovery codes.
16
+ add_recovery_codes_heading :: Text to use for heading above form to add recovery codes.
16
17
  add_recovery_codes_param :: The parameter name to use for adding recovery codes.
17
- add_recovery_auth_redirect :: Where to redirect to add recovery codes if recovery codes
18
- are the primary 2nd factor and have not been setup yet.
19
- invalid_recovery_code_error_flash :: The flash error to show when an invalid recovery
20
- code is used.
21
- invalid_recovery_code_message :: The error message to show when an invalid recovery code
22
- is used.
23
- recovery_auth_additional_form_tags :: HTML fragment containing additional form tags when
24
- authenticating via a recovery code.
18
+ add_recovery_auth_redirect :: Where to redirect to add recovery codes if recovery codes are the primary 2nd factor and have not been setup yet.
19
+ invalid_recovery_code_error_flash :: The flash error to show when an invalid recovery code is used.
20
+ invalid_recovery_code_message :: The error message to show when an invalid recovery code is used.
21
+ recovery_auth_additional_form_tags :: HTML fragment containing additional form tags when authenticating via a recovery code.
25
22
  recovery_auth_button :: The text to use for the button when authenticating via a recovery code.
26
23
  recovery_auth_redirect :: Where to redirect after authenticating via an recovery code.
27
- recovery_auth_route :: The route to the recovery code authentication action.
28
- Defaults to +recovery-auth+.
29
- recovery_codes_added_notice_flash :: The flash notice to show when recovery codes
30
- were added.
31
- recovery_codes_additional_form_tags :: HTML fragment containing additional form tags when
32
- adding recovery codes.
33
- recovery_codes_column :: The column in the recovery_codes_table containing the recovery
34
- code.
35
- recovery_codes_id_column :: The column in the recovery_codes_table containing the
36
- account id.
24
+ recovery_auth_route :: The route to the recovery code authentication action. Defaults to +recovery-auth+.
25
+ recovery_codes_added_notice_flash :: The flash notice to show when recovery codes were added.
26
+ recovery_codes_additional_form_tags :: HTML fragment containing additional form tags when adding recovery codes.
27
+ recovery_codes_column :: The column in the recovery_codes_table containing the recovery code.
28
+ recovery_codes_id_column :: The column in the recovery_codes_table containing the account id.
37
29
  recovery_codes_label :: The label for recovery codes.
38
30
  recovery_codes_limit :: The number of recovery codes to allow.
39
31
  recovery_codes_param :: The parameter name for the recovery code.
40
- recovery_codes_primary? :: Whether recovery codes are the primary second factor, true by
41
- default if neither the otp or sms_codes features are enabled.
42
- recovery_codes_route :: The route to the view recovery codes action. Defaults to
43
- +recovery-codes+.
32
+ recovery_codes_primary? :: Whether recovery codes are the primary second factor, true by default if neither the otp or sms_codes features are enabled.
33
+ recovery_codes_route :: The route to the view recovery codes action. Defaults to +recovery-codes+.
44
34
  recovery_codes_table :: The table storing the recovery codes.
45
35
  view_recovery_codes_button :: Text for the button to view recovery codes.
46
- view_recovery_codes_error_flash :: The flash error to show when viewing recovery codes
47
- was not successful.
36
+ view_recovery_codes_error_flash :: The flash error to show when viewing recovery codes was not successful.
48
37
 
49
38
  == Auth Methods
50
39
 
@@ -59,8 +48,6 @@ before_view_recovery_codes :: Run arbitrary code before viewing recovery codes.
59
48
  can_add_recovery_codes? :: Whether the current account can add more recovery codes.
60
49
  new_recovery_code :: A new recovery code to insert into the recovery codes table.
61
50
  recovery_auth_view :: The HTML to use for the form to authenticate via a recovery code.
62
- recovery_code_match?(code) :: Whether the given code matches any of the existing
63
- recovery_codes.
64
- recovery_codes :: An array containing all valid recovery codes for the current
65
- account.
51
+ recovery_code_match?(code) :: Whether the given code matches any of the existing recovery_codes.
52
+ recovery_codes :: An array containing all valid recovery codes for the current account.
66
53
  recovery_codes_view :: The HTML to use for the form to view recovery codes.
@@ -0,0 +1,175 @@
1
+ = New Features
2
+
3
+ * An hmac_secret configuration method has been added. If set,
4
+ Rodauth will use HMACs for all of the tokens that Rodauth creates.
5
+ By using HMACs for the tokens, even if the database storing the
6
+ tokens is compromised (e.g. via an SQL injection vulnerability), the
7
+ tokens stored in the database will not be usable without knowledge
8
+ of the HMAC secret.
9
+
10
+ The following features are affected by setting the hmac_secret
11
+ configuration method:
12
+
13
+ * email_auth
14
+ * lockout
15
+ * otp
16
+ * remember
17
+ * reset_password
18
+ * single_session
19
+ * verify_account
20
+ * verify_login_change
21
+
22
+ To allow for graceful transition when adding hmac_secret to an
23
+ existing Rodauth installation, you can use the
24
+ allow_raw_email_token? configuration method to keep allowing
25
+ raw tokens. However, you should remove the allow_raw_email_token?
26
+ setting after the existing tokens have expired (most tokens expire
27
+ after 1 day by default). Verify account tokens do not expire,
28
+ but users can request a new verify account token if their token has
29
+ expired.
30
+
31
+ For remember tokens, the raw_remember_token_deadline configuration
32
+ method can be used, which will only allow the use of raw remember
33
+ tokens before the given deadline, which should be the time in the
34
+ future when you want to no longer accept raw remember tokens. You
35
+ can remove this configuration method after the deadline has passed.
36
+ By default, the deadline should be set to 14 days after the time
37
+ you enable hmac_secret, since remember tokens expire in 14 days by
38
+ default.
39
+
40
+ Similarly, in the single_session feature, you can use the
41
+ allow_raw_single_session_key? configuration method to allow raw
42
+ single session keys.
43
+
44
+ In the otp feature, you cannot mix HMAC and non-HMAC tokens. If
45
+ the hmac_secret setting is enabled and there are any existing
46
+ otp tokens already setup, they will stop working. If you are
47
+ already using the otp feature and would like to use the hmac_secret
48
+ configuration method, you need to set the otp_keys_use_hmac?
49
+ configuration method to false unless you want to invalidate all
50
+ existing otp tokens.
51
+
52
+ The hmac_secret configuration is also used during OTP setup
53
+ in the otp feature, to ensure that the OTP secrets for two factor
54
+ authentication came from the server and were not modified by the
55
+ user. If hmac_secret is used, setting up OTP via JSON requires
56
+ sending a POST request to the otp-setup route. This request will
57
+ fail, but included in the response will be the OTP secret and raw
58
+ OTP secret to use. Submitting a POST request including the OTP
59
+ secret and raw OTP secret will allow OTP setup to complete.
60
+
61
+ * A jwt_refresh feature has been added. This uses the jwt feature,
62
+ and issuing short-lived JWTs with exp, iat, and nbf claims, with a
63
+ database-backed refresh token for issuing another short-lived JWT.
64
+ The refresh tokens will automatically use HMACs if the hmac_secret
65
+ configuration method is set.
66
+
67
+ * Rodauth's handling of form errors is now accessible by default.
68
+ aria-invalid attributes are now used on all input fields with
69
+ errors, and aria-describedby attributes are used to tie the input
70
+ fields to the error messages.
71
+
72
+ * All hard coded strings are now overridable via configuration
73
+ methods, with the following configuration methods added:
74
+
75
+ * lockout feature
76
+ * unlock_account_explanatory_text
77
+ * unlock_account_request_explanatory_text
78
+ * login_password_requirements_base feature
79
+ * already_an_account_with_this_login_message
80
+ * otp feature
81
+ * otp_provisioning_uri_label
82
+ * otp_secret_label
83
+ * recovery_codes feature
84
+ * add_recovery_codes_heading
85
+ * reset_password feature
86
+ * reset_password_explanatory_text
87
+ * verify_account feature
88
+ * verify_account_resend_explanatory_text
89
+
90
+ * The following configuration methods have been added to the base
91
+ feature, related to customization of input fields in Rodauth forms:
92
+
93
+ default_field_attributes :: The default attributes to use for input
94
+ field tags, if field_attributes does not
95
+ handle the field.
96
+ field_attributes(field) :: The attributes to use for input fields
97
+ with the given parameter name.
98
+ field_error_attributes(field) :: The attributes to use for input
99
+ fields with the given parameter
100
+ name if the field has an error.
101
+ formatted_field_error(field, error) :: HTML to use for the given
102
+ parameter name and error
103
+ text. Uses a span by
104
+ default.
105
+ input_field_error_class :: The CSS class to add for input fields
106
+ with errors.
107
+ input_field_error_message_class :: The CSS class to add for error
108
+ message spans.
109
+ input_field_label_suffix :: Adds suffix to all input field labels
110
+ login_input_type :: The input type to use for login fields.
111
+ Defaults to text, but can be set to email,
112
+ though that is currently a bad idea if you
113
+ want the login fields to have accessible error
114
+ handling.
115
+ mark_input_fields_as_required? :: Whether to mark all input fields
116
+ as required by default. Note that
117
+ this is currently a bad idea if
118
+ you want the fields to have
119
+ accessible error handling.
120
+
121
+ = Other Improvements
122
+
123
+ * rotp 5 is now supported in the otp feature. Previous rotp versions
124
+ down to rotp 2.1.1 remain supported.
125
+
126
+ * Performance of Rodauth routes has been improved by using defined
127
+ methods instead of instance_exec for route dispatching. Internal
128
+ unnecessary uses of instance_exec have also been removed for
129
+ performance reasons.
130
+
131
+ * When the disallow_password_reuse feature is used without the
132
+ verify_account feature, and account_password_hash_column
133
+ configuration is not used, Rodauth no longer tries to call a method
134
+ that does not exist.
135
+
136
+ * When using the disallow_password_reuse and verify_account features,
137
+ with verify_account_set_password? set to true, Rodauth skips adding
138
+ an empty password to the list of previous passwords.
139
+
140
+ * Rodauth now avoids an unnecessary DELETE query in the
141
+ disallow_password_reuse feature if there are no previous passwords.
142
+
143
+ * The otp-auth-code field now has an autocomplete=off attribute.
144
+
145
+ * On Ruby 1.8, new tokens now use URL safe base64 encoding, instead
146
+ of hex encoding. Rodauth has always used URL safe base64 encoding
147
+ for new tokens on Ruby 1.9+.
148
+
149
+ = Backwards Compatibility
150
+
151
+ * The following configuration methods have been renamed:
152
+
153
+ * email_auth feature
154
+ * no_matching_email_auth_key_message =>
155
+ no_matching_email_auth_key_error_flash
156
+ * lockout feature
157
+ * no_matching_unlock_account_key_message =>
158
+ no_matching_unlock_account_key_error_flash
159
+ * reset_password feature
160
+ * no_matching_reset_password_key_message =>
161
+ no_matching_reset_password_key_error_flash
162
+ * verify_account feature
163
+ * attempt_to_create_unverified_account_notice_message =>
164
+ attempt_to_create_unverified_account_error_flash
165
+ * attempt_to_login_to_unverified_account_notice_message =>
166
+ attempt_to_login_to_unverified_account_error_flash
167
+ * no_matching_verify_account_key_message =>
168
+ no_matching_verify_account_key_error_flash
169
+ * verify_login_change feature
170
+ * no_matching_verify_login_change_key_message =>
171
+ no_matching_verify_login_change_key_error_flash
172
+
173
+ Attempts to use the old method at configuration time, or calling
174
+ the method on the rodauth object at runtime, will result in a
175
+ deprecation warning.
data/doc/remember.rdoc CHANGED
@@ -23,6 +23,9 @@ remembering on login, you can do that via:
23
23
 
24
24
  extend_remember_deadline? :: Whether to extend the remember token deadline
25
25
  when the user is autologged in via token.
26
+ raw_remember_token_deadline :: A deadline before which to allow a raw remember
27
+ token to be used. Allows for graceful transition
28
+ for when +hmac_secret+ is first set.
26
29
  remember_additional_form_tags :: HTML fragment containing additional
27
30
  form tags to use on the change remember
28
31
  setting form.
@@ -8,7 +8,7 @@ the login feature.
8
8
 
9
9
  == Auth Value Methods
10
10
 
11
- no_matching_reset_password_key_message :: The flash error message to show if attempting to access the reset password form with an invalid key.
11
+ no_matching_reset_password_key_error_flash :: The flash error message to show if attempting to access the reset password form with an invalid key.
12
12
  reset_password_additional_form_tags :: HTML fragment containing additional form tags to use on the reset password form.
13
13
  reset_password_autologin? :: Whether to autologin the user after successfully resetting a password.
14
14
  reset_password_button :: The text to use for the reset password button.
@@ -21,6 +21,7 @@ reset_password_email_sent_redirect :: Where to redirect after sending a reset pa
21
21
  reset_password_email_subject :: The subject to use for reset password emails.
22
22
  reset_password_error_flash :: The flash error to show after resetting a password.
23
23
  reset_password_email_last_sent_column :: The email last sent column in the reset password keys table. nil by default, so a reset password email is always sent when requested by default.
24
+ reset_password_explanatory_text :: The text to display above the button to request a password reset.
24
25
  reset_password_id_column :: The id column in the reset password keys table, should be a foreign key referencing the accounts table.
25
26
  reset_password_key_column :: The reset password key/token column in the reset password keys table.
26
27
  reset_password_key_param :: The parameter name to use for the reset password key.
@@ -18,6 +18,9 @@ the previous session after logout no longer work.
18
18
 
19
19
  == Auth Value Methods
20
20
 
21
+ allow_raw_single_session_key? :: Whether to allow a raw single session key to
22
+ be accepted, should only be enabled for graceful
23
+ transition when +hmac_secret+ is first set.
21
24
  single_session_id_column :: The column in the +single_session_table+ containing
22
25
  the account id.
23
26
  single_session_key_column :: The column in the +single_session_table+ containing
@@ -7,9 +7,9 @@ after verifying the account. Depends on the login and create account features.
7
7
 
8
8
  == Auth Value Methods
9
9
 
10
- attempt_to_create_unverified_account_notice_message :: Message displayed when attempting to create an account awaiting verification.
11
- attempt_to_login_to_unverified_account_notice_message :: Message displayed when attempting to login to an account awaiting verification.
12
- no_matching_verify_account_key_message :: The flash error message to show when an invalid verify account key is used.
10
+ attempt_to_create_unverified_account_error_flash :: The flash error message to show when attempting to create an account awaiting verification.
11
+ attempt_to_login_to_unverified_account_error_flash :: The flash error message to show when attempting to login to an account awaiting verification.
12
+ no_matching_verify_account_key_error_flash :: The flash error message to show when an invalid verify account key is used.
13
13
  verify_account_additional_form_tags :: HTML fragment containing additional form tags to use on the verify account form.
14
14
  verify_account_autologin? :: Whether to autologin the user after successful account verification, true by default.
15
15
  verify_account_button :: The text to use for the verify account button.
@@ -28,6 +28,7 @@ verify_account_resend_additional_form_tags :: HTML fragment containing additiona
28
28
  verify_account_resend_button :: The text to use for the verify account resend button.
29
29
  verify_account_redirect :: Where to redirect after verifying the account.
30
30
  verify_account_resend_error_flash :: The flash error to show if unable to resend a verify account email.
31
+ verify_account_resend_explanatory_text :: The text to display above the button to resend the verify account email.
31
32
  verify_account_resend_link :: The HTML to use for a link to the page to request the account verification email be resent.
32
33
  verify_account_resend_route :: The route to the verify account resend action. Defaults to +verify-account-resend+.
33
34
  verify_account_route :: The route to the verify account action. Defaults to +verify-account+.
@@ -13,7 +13,7 @@ control. Depends on the change login and email base features.
13
13
 
14
14
  == Auth Value Methods
15
15
 
16
- no_matching_verify_login_change_key_message :: The flash error message to show when an invalid verify login change key is used.
16
+ no_matching_verify_login_change_key_error_flash :: The flash error message to show when an invalid verify login change key is used.
17
17
  verify_login_change_additional_form_tags :: HTML fragment containing additional form tags to use on the verify login change form.
18
18
  verify_login_change_autologin? :: Whether to autologin the user after successful login change verification, false by default.
19
19
  verify_login_change_button :: The text to use for the verify login change button.
data/lib/rodauth.rb CHANGED
@@ -18,11 +18,11 @@ module Rodauth
18
18
  when false
19
19
  # nothing
20
20
  when :route_csrf
21
- # :nocov:
22
21
  app.plugin :route_csrf
23
- # :nocov:
24
22
  else
23
+ # :nocov:
25
24
  app.plugin :csrf
25
+ # :nocov:
26
26
  end
27
27
 
28
28
  app.plugin :flash unless opts[:flash] == false
@@ -103,14 +103,23 @@ module Rodauth
103
103
  def route(name=feature_name, default=name.to_s.tr('_', '-'), &block)
104
104
  auth_value_method "#{name}_route", default
105
105
 
106
- handle_meth = "handle_#{name}"
106
+ handle_meth = :"handle_#{name}"
107
+ internal_handle_meth = :"_#{handle_meth}"
107
108
  route_meth = :"#{name}_route"
108
109
  before route_meth
109
110
 
111
+ unless block.arity == 1
112
+ # :nocov:
113
+ b = block
114
+ block = lambda{|r| instance_exec(r, &b)}
115
+ # :nocov:
116
+ end
117
+ define_method(internal_handle_meth, &block)
118
+
110
119
  define_method(handle_meth) do
111
120
  request.is send(route_meth) do
112
121
  before_rodauth
113
- instance_exec(request, &block)
122
+ send(internal_handle_meth, request)
114
123
  end
115
124
  end
116
125
 
@@ -138,6 +147,26 @@ module Rodauth
138
147
  configuration.module_eval(&block)
139
148
  end
140
149
 
150
+ if RUBY_VERSION >= '2.5'
151
+ DEPRECATED_ARGS = [{:uplevel=>1}]
152
+ else
153
+ # :nocov:
154
+ DEPRECATED_ARGS = []
155
+ # :nocov:
156
+ end
157
+ def def_deprecated_alias(new, old)
158
+ configuration_module_eval do
159
+ define_method(old) do |*a, &block|
160
+ warn("Deprecated #{old} method used during configuration, switch to using #{new}", *DEPRECATED_ARGS)
161
+ send(new, *a, &block)
162
+ end
163
+ end
164
+ define_method(old) do
165
+ warn("Deprecated #{old} method called at runtime, switch to using #{new}", *DEPRECATED_ARGS)
166
+ send(new)
167
+ end
168
+ end
169
+
141
170
  DEFAULT_REDIRECT_BLOCK = proc{default_redirect}
142
171
  def redirect(name=feature_name, &block)
143
172
  meth = :"#{name}_redirect"
@@ -21,6 +21,10 @@ module Rodauth
21
21
  auth_value_method :default_redirect, '/'
22
22
  session_key :flash_error_key, :error
23
23
  session_key :flash_notice_key, :notice
24
+ auth_value_method :hmac_secret, nil
25
+ auth_value_method :input_field_label_suffix, ''
26
+ auth_value_method :input_field_error_class, 'error'
27
+ auth_value_method :input_field_error_message_class, 'error_message'
24
28
  auth_value_method :invalid_field_error_status, 422
25
29
  auth_value_method :invalid_key_error_status, 401
26
30
  auth_value_method :invalid_password_error_status, 401
@@ -35,15 +39,18 @@ module Rodauth
35
39
  auth_value_method :no_matching_login_message, "no matching login"
36
40
  auth_value_method :login_param, 'login'
37
41
  auth_value_method :login_label, 'Login'
42
+ auth_value_method :login_input_type, 'text'
38
43
  auth_value_method :password_label, 'Password'
39
44
  auth_value_method :password_param, 'password'
40
45
  auth_value_method :modifications_require_password?, true
41
46
  session_key :session_key, :account_id
42
47
  auth_value_method :prefix, ''
43
48
  auth_value_method :require_bcrypt?, true
49
+ auth_value_method :mark_input_fields_as_required?, false
44
50
  auth_value_method :skip_status_checks?, true
45
51
  auth_value_method :template_opts, {}
46
52
  auth_value_method :title_instance_variable, nil
53
+ auth_value_method :token_separator, "_"
47
54
  auth_value_method :unmatched_field_error_status, 422
48
55
  auth_value_method :unopen_account_error_status, 403
49
56
  auth_value_method :unverified_account_message, "unverified account, please verify account before logging in"
@@ -52,6 +59,7 @@ module Rodauth
52
59
 
53
60
  auth_value_methods(
54
61
  :db,
62
+ :default_field_attributes,
55
63
  :set_deadline_values?,
56
64
  :use_date_arithmetic?,
57
65
  :use_database_authentication_functions?,
@@ -84,7 +92,10 @@ module Rodauth
84
92
 
85
93
  auth_private_methods(
86
94
  :account_from_login,
87
- :account_from_session
95
+ :account_from_session,
96
+ :field_attributes,
97
+ :field_error_attributes,
98
+ :formatted_field_error
88
99
  )
89
100
 
90
101
  configuration_module_eval do
@@ -144,6 +155,51 @@ module Rodauth
144
155
  @field_errors[field]
145
156
  end
146
157
 
158
+ def add_field_error_class(field)
159
+ if field_error(field)
160
+ " #{input_field_error_class}"
161
+ end
162
+ end
163
+
164
+ def input_field_string(param, id, opts={})
165
+ type = opts.fetch(:type, "text")
166
+
167
+ unless type == "password"
168
+ value = opts.fetch(:value){scope.h param(param)}
169
+ end
170
+
171
+ "<input #{opts[:attr]} #{field_attributes(param)} #{field_error_attributes(param)} type=\"#{type}\" class=\"form-control#{add_field_error_class(param)}\" name=\"#{param}\" id=\"#{id}\" value=\"#{value}\"/> #{formatted_field_error(param)}"
172
+ end
173
+
174
+ def default_field_attributes
175
+ if mark_input_fields_as_required?
176
+ "required=\"required\""
177
+ end
178
+ end
179
+
180
+ def field_attributes(field)
181
+ _field_attributes(field) || default_field_attributes
182
+ end
183
+
184
+ def field_error_attributes(field)
185
+ if field_error(field)
186
+ _field_error_attributes(field)
187
+ end
188
+ end
189
+
190
+ def formatted_field_error(field)
191
+ if error = field_error(field)
192
+ _formatted_field_error(field, error)
193
+ end
194
+ end
195
+
196
+ # Return urlsafe base64 HMAC for data, assumes hmac_secret is set.
197
+ def compute_hmac(data)
198
+ s = [compute_raw_hmac(data)].pack('m').chomp!("=\n")
199
+ s.tr!('+/', '-_')
200
+ s
201
+ end
202
+
147
203
  def account_id
148
204
  account[account_id_column]
149
205
  end
@@ -241,11 +297,11 @@ module Rodauth
241
297
  return unless scope.respond_to?(:csrf_tag)
242
298
 
243
299
  if use_request_specific_csrf_tokens?
244
- # :nocov:
245
300
  scope.csrf_tag(path)
246
- # :nocov:
247
301
  else
302
+ # :nocov:
248
303
  scope.csrf_tag
304
+ # :nocov:
249
305
  end
250
306
  end
251
307
 
@@ -315,6 +371,18 @@ module Rodauth
315
371
 
316
372
  private
317
373
 
374
+ def convert_token_key(key)
375
+ if key && hmac_secret
376
+ compute_hmac(key)
377
+ else
378
+ key
379
+ end
380
+ end
381
+
382
+ def split_token(token)
383
+ token.split(token_separator, 2)
384
+ end
385
+
318
386
  def redirect(path)
319
387
  request.redirect(path)
320
388
  end
@@ -330,7 +398,9 @@ module Rodauth
330
398
  else
331
399
  # :nocov:
332
400
  def random_key
333
- SecureRandom.hex(32)
401
+ s = [SecureRandom.random_bytes(32)].pack('m').chomp!("=\n")
402
+ s.tr!('+/', '-_')
403
+ s
334
404
  end
335
405
  # :nocov:
336
406
  end
@@ -438,6 +508,22 @@ module Rodauth
438
508
  ds.first
439
509
  end
440
510
 
511
+ def compute_raw_hmac(data)
512
+ OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, hmac_secret, data)
513
+ end
514
+
515
+ def _field_attributes(field)
516
+ nil
517
+ end
518
+
519
+ def _field_error_attributes(field)
520
+ " aria-invalid=\"true\" aria-describedby=\"#{field}_error_message\" "
521
+ end
522
+
523
+ def _formatted_field_error(field, error)
524
+ "<span class=\"#{input_field_error_message_class}\" id=\"#{field}_error_message\">#{error}</span>"
525
+ end
526
+
441
527
  def account_session_status_filter
442
528
  {account_status_column=>account_open_status_value}
443
529
  end
@@ -536,18 +622,15 @@ module Rodauth
536
622
  end
537
623
 
538
624
  def _view_opts(page)
539
- auth_template_path = template_path(page)
540
625
  opts = template_opts.dup
541
626
  opts[:locals] = opts[:locals] ? opts[:locals].dup : {}
542
627
  opts[:locals][:rodauth] = self
543
628
  opts[:cache] = cache_templates
544
629
  opts[:cache_key] = :"rodauth_#{page}"
545
630
 
546
- scope.instance_exec do
547
- opts = find_template(parse_template_opts(page, opts))
548
- unless File.file?(template_path(opts))
549
- opts[:path] = auth_template_path
550
- end
631
+ opts = scope.send(:find_template, scope.send(:parse_template_opts, page, opts))
632
+ unless File.file?(scope.send(:template_path, opts))
633
+ opts[:path] = template_path(page)
551
634
  end
552
635
 
553
636
  opts