rodauth 1.23.0 → 2.4.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 (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +184 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +221 -79
  5. data/doc/account_expiration.rdoc +12 -26
  6. data/doc/active_sessions.rdoc +49 -0
  7. data/doc/audit_logging.rdoc +44 -0
  8. data/doc/base.rdoc +76 -128
  9. data/doc/change_login.rdoc +7 -14
  10. data/doc/change_password.rdoc +9 -13
  11. data/doc/change_password_notify.rdoc +2 -2
  12. data/doc/close_account.rdoc +9 -16
  13. data/doc/confirm_password.rdoc +12 -5
  14. data/doc/create_account.rdoc +11 -22
  15. data/doc/disallow_password_reuse.rdoc +6 -13
  16. data/doc/email_auth.rdoc +15 -14
  17. data/doc/email_base.rdoc +5 -15
  18. data/doc/guides/admin_activation.rdoc +46 -0
  19. data/doc/guides/already_authenticated.rdoc +10 -0
  20. data/doc/guides/alternative_login.rdoc +46 -0
  21. data/doc/guides/create_account_programmatically.rdoc +38 -0
  22. data/doc/guides/delay_password.rdoc +25 -0
  23. data/doc/guides/email_only.rdoc +16 -0
  24. data/doc/guides/i18n.rdoc +26 -0
  25. data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
  26. data/doc/guides/links.rdoc +12 -0
  27. data/doc/guides/login_return.rdoc +37 -0
  28. data/doc/guides/password_column.rdoc +25 -0
  29. data/doc/guides/password_confirmation.rdoc +37 -0
  30. data/doc/guides/password_requirements.rdoc +30 -0
  31. data/doc/guides/paths.rdoc +36 -0
  32. data/doc/guides/query_params.rdoc +9 -0
  33. data/doc/guides/redirects.rdoc +17 -0
  34. data/doc/guides/registration_field.rdoc +68 -0
  35. data/doc/guides/require_mfa.rdoc +30 -0
  36. data/doc/guides/reset_password_autologin.rdoc +21 -0
  37. data/doc/guides/status_column.rdoc +28 -0
  38. data/doc/guides/totp_or_recovery.rdoc +16 -0
  39. data/doc/http_basic_auth.rdoc +10 -1
  40. data/doc/jwt.rdoc +22 -22
  41. data/doc/jwt_cors.rdoc +2 -3
  42. data/doc/jwt_refresh.rdoc +23 -8
  43. data/doc/lockout.rdoc +17 -15
  44. data/doc/login.rdoc +17 -2
  45. data/doc/login_password_requirements_base.rdoc +18 -37
  46. data/doc/logout.rdoc +2 -2
  47. data/doc/otp.rdoc +25 -19
  48. data/doc/password_complexity.rdoc +10 -26
  49. data/doc/password_expiration.rdoc +11 -25
  50. data/doc/password_grace_period.rdoc +16 -2
  51. data/doc/password_pepper.rdoc +44 -0
  52. data/doc/recovery_codes.rdoc +18 -12
  53. data/doc/release_notes/2.0.0.txt +361 -0
  54. data/doc/release_notes/2.1.0.txt +31 -0
  55. data/doc/release_notes/2.2.0.txt +39 -0
  56. data/doc/release_notes/2.3.0.txt +37 -0
  57. data/doc/release_notes/2.4.0.txt +22 -0
  58. data/doc/remember.rdoc +40 -64
  59. data/doc/reset_password.rdoc +12 -9
  60. data/doc/session_expiration.rdoc +1 -0
  61. data/doc/single_session.rdoc +16 -25
  62. data/doc/sms_codes.rdoc +24 -14
  63. data/doc/two_factor_base.rdoc +60 -22
  64. data/doc/verify_account.rdoc +14 -12
  65. data/doc/verify_account_grace_period.rdoc +6 -2
  66. data/doc/verify_login_change.rdoc +9 -8
  67. data/doc/webauthn.rdoc +115 -0
  68. data/doc/webauthn_login.rdoc +15 -0
  69. data/doc/webauthn_verify_account.rdoc +9 -0
  70. data/javascript/webauthn_auth.js +45 -0
  71. data/javascript/webauthn_setup.js +35 -0
  72. data/lib/roda/plugins/rodauth.rb +1 -1
  73. data/lib/rodauth.rb +33 -28
  74. data/lib/rodauth/features/account_expiration.rb +5 -5
  75. data/lib/rodauth/features/active_sessions.rb +158 -0
  76. data/lib/rodauth/features/audit_logging.rb +98 -0
  77. data/lib/rodauth/features/base.rb +152 -49
  78. data/lib/rodauth/features/change_password_notify.rb +1 -1
  79. data/lib/rodauth/features/close_account.rb +8 -6
  80. data/lib/rodauth/features/confirm_password.rb +40 -2
  81. data/lib/rodauth/features/create_account.rb +8 -13
  82. data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
  83. data/lib/rodauth/features/disallow_password_reuse.rb +5 -3
  84. data/lib/rodauth/features/email_auth.rb +30 -28
  85. data/lib/rodauth/features/email_base.rb +3 -3
  86. data/lib/rodauth/features/http_basic_auth.rb +55 -35
  87. data/lib/rodauth/features/jwt.rb +63 -16
  88. data/lib/rodauth/features/jwt_cors.rb +15 -15
  89. data/lib/rodauth/features/jwt_refresh.rb +42 -13
  90. data/lib/rodauth/features/lockout.rb +11 -13
  91. data/lib/rodauth/features/login.rb +58 -13
  92. data/lib/rodauth/features/login_password_requirements_base.rb +13 -8
  93. data/lib/rodauth/features/otp.rb +76 -82
  94. data/lib/rodauth/features/password_complexity.rb +8 -13
  95. data/lib/rodauth/features/password_expiration.rb +1 -1
  96. data/lib/rodauth/features/password_grace_period.rb +17 -10
  97. data/lib/rodauth/features/password_pepper.rb +45 -0
  98. data/lib/rodauth/features/recovery_codes.rb +47 -51
  99. data/lib/rodauth/features/remember.rb +13 -27
  100. data/lib/rodauth/features/reset_password.rb +25 -25
  101. data/lib/rodauth/features/session_expiration.rb +7 -10
  102. data/lib/rodauth/features/single_session.rb +8 -6
  103. data/lib/rodauth/features/sms_codes.rb +58 -68
  104. data/lib/rodauth/features/two_factor_base.rb +134 -30
  105. data/lib/rodauth/features/verify_account.rb +28 -20
  106. data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
  107. data/lib/rodauth/features/verify_login_change.rb +11 -10
  108. data/lib/rodauth/features/webauthn.rb +505 -0
  109. data/lib/rodauth/features/webauthn_login.rb +70 -0
  110. data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
  111. data/lib/rodauth/migrations.rb +16 -5
  112. data/lib/rodauth/version.rb +2 -2
  113. data/templates/button.str +1 -3
  114. data/templates/change-login.str +1 -2
  115. data/templates/change-password.str +3 -5
  116. data/templates/close-account.str +2 -2
  117. data/templates/confirm-password.str +1 -1
  118. data/templates/create-account.str +1 -1
  119. data/templates/email-auth-request-form.str +1 -2
  120. data/templates/email-auth.str +1 -1
  121. data/templates/global-logout-field.str +6 -0
  122. data/templates/login-confirm-field.str +2 -4
  123. data/templates/login-display.str +3 -2
  124. data/templates/login-field.str +2 -4
  125. data/templates/login-form-footer.str +6 -0
  126. data/templates/login-form.str +7 -0
  127. data/templates/login.str +1 -9
  128. data/templates/logout.str +1 -1
  129. data/templates/multi-phase-login.str +3 -0
  130. data/templates/otp-auth-code-field.str +5 -3
  131. data/templates/otp-auth.str +1 -1
  132. data/templates/otp-disable.str +1 -1
  133. data/templates/otp-setup.str +3 -3
  134. data/templates/password-confirm-field.str +2 -4
  135. data/templates/password-field.str +2 -4
  136. data/templates/recovery-auth.str +3 -6
  137. data/templates/recovery-codes.str +1 -1
  138. data/templates/remember.str +15 -20
  139. data/templates/reset-password-request.str +2 -2
  140. data/templates/reset-password.str +1 -2
  141. data/templates/sms-auth.str +1 -1
  142. data/templates/sms-code-field.str +5 -3
  143. data/templates/sms-confirm.str +1 -2
  144. data/templates/sms-disable.str +1 -2
  145. data/templates/sms-request.str +1 -1
  146. data/templates/sms-setup.str +6 -4
  147. data/templates/two-factor-auth.str +5 -0
  148. data/templates/two-factor-disable.str +6 -0
  149. data/templates/two-factor-manage.str +16 -0
  150. data/templates/unlock-account-request.str +2 -2
  151. data/templates/unlock-account.str +1 -1
  152. data/templates/verify-account-resend.str +1 -1
  153. data/templates/verify-account.str +1 -2
  154. data/templates/verify-login-change.str +1 -1
  155. data/templates/webauthn-auth.str +11 -0
  156. data/templates/webauthn-remove.str +14 -0
  157. data/templates/webauthn-setup.str +12 -0
  158. metadata +96 -13
  159. data/doc/verify_change_login.rdoc +0 -11
  160. data/lib/rodauth/features/verify_change_login.rb +0 -20
@@ -0,0 +1,45 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:password_pepper, :PasswordPepper) do
5
+ depends :login_password_requirements_base
6
+
7
+ auth_value_method :password_pepper, nil
8
+ auth_value_method :previous_password_peppers, [""]
9
+ auth_value_method :password_pepper_update?, true
10
+
11
+ def password_match?(password)
12
+ if (result = super) && @previous_pepper_matched && password_pepper_update?
13
+ set_password(password)
14
+ end
15
+
16
+ result
17
+ end
18
+
19
+ private
20
+
21
+ def password_hash(password)
22
+ super(password + password_pepper.to_s)
23
+ end
24
+
25
+ def password_hash_match?(hash, password)
26
+ return super if password_pepper.nil?
27
+
28
+ return true if super(hash, password + password_pepper)
29
+
30
+ @previous_pepper_matched = previous_password_peppers.any? do |pepper|
31
+ super(hash, password + pepper)
32
+ end
33
+ end
34
+
35
+ def database_function_password_match?(name, hash_id, password, salt)
36
+ return super if password_pepper.nil?
37
+
38
+ return true if super(name, hash_id, password + password_pepper, salt)
39
+
40
+ @previous_pepper_matched = previous_password_peppers.any? do |pepper|
41
+ super(name, hash_id, password + pepper, salt)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -17,11 +17,11 @@ module Rodauth
17
17
  button 'Authenticate via Recovery Code', 'recovery_auth'
