rodauth 1.21.0 → 2.2.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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +182 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +211 -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 +75 -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 +6 -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 +22 -0
  42. data/doc/jwt_refresh.rdoc +18 -8
  43. data/doc/lockout.rdoc +17 -15
  44. data/doc/login.rdoc +10 -2
  45. data/doc/login_password_requirements_base.rdoc +15 -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/recovery_codes.rdoc +18 -12
  52. data/doc/release_notes/1.22.0.txt +11 -0
  53. data/doc/release_notes/1.23.0.txt +32 -0
  54. data/doc/release_notes/2.0.0.txt +361 -0
  55. data/doc/release_notes/2.1.0.txt +31 -0
  56. data/doc/release_notes/2.2.0.txt +39 -0
  57. data/doc/remember.rdoc +40 -64
  58. data/doc/reset_password.rdoc +12 -9
  59. data/doc/session_expiration.rdoc +1 -0
  60. data/doc/single_session.rdoc +16 -25
  61. data/doc/sms_codes.rdoc +24 -14
  62. data/doc/two_factor_base.rdoc +60 -22
  63. data/doc/verify_account.rdoc +14 -12
  64. data/doc/verify_account_grace_period.rdoc +6 -2
  65. data/doc/verify_login_change.rdoc +9 -8
  66. data/doc/webauthn.rdoc +115 -0
  67. data/doc/webauthn_login.rdoc +15 -0
  68. data/doc/webauthn_verify_account.rdoc +9 -0
  69. data/javascript/webauthn_auth.js +45 -0
  70. data/javascript/webauthn_setup.js +35 -0
  71. data/lib/roda/plugins/rodauth.rb +1 -1
  72. data/lib/rodauth.rb +36 -28
  73. data/lib/rodauth/features/account_expiration.rb +5 -5
  74. data/lib/rodauth/features/active_sessions.rb +158 -0
  75. data/lib/rodauth/features/audit_logging.rb +98 -0
  76. data/lib/rodauth/features/base.rb +144 -43
  77. data/lib/rodauth/features/change_password_notify.rb +2 -2
  78. data/lib/rodauth/features/confirm_password.rb +40 -2
  79. data/lib/rodauth/features/create_account.rb +8 -13
  80. data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
  81. data/lib/rodauth/features/disallow_password_reuse.rb +1 -1
  82. data/lib/rodauth/features/email_auth.rb +31 -30
  83. data/lib/rodauth/features/email_base.rb +9 -4
  84. data/lib/rodauth/features/http_basic_auth.rb +55 -35
  85. data/lib/rodauth/features/jwt.rb +63 -16
  86. data/lib/rodauth/features/jwt_cors.rb +53 -0
  87. data/lib/rodauth/features/jwt_refresh.rb +32 -9
  88. data/lib/rodauth/features/lockout.rb +12 -14
  89. data/lib/rodauth/features/login.rb +54 -10
  90. data/lib/rodauth/features/login_password_requirements_base.rb +4 -4
  91. data/lib/rodauth/features/otp.rb +77 -80
  92. data/lib/rodauth/features/password_complexity.rb +8 -13
  93. data/lib/rodauth/features/password_expiration.rb +2 -2
  94. data/lib/rodauth/features/password_grace_period.rb +17 -10
  95. data/lib/rodauth/features/recovery_codes.rb +49 -53
  96. data/lib/rodauth/features/remember.rb +11 -27
  97. data/lib/rodauth/features/reset_password.rb +26 -26
  98. data/lib/rodauth/features/session_expiration.rb +6 -4
  99. data/lib/rodauth/features/single_session.rb +8 -6
  100. data/lib/rodauth/features/sms_codes.rb +62 -72
  101. data/lib/rodauth/features/two_factor_base.rb +134 -30
  102. data/lib/rodauth/features/verify_account.rb +29 -21
  103. data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
  104. data/lib/rodauth/features/verify_login_change.rb +12 -11
  105. data/lib/rodauth/features/webauthn.rb +505 -0
  106. data/lib/rodauth/features/webauthn_login.rb +70 -0
  107. data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
  108. data/lib/rodauth/version.rb +2 -2
  109. data/templates/button.str +1 -3
  110. data/templates/change-login.str +1 -2
  111. data/templates/change-password.str +3 -5
  112. data/templates/close-account.str +2 -2
  113. data/templates/confirm-password.str +1 -1
  114. data/templates/create-account.str +1 -1
  115. data/templates/email-auth-email.str +1 -1
  116. data/templates/email-auth-request-form.str +2 -3
  117. data/templates/email-auth.str +1 -1
  118. data/templates/global-logout-field.str +6 -0
  119. data/templates/login-confirm-field.str +2 -4
  120. data/templates/login-display.str +3 -2
  121. data/templates/login-field.str +2 -4
  122. data/templates/login-form-footer.str +6 -0
  123. data/templates/login-form.str +7 -0
  124. data/templates/login.str +1 -9
  125. data/templates/logout.str +1 -1
  126. data/templates/multi-phase-login.str +3 -0
  127. data/templates/otp-auth-code-field.str +5 -3
  128. data/templates/otp-auth.str +1 -1
  129. data/templates/otp-disable.str +1 -1
  130. data/templates/otp-setup.str +3 -3
  131. data/templates/password-confirm-field.str +2 -4
  132. data/templates/password-field.str +2 -4
  133. data/templates/recovery-auth.str +3 -6
  134. data/templates/recovery-codes.str +1 -1
  135. data/templates/remember.str +15 -20
  136. data/templates/reset-password-email.str +1 -1
  137. data/templates/reset-password-request.str +3 -3
  138. data/templates/reset-password.str +1 -2
  139. data/templates/sms-auth.str +1 -1
  140. data/templates/sms-code-field.str +5 -3
  141. data/templates/sms-confirm.str +1 -2
  142. data/templates/sms-disable.str +1 -2
  143. data/templates/sms-request.str +1 -1
  144. data/templates/sms-setup.str +6 -4
  145. data/templates/two-factor-auth.str +5 -0
  146. data/templates/two-factor-disable.str +6 -0
  147. data/templates/two-factor-manage.str +16 -0
  148. data/templates/unlock-account-email.str +1 -1
  149. data/templates/unlock-account-request.str +4 -4
  150. data/templates/unlock-account.str +1 -1
  151. data/templates/verify-account-email.str +1 -1
  152. data/templates/verify-account-resend.str +3 -3
  153. data/templates/verify-account.str +1 -2
  154. data/templates/verify-login-change-email.str +2 -1
  155. data/templates/verify-login-change.str +1 -1
  156. data/templates/webauthn-auth.str +11 -0
  157. data/templates/webauthn-remove.str +14 -0
  158. data/templates/webauthn-setup.str +12 -0
  159. metadata +110 -52
  160. data/Rakefile +0 -179
  161. data/doc/verify_change_login.rdoc +0 -11
  162. data/lib/rodauth/features/verify_change_login.rb +0 -20
  163. data/spec/account_expiration_spec.rb +0 -225
  164. data/spec/all.rb +0 -1
  165. data/spec/change_login_spec.rb +0 -156
  166. data/spec/change_password_notify_spec.rb +0 -33
  167. data/spec/change_password_spec.rb +0 -202
  168. data/spec/close_account_spec.rb +0 -162
  169. data/spec/confirm_password_spec.rb +0 -70
  170. data/spec/create_account_spec.rb +0 -127
  171. data/spec/disallow_common_passwords_spec.rb +0 -93
  172. data/spec/disallow_password_reuse_spec.rb +0 -179
  173. data/spec/email_auth_spec.rb +0 -285
  174. data/spec/http_basic_auth_spec.rb +0 -143
  175. data/spec/jwt_refresh_spec.rb +0 -256
  176. data/spec/jwt_spec.rb +0 -235
  177. data/spec/lockout_spec.rb +0 -250
  178. data/spec/login_spec.rb +0 -328
  179. data/spec/migrate/001_tables.rb +0 -184
  180. data/spec/migrate/002_account_password_hash_column.rb +0 -11
  181. data/spec/migrate_password/001_tables.rb +0 -73
  182. data/spec/migrate_travis/001_tables.rb +0 -141
  183. data/spec/password_complexity_spec.rb +0 -109
  184. data/spec/password_expiration_spec.rb +0 -244
  185. data/spec/password_grace_period_spec.rb +0 -93
  186. data/spec/remember_spec.rb +0 -451
  187. data/spec/reset_password_spec.rb +0 -229
  188. data/spec/rodauth_spec.rb +0 -343
  189. data/spec/session_expiration_spec.rb +0 -58
  190. data/spec/single_session_spec.rb +0 -127
  191. data/spec/spec_helper.rb +0 -327
  192. data/spec/two_factor_spec.rb +0 -1462
  193. data/spec/update_password_hash_spec.rb +0 -40
  194. data/spec/verify_account_grace_period_spec.rb +0 -171
  195. data/spec/verify_account_spec.rb +0 -240
  196. data/spec/verify_change_login_spec.rb +0 -46
  197. data/spec/verify_login_change_spec.rb +0 -232
  198. data/spec/views/layout-other.str +0 -11
  199. data/spec/views/layout.str +0 -11
  200. data/spec/views/login.str +0 -21