18
18
  button 'View Authentication Recovery Codes', 'view_recovery_codes'
19
19
 
20
- error_flash "Error authenticating via recovery code.", 'invalid_recovery_code'
21
- error_flash "Unable to add recovery codes.", 'add_recovery_codes'
22
- error_flash "Unable to view recovery codes.", 'view_recovery_codes'
20
+ error_flash "Error authenticating via recovery code", 'invalid_recovery_code'
21
+ error_flash "Unable to add recovery codes", 'add_recovery_codes'
22
+ error_flash "Unable to view recovery codes", 'view_recovery_codes'
23
23
 
24
- notice_flash "Additional authentication recovery codes have been added.", 'recovery_codes_added'
24
+ notice_flash "Additional authentication recovery codes have been added", 'recovery_codes_added'
25
25
 
26
26
  redirect(:recovery_auth){recovery_auth_path}
27
27
  redirect(:add_recovery_codes){recovery_codes_path}
@@ -32,15 +32,19 @@ module Rodauth
32
32
  view 'recovery-codes', 'View Authentication Recovery Codes', 'recovery_codes'
33
33
 
34
34
  auth_value_method :add_recovery_codes_param, 'add'
35
- auth_value_method :add_recovery_codes_heading, '<h2>Add Additional Recovery Codes</h2>'
36
- auth_value_method :invalid_recovery_code_message, "Invalid recovery code"
35
+ translatable_method :add_recovery_codes_heading, '<h2>Add Additional Recovery Codes</h2>'
36
+ auth_value_method :auto_add_recovery_codes?, false
37
+ translatable_method :invalid_recovery_code_message, "Invalid recovery code"
37
38
  auth_value_method :recovery_codes_limit, 16