@@ -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,
@@ -109,9 +105,9 @@ module Rodauth
109
105
  return
110
106
  end
111
107
 
112
- session[session_key] = id
108
+ set_session_value(session_key, id)
113
109
  account = account_from_session
114
- session.delete(session_key)
110
+ remove_session_value(session_key)
115
111
 
116
112
  unless account
117
113
  remove_remember_key(id)
@@ -120,9 +116,8 @@ module Rodauth
120
116
  end
121
117
 
122
118
  before_load_memory
123
- update_session
119
+ login_session('remember')
124
120
 
125
- set_session_value(remembered_session_key, true)
126
121
  if extend_remember_deadline?
127
122
  active_remember_key_ds(id).update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period))
128
123
  remember_login
@@ -133,9 +128,7 @@ module Rodauth
133
128
  def remember_login
134
129
  get_remember_key
135
130
  opts = Hash[remember_cookie_options]
136
- key = remember_key_value
137
- key = compute_hmac(key) if hmac_secret
138
- opts[:value] = "#{account_id}_#{key}"
131
+ opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}"
139
132
  opts[:expires] = convert_timestamp(active_remember_key_ds.get(remember_deadline_column))
140
133
  ::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts)
141
134
  end
@@ -174,12 +167,8 @@ module Rodauth
174
167
  remember_key_ds(id).delete