38
39
  auth_value_method :recovery_codes_column, :code
39
40
  auth_value_method :recovery_codes_id_column, :id
40
- auth_value_method :recovery_codes_label, 'Recovery Code'
41
+ translatable_method :recovery_codes_label, 'Recovery Code'
41
42
  auth_value_method :recovery_codes_param, 'recovery-code'
42
43
  auth_value_method :recovery_codes_table, :account_recovery_codes
43
44
 
45
+ translatable_method :recovery_auth_link_text, "Authenticate Using Recovery Code"
46
+ translatable_method :recovery_codes_link_text, "View Authentication Recovery Codes"
47
+
44
48
  auth_cached_method :recovery_codes
45
49
 
46
50
  auth_value_methods(
@@ -59,7 +63,7 @@ module Rodauth
59
63
  require_login
60
64
  require_account_session
61
65
  require_two_factor_setup
62
- require_two_factor_not_authenticated
66
+ require_two_factor_not_authenticated('recovery_code')
63
67
  before_recovery_auth_route
64
68
 
65
69
  r.get do
@@ -69,7 +73,7 @@ module Rodauth
69
73
  r.post do
70
74
  if recovery_code_match?(param(recovery_codes_param))
71
75
  before_recovery_auth
72
- two_factor_authenticate(:recovery_code)
76
+ two_factor_authenticate('recovery_code')
73
77
  end
74
78
 
75
79
  set_response_error_status(invalid_key_error_status)
@@ -124,61 +128,24 @@ module Rodauth
124
128
 
125
129
  attr_accessor :recovery_codes_button
126
130
 
127
- def two_factor_need_setup_redirect
128
- super || (add_recovery_codes_redirect if recovery_codes_primary?)
129
- end
130
-
131
- def two_factor_auth_required_redirect
132
- super || (recovery_auth_redirect if recovery_codes_primary?)
133
- end
134
-
135
- def two_factor_auth_fallback_redirect
136
- recovery_auth_redirect
137
- end
138
-
139
131
  def two_factor_remove
140
132
  super
141
133
  recovery_codes_remove
142
134
  end
143
135
 
144
- def two_factor_authentication_setup?
145
- super || (recovery_codes_primary? && !recovery_codes.empty?)
146
- end
147
-
148
- def otp_auth_form_footer
149
- "#{super if defined?(super)}<p><a href=\"#{recovery_auth_path}\">Authenticate using recovery code</a></p>"
150
- end
151
-
152
- def otp_lockout_redirect
153
- recovery_auth_redirect
154
- end
155
-
156
- def otp_lockout_error_flash
157
- "#{super if defined?(super)} Can use recovery code to unlock."
158
- end
159
-
160
136
  def otp_add_key
161
137
  super if defined?(super)
162
- add_recovery_codes(recovery_codes_limit - recovery_codes.length)
138
+ auto_add_missing_recovery_codes
163
139
  end
164
140
 
165
141
  def sms_confirm
166
142
  super if defined?(super)
167
- add_recovery_codes(recovery_codes_limit - recovery_codes.length)
168
- end
169
-
170
- def otp_remove
171
- super if defined?(super)
172
- unless recovery_codes_primary?
173
- recovery_codes_remove
174
- end
143
+ auto_add_missing_recovery_codes
175
144
  end
176
145
 
177
- def sms_disable
146
+ def add_webauthn_credential(_)
178
147
  super if defined?(super)
179
- unless recovery_codes_primary?
180
- recovery_codes_remove
181
- end
148
+ auto_add_missing_recovery_codes
182
149
  end
183
150
 
184
151
  def recovery_codes_remove
@@ -221,14 +188,43 @@ module Rodauth
221
188
  end
222
189
  end
223
190
 
191
+ def possible_authentication_methods
192
+ methods = super
193
+ methods << 'recovery_code' unless recovery_codes_ds.empty?
194
+ methods
195
+ end
196
+
224
197
  private
225
198
 
199
+ def _two_factor_auth_links
200
+ links = super
201
+ links << [40, recovery_auth_path, recovery_auth_link_text] unless recovery_codes_ds.empty?
202
+ links
203
+ end
204
+
205
+ def _two_factor_setup_links
206
+ links = super
207
+ links << [40, recovery_codes_path, recovery_codes_link_text] if (recovery_codes_primary? || uses_two_factor_authentication?)
208
+ links
209
+ end
210
+
211
+ def _two_factor_remove_all_from_session
212
+ two_factor_remove_session('recovery_code')
213
+ super
214
+ end
215
+
226
216
  def new_recovery_code
227
217
  random_key
228
218
  end
229
219
 
230
220
  def recovery_codes_primary?
231
- (features & [:otp, :sms_codes]).empty?
221
+ (features & [:otp, :sms_codes, :webauthn]).empty?
222
+ end
223
+
224
+ def auto_add_missing_recovery_codes
225
+ if auto_add_recovery_codes?
226
+ add_recovery_codes(recovery_codes_limit - recovery_codes.length)
227
+ end
232
228
  end
233
229
 
234
230
  def _recovery_codes
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  Feature.define(:remember, :Remember) do
5
- depends :confirm_password
6
-
7
5
  notice_flash "Your remember setting has been updated"
8
6
  error_flash "There was an error updating your remember setting"
9
7
  loaded_templates %w'remember'
@@ -17,11 +15,10 @@ module Rodauth
17
15
  redirect
18
16
 
19
17
  auth_value_method :raw_remember_token_deadline, nil
20
- auth_value_method :remember_cookie_options, {}
18
+ auth_value_method :remember_cookie_options, {}.freeze
21
19
  auth_value_method :extend_remember_deadline?, false
22
- auth_value_method :remember_period, {:days=>14}
23
- session_key :remembered_session_key, :remembered
24
- auth_value_method :remember_deadline_interval, {:days=>14}
20
+ auth_value_method :remember_period, {:days=>14}.freeze
21
+ auth_value_method :remember_deadline_interval, {:days=>14}.freeze
25
22
  auth_value_method :remember_id_column, :id
26
23
  auth_value_method :remember_key_column, :key
27
24
  auth_value_method :remember_deadline_column, :deadline
@@ -31,13 +28,12 @@ module Rodauth
31
28
  auth_value_method :remember_remember_param_value, 'remember'
32
29
  auth_value_method :remember_forget_param_value, 'forget'
33
30
  auth_value_method :remember_disable_param_value, 'disable'
34
- auth_value_method :remember_remember_label, 'Remember Me'
35
- auth_value_method :remember_forget_label, 'Forget Me'
36
- auth_value_method :remember_disable_label, 'Disable Remember Me'
31
+ translatable_method :remember_remember_label, 'Remember Me'
32
+ translatable_method :remember_forget_label, 'Forget Me'
33
+ translatable_method :remember_disable_label, 'Disable Remember Me'
37
34
 
38
35
  auth_methods(
39
36
  :add_remember_key,
40
- :clear_remembered_session_key,
41
37
  :disable_remember_login,
42
38
  :forget_login,
43
39
  :generate_remember_key_value,
@@ -62,7 +58,9 @@ module Rodauth
62
58
  if [remember_remember_param_value, remember_forget_param_value, remember_disable_param_value].include?(remember)
63
59
  transaction do
64
60
  before_remember
61
+ # :nocov:
65
62
  case remember
63
+ # :nocov:
66
64
  when remember_remember_param_value
67
65
  remember_login
68
66
  when remember_forget_param_value
@@ -109,9 +107,9 @@ module Rodauth
109
107
  return
110
108
  end
111
109
 
112
- session[session_key] = id
110
+ set_session_value(session_key, id)
113
111
  account = account_from_session
114
- session.delete(session_key)
112
+ remove_session_value(session_key)
115
113
 
116
114
  unless account
117
115
  remove_remember_key(id)
@@ -120,9 +118,8 @@ module Rodauth
120
118
  end
121
119
 
122
120
  before_load_memory
123
- update_session
121
+ login_session('remember')
124
122
 
125
- set_session_value(remembered_session_key, true)
126
123
  if extend_remember_deadline?
127
124
  active_remember_key_ds(id).update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period))