175
168
  end
176
169
 
177
- def clear_remembered_session_key
178
- session.delete(remembered_session_key)
179
- end
180
-
181
170
  def logged_in_via_remember_key?
182
- !!session[remembered_session_key]
171
+ authenticated_by.include?('remember')
183
172
  end
184
173
 
185
174
  private
@@ -194,11 +183,6 @@ module Rodauth
194
183
  super if defined?(super)
195
184
  end
196
185
 
197
- def after_confirm_password
198
- super
199
- clear_remembered_session_key
200
- end
201
-
202
186
  attr_reader :remember_key_value
203
187
 
204
188
  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
@@ -179,7 +183,7 @@ module Rodauth
179
183
  end
180
184
 
181
185
  def send_reset_password_email
182
- create_reset_password_email.deliver!
186
+ send_email(create_reset_password_email)
183
187
  end
184
188
 
185
189
  def reset_password_email_link
@@ -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=\"#{prefix}/#{reset_password_request_route}\">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,10 +2,11 @@
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
6
 
7
7
  auth_value_method :max_session_lifetime, 86400
8
8
  session_key :session_created_session_key, :session_created_at
9
+ auth_value_method :session_expiration_error_status, 401
9
10
  auth_value_method :session_expiration_default, true
10
11
  auth_value_method :session_inactivity_timeout, 1800
11
12
  session_key :session_last_activity_session_key, :last_session_activity_at
@@ -37,6 +38,7 @@ module Rodauth
37
38
 
38
39
  def expire_session
39
40
  clear_session
41
+ set_redirect_error_status session_expiration_error_status
40
42
  set_redirect_error_flash session_expiration_error_flash
41
43
  redirect session_expiration_redirect
42
44
  end
@@ -45,11 +47,11 @@ module Rodauth
45
47
  require_login_redirect
46
48
  end
47
49
 
48
- private
49
-
50
50
  def update_session
51
51
  super
52
- session[session_last_activity_session_key] = session[session_created_session_key] = Time.now.to_i
52
+ t = Time.now.to_i
53
+ set_session_value(session_last_activity_session_key, t)
54
+ set_session_value(session_created_session_key, t)
53
55
  end
54
56
  end
55
57
  end
@@ -6,6 +6,7 @@ module Rodauth
6
6
  redirect
7
7
 
8
8
  auth_value_method :allow_raw_single_session_key?, false
9
+ auth_value_method :inactive_session_error_status, 401
9
10
  auth_value_method :single_session_id_column, :id
10
11
  auth_value_method :single_session_key_column, :key
11
12
  session_key :single_session_session_key, :single_session_key
@@ -55,6 +56,7 @@ module Rodauth
55
56
 
56
57
  def no_longer_active_session
57
58
  clear_session
59
+ set_redirect_error_status inactive_session_error_status
58
60
  set_redirect_error_flash single_session_error_flash
59
61
  redirect single_session_redirect
60
62
  end
@@ -70,6 +72,11 @@ module Rodauth
70
72
  end
71
73
  end
72
74
 
75
+ def update_session
76
+ super
77
+ update_single_session_key
78
+ end
79
+
73
80
  private
74
81
 
75
82
  def after_close_account
@@ -78,7 +85,7 @@ module Rodauth
78
85
  end
79
86
 
80
87
  def before_logout
81
- reset_single_session_key if request.post?
88
+ reset_single_session_key
82
89
  super if defined?(super)
83
90
  end
84
91
 
@@ -87,11 +94,6 @@ module Rodauth
87
94
  set_session_value(single_session_session_key, data)
88
95
  end
89
96
 
90
- def update_session
91
- super
92
- update_single_session_key
93
- end
94
-
95
97
  def single_session_ds
96
98
  db[single_session_table].
97
99
  where(single_session_id_column=>session_value)
@@ -28,28 +28,32 @@ module Rodauth
28
28
  button 'Send SMS Code', 'sms_request'
29
29
  button 'Setup SMS Backup Number', 'sms_setup'
30
30
 
31
- error_flash "Error authenticating via SMS code.", 'sms_invalid_code'
31
+ error_flash "Error authenticating via SMS code", 'sms_invalid_code'
32
32
  error_flash "Error disabling SMS authentication", 'sms_disable'
33
33
  error_flash "Error setting up SMS authentication", 'sms_setup'
34
- error_flash "Invalid or out of date SMS confirmation code used, must setup SMS authentication again.", 'sms_invalid_confirmation_code'
34
+ error_flash "Invalid or out of date SMS confirmation code used, must setup SMS authentication again", 'sms_invalid_confirmation_code'
35
35
  error_flash "No current SMS code for this account", 'no_current_sms_code'
36
- error_flash "SMS authentication has been locked out.", 'sms_lockout'
37
- error_flash "SMS authentication has already been setup.", 'sms_already_setup'
38
- error_flash "SMS authentication has not been setup yet.", 'sms_not_setup'
39
- error_flash "SMS authentication needs confirmation.", 'sms_needs_confirmation'
36
+ error_flash "SMS authentication has been locked out", 'sms_lockout'
37
+ error_flash "SMS authentication has already been setup", 'sms_already_setup'
38
+ error_flash "SMS authentication has not been setup yet", 'sms_not_setup'
39
+ error_flash "SMS authentication needs confirmation", 'sms_needs_confirmation'
40
40
 
41
- notice_flash "SMS authentication code has been sent.", 'sms_request'
42
- notice_flash "SMS authentication has been disabled.", 'sms_disable'
43
- notice_flash "SMS authentication has been setup.", 'sms_confirm'
41
+ notice_flash "SMS authentication code has been sent", 'sms_request'
42
+ notice_flash "SMS authentication has been disabled", 'sms_disable'
43
+ notice_flash "SMS authentication has been setup", 'sms_confirm'
44
+
45
+ translatable_method :sms_auth_link_text, "Authenticate Using SMS Code"
46
+ translatable_method :sms_setup_link_text, "Setup Backup SMS Authentication"
47
+ translatable_method :sms_disable_link_text, "Disable SMS Authentication"
44
48
 
45
49
  redirect :sms_already_setup