128
125
  remember_login
@@ -133,9 +130,7 @@ module Rodauth
133
130
  def remember_login
134
131
  get_remember_key
135
132
  opts = Hash[remember_cookie_options]
136
- key = remember_key_value
137
- key = compute_hmac(key) if hmac_secret
138
- opts[:value] = "#{account_id}_#{key}"
133
+ opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}"
139
134
  opts[:expires] = convert_timestamp(active_remember_key_ds.get(remember_deadline_column))
140
135
  ::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts)
141
136
  end
@@ -174,12 +169,8 @@ module Rodauth
174
169
  remember_key_ds(id).delete
175
170
  end
176
171
 
177
- def clear_remembered_session_key
178
- session.delete(remembered_session_key)
179
- end
180
-
181
172
  def logged_in_via_remember_key?
182
- !!session[remembered_session_key]
173
+ authenticated_by.include?('remember')
183
174
  end
184
175
 
185
176
  private
@@ -194,11 +185,6 @@ module Rodauth
194
185
  super if defined?(super)
195
186
  end
196
187
 
197
- def after_confirm_password
198
- super
199
- clear_remembered_session_key
200
- end
201
-
202
188
  attr_reader :remember_key_value
203
189
 
204
190
  def generate_remember_key_value
@@ -4,8 +4,6 @@ module Rodauth
4
4
  Feature.define(:reset_password, :ResetPassword) do