46
50
  redirect :sms_confirm
47
51
  redirect :sms_disable
48
- redirect(:sms_auth){"#{prefix}/#{sms_auth_route}"}
49
- redirect(:sms_needs_confirmation){"#{prefix}/#{sms_confirm_route}"}
50
- redirect(:sms_needs_setup){"#{prefix}/#{sms_setup_route}"}
51
- redirect(:sms_request){"#{prefix}/#{sms_request_route}"}
52
- redirect(:sms_lockout){_two_factor_auth_required_redirect}
52
+ redirect(:sms_auth){sms_auth_path}
53
+ redirect(:sms_needs_confirmation){sms_confirm_path}
54
+ redirect(:sms_needs_setup){sms_setup_path}
55
+ redirect(:sms_request){sms_request_path}
56
+ redirect(:sms_lockout){two_factor_auth_required_redirect}
53
57
 
54
58
  loaded_templates %w'sms-auth sms-confirm sms-disable sms-request sms-setup sms-code-field password-field'
55
59
  view 'sms-auth', 'Authenticate via SMS Code', 'sms_auth'
@@ -64,18 +68,19 @@ module Rodauth
64
68
  auth_value_method :sms_auth_code_length, 6
65
69
  auth_value_method :sms_code_allowed_seconds, 300
66
70
  auth_value_method :sms_code_column, :code
67
- auth_value_method :sms_code_label, 'SMS Code'
71
+ translatable_method :sms_code_label, 'SMS Code'
68
72
  auth_value_method :sms_code_param, 'sms-code'
69
73
  auth_value_method :sms_codes_table, :account_sms_codes
70
74
  auth_value_method :sms_confirm_code_length, 12
71
75
  auth_value_method :sms_failure_limit, 5
72
76
  auth_value_method :sms_failures_column, :num_failures
73
77
  auth_value_method :sms_id_column, :id
74
- auth_value_method :sms_invalid_code_message, "invalid SMS code"
75
- auth_value_method :sms_invalid_phone_message, "invalid SMS phone number"
78
+ translatable_method :sms_invalid_code_message, "invalid SMS code"
79
+ translatable_method :sms_invalid_phone_message, "invalid SMS phone number"
76
80
  auth_value_method :sms_issued_at_column, :code_issued_at
77
81
  auth_value_method :sms_phone_column, :phone_number
78
- auth_value_method :sms_phone_label, 'Phone Number'
82
+ translatable_method :sms_phone_label, 'Phone Number'
83
+ auth_value_method :sms_phone_input_type, 'tel'
79
84
  auth_value_method :sms_phone_min_length, 7
80
85
  auth_value_method :sms_phone_param, 'sms-phone'
81
86
 
@@ -110,7 +115,7 @@ module Rodauth
110
115
  route(:sms_request) do |r|
111
116
  require_login
112
117
  require_account_session
113
- require_two_factor_not_authenticated
118
+ require_two_factor_not_authenticated('sms_code')
114
119
  require_sms_available
115
120
  before_sms_request_route
116
121
 
@@ -133,7 +138,7 @@ module Rodauth
133
138
  route(:sms_auth) do |r|
134
139
  require_login
135
140
  require_account_session
136
- require_two_factor_not_authenticated
141
+ require_two_factor_not_authenticated('sms_code')
137
142
  require_sms_available
138
143
 
139
144
  unless sms_current_auth?
@@ -157,7 +162,7 @@ module Rodauth
157
162
  if sms_code_match?(param(sms_code_param))
158
163
  before_sms_auth
159
164
  sms_remove_failures
160
- two_factor_authenticate(:sms_code)
165
+ two_factor_authenticate('sms_code')
161
166
  else
162
167
  sms_record_failure
163
168
  after_sms_failure
@@ -238,8 +243,8 @@ module Rodauth
238
243
  before_sms_confirm
239
244
  sms_confirm
240
245
  after_sms_confirm
241
- if sms_codes_primary?
242
- two_factor_authenticate(:sms_code)
246
+ unless two_factor_authenticated?
247
+ two_factor_update_session('sms_code')
243
248
  end
244
249
  end
245
250
 
@@ -268,8 +273,8 @@ module Rodauth
268
273
  transaction do