5
5
  depends :login, :email_base, :login_password_requirements_base
6
6
 
7
- def_deprecated_alias :no_matching_reset_password_key_error_flash, :no_matching_reset_password_key_message
8
-
9
7
  notice_flash "Your password has been reset"
10
8
  notice_flash "An email has been sent to you with a link to reset the password for your account", 'reset_password_email_sent'
11
9
  error_flash "There was an error resetting your password"
@@ -28,20 +26,19 @@ module Rodauth
28
26
  redirect(:reset_password_email_recently_sent){default_post_email_redirect}
29
27
 
30
28
  auth_value_method :reset_password_deadline_column, :deadline
31
- auth_value_method :reset_password_deadline_interval, {:days=>1}
32
- auth_value_method :reset_password_email_subject, 'Reset Password'
29
+ auth_value_method :reset_password_deadline_interval, {:days=>1}.freeze
30
+ translatable_method :reset_password_email_subject, 'Reset Password'
33
31
  auth_value_method :reset_password_key_param, 'key'
34
32
  auth_value_method :reset_password_autologin?, false
35
33
  auth_value_method :reset_password_table, :account_password_reset_keys
36
34
  auth_value_method :reset_password_id_column, :id
37
35
  auth_value_method :reset_password_key_column, :key
38
- auth_value_method :reset_password_email_last_sent_column, nil
39
- auth_value_method :reset_password_explanatory_text, "<p>If you have forgotten your password, you can request a password reset:</p>"
36
+ auth_value_method :reset_password_email_last_sent_column, :email_last_sent
37
+ translatable_method :reset_password_explanatory_text, "<p>If you have forgotten your password, you can request a password reset:</p>"
40
38
  auth_value_method :reset_password_skip_resend_email_within, 300