269
274
  before_sms_disable
270
275
  sms_disable
271
- if sms_codes_primary?
272
- two_factor_remove_session
276
+ if two_factor_login_type_match?('sms_code')
277
+ two_factor_remove_session('sms_code')
273
278
  end
274
279
  after_sms_disable
275
280
  end
@@ -284,18 +289,6 @@ module Rodauth
284
289
  end
285
290
  end
286
291
 
287
- def two_factor_need_setup_redirect
288
- super || (sms_needs_setup_redirect if sms_codes_primary?)
289
- end
290
-
291
- def two_factor_auth_required_redirect
292
- super || (sms_request_redirect if sms_codes_primary? && sms_available?)
293
- end
294
-
295
- def two_factor_auth_fallback_redirect
296
- sms_available? ? sms_request_redirect : super
297
- end
298
-
299
292
  def two_factor_remove
300
293
  super
301
294
  sms_disable
@@ -306,37 +299,6 @@ module Rodauth
306
299
  sms_remove_failures
307
300
  end
308
301
 
309
- def two_factor_authentication_setup?
310
- super || (sms_codes_primary? && sms_setup?)
311
- end
312
-
313
- def otp_auth_form_footer
314
- "#{super if defined?(super)}#{"<p><a href=\"#{sms_request_route}\">Authenticate using SMS code</a></p>" if sms_available?}"
315
- end
316
-
317
- def otp_lockout_redirect
318
- if sms_available?
319
- sms_request_redirect
320
- else
321
- super if defined?(super)
322
- end
323
- end
324
-
325
- def otp_lockout_error_flash
326
- msg = super if defined?(super)
327
- if sms_available?
328
- msg = "#{msg} Can use SMS code to unlock."
329
- end
330
- msg
331
- end
332
-
333
- def otp_remove
334
- super if defined?(super)
335
- unless sms_codes_primary?
336
- sms_disable
337
- end
338
- end
339
-
340
302
  def require_sms_setup
341
303
  unless sms_setup?
342
304
  set_redirect_error_status(two_factor_not_setup_error_status)
@@ -375,7 +337,6 @@ module Rodauth
375
337
  def sms_disable
376
338
  sms_ds.delete
377
339
  @sms = nil
378
- super if defined?(super)
379
340
  end
380
341
 
381
342
  def sms_confirm_failure
@@ -415,11 +376,11 @@ module Rodauth
415
376
  end
416
377
 
417
378
  def sms_auth_message(code)
418
- "SMS authentication code for #{request.host} is #{code}"
379
+ "SMS authentication code for #{domain} is #{code}"
419
380
  end
420
381
 
421
382
  def sms_confirm_message(code)
422
- "SMS confirmation code for #{request.host} is #{code}"
383
+ "SMS confirmation code for #{domain} is #{code}"
423
384
  end
424
385
 
425
386
  def sms_set_code(code)
@@ -468,10 +429,39 @@ module Rodauth
468
429
  sms_code && sms_code_issued_at + sms_code_allowed_seconds > Time.now
469
430
  end
470
431
 
432
+ def possible_authentication_methods
433
+ methods = super
434
+ methods << 'sms_code' if sms_setup?
435
+ methods
436
+ end
437
+
471
438
  private
472
439
 
440
+ def _two_factor_auth_links
441
+ links = super
442
+ links << [30, sms_request_path, sms_auth_link_text] if sms_available?
443
+ links
444
+ end
445
+
446
+ def _two_factor_setup_links
447
+ links = super
448
+ links << [30, sms_setup_path, sms_setup_link_text] if !sms_setup? && (sms_codes_primary? || uses_two_factor_authentication?)
449
+ links
450
+ end
451
+
452
+ def _two_factor_remove_links
453
+ links = super
454
+ links << [30, sms_disable_path, sms_disable_link_text] if sms_setup?
455
+ links
456
+ end
457
+
458
+ def _two_factor_remove_all_from_session
459
+ two_factor_remove_session('sms_codes')
460
+ super
461
+ end
462
+
473
463
  def sms_codes_primary?
474
- !features.include?(:otp)
464
+ (features & [:otp, :webauthn]).empty?
475
465
  end
476
466
 
477
467
  def sms_normalize_phone(phone)