39
+ translatable_method :reset_password_request_link_text, "Forgot Password?"
41
40
  session_key :reset_password_session_key, :reset_password_key
42
41
 
43
- auth_value_methods :reset_password_request_link
44
-
45
42
  auth_methods(
46
43
  :create_reset_password_key,
47
44
  :create_reset_password_email,
@@ -69,7 +66,15 @@ module Rodauth
69
66
  end
70
67
 
71
68
  r.post do
72
- if account_from_login(param(login_param)) && open_account?
69
+ catch_error do
70
+ unless account_from_login(param(login_param))
71
+ throw_error_status(no_matching_login_error_status, login_param, no_matching_login_message)
72
+ end
73
+
74
+ unless open_account?
75
+ throw_error_status(unopen_account_error_status, login_param, unverified_account_message)
76
+ end
77
+
73
78
  if reset_password_email_recently_sent?
74
79
  set_redirect_error_flash reset_password_email_recently_sent_error_flash
75
80
  redirect reset_password_email_recently_sent_redirect
@@ -84,12 +89,11 @@ module Rodauth
84
89
  end
85
90
 
86
91
  set_notice_flash reset_password_email_sent_notice_flash
87
- else
88
- set_redirect_error_status(no_matching_login_error_status)
89
- set_redirect_error_flash reset_password_request_error_flash
92
+ redirect reset_password_email_sent_redirect
90
93
  end
91
94
 
92
- redirect reset_password_email_sent_redirect
95
+ set_error_flash reset_password_request_error_flash
96
+ reset_password_request_view
93
97
  end
94
98
  end
95
99
 
@@ -99,7 +103,7 @@ module Rodauth
99
103
 
100
104
  r.get do
101
105
  if key = param_or_nil(reset_password_key_param)
102
- session[reset_password_session_key] = key
106
+ set_session_value(reset_password_session_key, key)
103
107
  redirect(r.path)
104
108
  end
105
109
 
@@ -107,7 +111,7 @@ module Rodauth
107
111
  if account_from_reset_password_key(key)
108
112
  reset_password_view
109
113
  else
110
- session[reset_password_session_key] = nil
114
+ remove_session_value(reset_password_session_key)
111
115
  set_redirect_error_flash no_matching_reset_password_key_error_flash
112
116
  redirect require_login_redirect
113
117
  end
@@ -144,10 +148,10 @@ module Rodauth
144
148
  end
145
149
 
146
150
  if reset_password_autologin?
147
- update_session
151
+ autologin_session('reset_password')
148
152
  end
149
153
 
150
- session[reset_password_session_key] = nil
154
+ remove_session_value(reset_password_session_key)
151
155
  set_notice_flash reset_password_notice_flash
152
156
  redirect reset_password_redirect
153
157
  end
@@ -192,14 +196,6 @@ module Rodauth
192
196
  ds.get(reset_password_key_column)
193
197
  end
194
198
 
195
- def login_form_footer
196
- super + reset_password_request_link
197
- end
198
-
199
- def reset_password_request_link
200
- "<p><a href=\"#{reset_password_request_path}\">Forgot Password?</a></p>"
201
- end
202
-
203
199
  def set_reset_password_email_last_sent
204
200
  password_reset_ds.update(reset_password_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if reset_password_email_last_sent_column
205
201
  end
@@ -214,6 +210,10 @@ module Rodauth
214
210
 
215
211
  private
216
212
 
213
+ def _login_form_footer_links
214
+ super << [20, reset_password_request_path, reset_password_request_link_text]
215
+ end
216
+
217
217
  def reset_password_email_recently_sent?
218
218
  (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within)
219
219
  end
@@ -2,16 +2,16 @@
2
2
 
3
3
  module Rodauth
4
4
  Feature.define(:session_expiration, :SessionExpiration) do
5
- error_flash "This session has expired, please login again."
5
+ error_flash "This session has expired, please login again"
6
+ redirect{require_login_redirect}
6
7
 
7
8
  auth_value_method :max_session_lifetime, 86400
8
9
  session_key :session_created_session_key, :session_created_at
10
+ auth_value_method :session_expiration_error_status, 401
9
11
  auth_value_method :session_expiration_default, true
10
12
  auth_value_method :session_inactivity_timeout, 1800
11
13
  session_key :session_last_activity_session_key, :last_session_activity_at
12
14
 
13
- auth_value_methods :session_expiration_redirect
14
-
15
15
  def check_session_expiration
16
16
  return unless logged_in?
17
17
 
@@ -37,19 +37,16 @@ module Rodauth
37
37
 
38
38
  def expire_session
39
39
  clear_session
40
+ set_redirect_error_status session_expiration_error_status
40
41
  set_redirect_error_flash session_expiration_error_flash
41
42
  redirect session_expiration_redirect
42
43
  end
43
44
 
44
- def session_expiration_redirect
45
- require_login_redirect
46
- end
47
-
48
- private
49
-
50
45
  def update_session
51
46
  super
52
- session[session_last_activity_session_key] = session[session_created_session_key] = Time.now.to_i
47
+ t = Time.now.to_i
48
+ set_session_value(session_last_activity_session_key, t)
49
+ set_session_value(session_created_session_key, t)
53
50
  end
54
51
  end
55
52
  end