rodauth 2.36.0 → 2.38.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/lib/rodauth/features/base.rb +52 -9
  3. data/lib/rodauth/features/change_login.rb +2 -2
  4. data/lib/rodauth/features/create_account.rb +2 -2
  5. data/lib/rodauth/features/email_auth.rb +5 -5
  6. data/lib/rodauth/features/internal_request.rb +4 -4
  7. data/lib/rodauth/features/json.rb +5 -0
  8. data/lib/rodauth/features/jwt.rb +7 -11
  9. data/lib/rodauth/features/lockout.rb +5 -5
  10. data/lib/rodauth/features/login.rb +1 -1
  11. data/lib/rodauth/features/login_password_requirements_base.rb +13 -0
  12. data/lib/rodauth/features/reset_password.rb +5 -5
  13. data/lib/rodauth/features/sms_codes.rb +1 -1
  14. data/lib/rodauth/features/two_factor_base.rb +6 -13
  15. data/lib/rodauth/features/verify_account.rb +6 -6
  16. data/lib/rodauth/features/webauthn_autofill.rb +2 -1
  17. data/lib/rodauth/features/webauthn_login.rb +1 -1
  18. data/lib/rodauth/version.rb +1 -1
  19. data/lib/rodauth.rb +8 -2
  20. metadata +3 -261
  21. data/CHANGELOG +0 -521
  22. data/README.rdoc +0 -1555
  23. data/doc/account_expiration.rdoc +0 -41
  24. data/doc/active_sessions.rdoc +0 -56
  25. data/doc/argon2.rdoc +0 -54
  26. data/doc/audit_logging.rdoc +0 -44
  27. data/doc/base.rdoc +0 -123
  28. data/doc/change_login.rdoc +0 -25
  29. data/doc/change_password.rdoc +0 -26
  30. data/doc/change_password_notify.rdoc +0 -14
  31. data/doc/close_account.rdoc +0 -26
  32. data/doc/confirm_password.rdoc +0 -32
  33. data/doc/create_account.rdoc +0 -27
  34. data/doc/disallow_common_passwords.rdoc +0 -17
  35. data/doc/disallow_password_reuse.rdoc +0 -30
  36. data/doc/email_auth.rdoc +0 -55
  37. data/doc/email_base.rdoc +0 -18
  38. data/doc/error_reasons.rdoc +0 -77
  39. data/doc/guides/admin_activation.rdoc +0 -46
  40. data/doc/guides/already_authenticated.rdoc +0 -10
  41. data/doc/guides/alternative_login.rdoc +0 -46
  42. data/doc/guides/change_table_and_column_names.rdoc +0 -19
  43. data/doc/guides/create_account_programmatically.rdoc +0 -38
  44. data/doc/guides/delay_password.rdoc +0 -25
  45. data/doc/guides/email_only.rdoc +0 -16
  46. data/doc/guides/i18n.rdoc +0 -29
  47. data/doc/guides/internals.rdoc +0 -233
  48. data/doc/guides/links.rdoc +0 -12
  49. data/doc/guides/login_return.rdoc +0 -37
  50. data/doc/guides/migrate_password_hash_algorithm.rdoc +0 -15
  51. data/doc/guides/password_column.rdoc +0 -25
  52. data/doc/guides/password_confirmation.rdoc +0 -37
  53. data/doc/guides/password_requirements.rdoc +0 -43
  54. data/doc/guides/paths.rdoc +0 -51
  55. data/doc/guides/query_params.rdoc +0 -9
  56. data/doc/guides/redirects.rdoc +0 -17
  57. data/doc/guides/registration_field.rdoc +0 -68
  58. data/doc/guides/render_confirmation.rdoc +0 -17
  59. data/doc/guides/require_mfa.rdoc +0 -30
  60. data/doc/guides/reset_password_autologin.rdoc +0 -21
  61. data/doc/guides/share_configuration.rdoc +0 -34
  62. data/doc/guides/status_column.rdoc +0 -28
  63. data/doc/guides/totp_or_recovery.rdoc +0 -16
  64. data/doc/http_basic_auth.rdoc +0 -18
  65. data/doc/internal_request.rdoc +0 -539
  66. data/doc/json.rdoc +0 -56
  67. data/doc/jwt.rdoc +0 -52
  68. data/doc/jwt_cors.rdoc +0 -22
  69. data/doc/jwt_refresh.rdoc +0 -58
  70. data/doc/lockout.rdoc +0 -73
  71. data/doc/login.rdoc +0 -39
  72. data/doc/login_password_requirements_base.rdoc +0 -44
  73. data/doc/logout.rdoc +0 -22
  74. data/doc/otp.rdoc +0 -93
  75. data/doc/otp_lockout_email.rdoc +0 -30
  76. data/doc/otp_modify_email.rdoc +0 -19
  77. data/doc/otp_unlock.rdoc +0 -58
  78. data/doc/password_complexity.rdoc +0 -34
  79. data/doc/password_expiration.rdoc +0 -38
  80. data/doc/password_grace_period.rdoc +0 -24
  81. data/doc/password_pepper.rdoc +0 -52
  82. data/doc/path_class_methods.rdoc +0 -10
  83. data/doc/recovery_codes.rdoc +0 -61
  84. data/doc/release_notes/1.0.0.txt +0 -443
  85. data/doc/release_notes/1.1.0.txt +0 -8
  86. data/doc/release_notes/1.10.0.txt +0 -80
  87. data/doc/release_notes/1.11.0.txt +0 -32
  88. data/doc/release_notes/1.12.0.txt +0 -61
  89. data/doc/release_notes/1.13.0.txt +0 -34
  90. data/doc/release_notes/1.14.0.txt +0 -19
  91. data/doc/release_notes/1.15.0.txt +0 -21
  92. data/doc/release_notes/1.16.0.txt +0 -31
  93. data/doc/release_notes/1.17.0.txt +0 -23
  94. data/doc/release_notes/1.18.0.txt +0 -26
  95. data/doc/release_notes/1.19.0.txt +0 -116
  96. data/doc/release_notes/1.2.0.txt +0 -18
  97. data/doc/release_notes/1.20.0.txt +0 -175
  98. data/doc/release_notes/1.21.0.txt +0 -12
  99. data/doc/release_notes/1.22.0.txt +0 -11
  100. data/doc/release_notes/1.23.0.txt +0 -32
  101. data/doc/release_notes/1.3.0.txt +0 -21
  102. data/doc/release_notes/1.4.0.txt +0 -11
  103. data/doc/release_notes/1.5.0.txt +0 -74
  104. data/doc/release_notes/1.6.0.txt +0 -37
  105. data/doc/release_notes/1.7.0.txt +0 -6
  106. data/doc/release_notes/1.8.0.txt +0 -14
  107. data/doc/release_notes/1.9.0.txt +0 -15
  108. data/doc/release_notes/2.0.0.txt +0 -361
  109. data/doc/release_notes/2.1.0.txt +0 -31
  110. data/doc/release_notes/2.10.0.txt +0 -47
  111. data/doc/release_notes/2.11.0.txt +0 -31
  112. data/doc/release_notes/2.12.0.txt +0 -17
  113. data/doc/release_notes/2.13.0.txt +0 -19
  114. data/doc/release_notes/2.14.0.txt +0 -17
  115. data/doc/release_notes/2.15.0.txt +0 -48
  116. data/doc/release_notes/2.16.0.txt +0 -20
  117. data/doc/release_notes/2.17.0.txt +0 -10
  118. data/doc/release_notes/2.18.0.txt +0 -27
  119. data/doc/release_notes/2.19.0.txt +0 -61
  120. data/doc/release_notes/2.2.0.txt +0 -39
  121. data/doc/release_notes/2.20.0.txt +0 -10
  122. data/doc/release_notes/2.21.0.txt +0 -28
  123. data/doc/release_notes/2.22.0.txt +0 -43
  124. data/doc/release_notes/2.23.0.txt +0 -15
  125. data/doc/release_notes/2.24.0.txt +0 -15
  126. data/doc/release_notes/2.25.0.txt +0 -8
  127. data/doc/release_notes/2.26.0.txt +0 -45
  128. data/doc/release_notes/2.27.0.txt +0 -35
  129. data/doc/release_notes/2.28.0.txt +0 -16
  130. data/doc/release_notes/2.29.0.txt +0 -27
  131. data/doc/release_notes/2.3.0.txt +0 -37
  132. data/doc/release_notes/2.30.0.txt +0 -15
  133. data/doc/release_notes/2.31.0.txt +0 -47
  134. data/doc/release_notes/2.32.0.txt +0 -65
  135. data/doc/release_notes/2.33.0.txt +0 -18
  136. data/doc/release_notes/2.34.0.txt +0 -36
  137. data/doc/release_notes/2.35.0.txt +0 -22
  138. data/doc/release_notes/2.36.0.txt +0 -35
  139. data/doc/release_notes/2.4.0.txt +0 -22
  140. data/doc/release_notes/2.5.0.txt +0 -20
  141. data/doc/release_notes/2.6.0.txt +0 -37
  142. data/doc/release_notes/2.7.0.txt +0 -33
  143. data/doc/release_notes/2.8.0.txt +0 -20
  144. data/doc/release_notes/2.9.0.txt +0 -21
  145. data/doc/remember.rdoc +0 -79
  146. data/doc/reset_password.rdoc +0 -66
  147. data/doc/reset_password_notify.rdoc +0 -17
  148. data/doc/session_expiration.rdoc +0 -28
  149. data/doc/single_session.rdoc +0 -37
  150. data/doc/sms_codes.rdoc +0 -138
  151. data/doc/two_factor_base.rdoc +0 -70
  152. data/doc/update_password_hash.rdoc +0 -7
  153. data/doc/verify_account.rdoc +0 -67
  154. data/doc/verify_account_grace_period.rdoc +0 -19
  155. data/doc/verify_login_change.rdoc +0 -59
  156. data/doc/webauthn.rdoc +0 -118
  157. data/doc/webauthn_autofill.rdoc +0 -19
  158. data/doc/webauthn_login.rdoc +0 -16
  159. data/doc/webauthn_modify_email.rdoc +0 -19
  160. data/doc/webauthn_verify_account.rdoc +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4daf43beb9b2af129683b299f1e455e5b38fa925b1cc020ec79ae951f56f292b
4
- data.tar.gz: a694fe203f76512691004d1138ad03cab7699d317a0a658ae6ac51732ff22b36
3
+ metadata.gz: f79db7dec7147665538bf0b4f8e0c6c554d88396b294f19e5a5ed26543c31d1c
4
+ data.tar.gz: ea377861679a55895bc325b1cf3932970b0de9c773615ba2daecb5805704ae02
5
5
  SHA512:
6
- metadata.gz: aef00a1d31a310ea0061f54ebcb4044ccdcc7106d2f07074f4585ac5406776784bef9c698befa050b374d4dd37dd2495d3a3e4dcd6854e387fc8442901f3f227
7
- data.tar.gz: 85d9837f21b320f2bec1241f07e22ff150b49192017cb1ea20d2863fce93b547d5271cafe30b08baeaa582fd4f40b715ce488d9fb19bd7fe2cc43a2f565b5266
6
+ metadata.gz: 8e13b4dc188867866ee0eda53765091bb48f583d65957e488737b612bde4cf4ae9caa18edd2d88b3c609a7a3e25a51eec42fc5aa25e4083ea51c283310a36cb9
7
+ data.tar.gz: d50e275056bf3196a025e1933ab97ecd88d784c80ec97412cf046bb9718f558da4b321f7fbf52ba09d3554059157ef67f3f407625fd65c237fd2334e170066a7
@@ -67,6 +67,7 @@ module Rodauth
67
67
  auth_value_method :unopen_account_error_status, 403
68
68
  translatable_method :unverified_account_message, "unverified account, please verify account before logging in"
69
69
  auth_value_method :default_field_attributes, ''
70
+ auth_value_method :use_template_fixed_locals?, true
70
71
 
71
72
  redirect(:require_login){"#{prefix}/login"}
72
73
 
@@ -98,6 +99,7 @@ module Rodauth
98
99
  :inputmode_for_field?,
99
100
  :logged_in?,
100
101
  :login_required,
102
+ :normalize_login,
101
103
  :null_byte_parameter_value,
102
104
  :open_account?,
103
105
  :over_max_bytesize_param_value,
@@ -321,7 +323,7 @@ module Rodauth
321
323
  end
322
324
 
323
325
  def clear_session
324
- if scope.respond_to?(:clear_session)
326
+ if use_scope_clear_session?
325
327
  scope.clear_session
326
328
  else
327
329
  session.clear
@@ -408,6 +410,7 @@ module Rodauth
408
410
 
409
411
  def button_opts(value, opts)
410
412
  opts = Hash[template_opts].merge!(opts)
413
+ _merge_fixed_locals_opts(opts, button_fixed_locals)
411
414
  opts[:locals] = {:value=>value, :opts=>opts}
412
415
  opts[:cache] = cache_templates
413
416
  opts[:cache_key] = :rodauth_button
@@ -505,6 +508,15 @@ module Rodauth
505
508
  nil
506
509
  end
507
510
 
511
+ # The normalized value of the login parameter
512
+ def login_param_value
513
+ normalize_login(param(login_param))
514
+ end
515
+
516
+ def normalize_login(login)
517
+ login
518
+ end
519
+
508
520
  # Return nil by default for values with null bytes
509
521
  def null_byte_parameter_value(key, value)
510
522
  nil
@@ -532,6 +544,12 @@ module Rodauth
532
544
  has_password? ? ['password'] : []
533
545
  end
534
546
 
547
+ def has_password?
548
+ return @has_password if defined?(@has_password)
549
+ return false unless account || session_value
550
+ @has_password = !!get_password_hash
551
+ end
552
+
535
553
  private
536
554
 
537
555
  def _around_rodauth
@@ -545,6 +563,20 @@ module Rodauth
545
563
  s
546
564
  end
547
565
 
566
+ if RUBY_VERSION >= '2.1'
567
+ def button_fixed_locals
568
+ '(value:, opts:)'
569
+ end
570
+ # :nocov:
571
+ else
572
+ # Work on Ruby 2.0 when using Tilt 2.6+, as Ruby 2.0 does
573
+ # not support required keyword arguments.
574
+ def button_fixed_locals
575
+ '(value: nil, opts: nil)'
576
+ end
577
+ end
578
+ # :nocov:
579
+
548
580
  def database_function_password_match?(name, hash_id, password, salt)
549
581
  db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
550
582
  end
@@ -708,12 +740,6 @@ module Rodauth
708
740
  end
709
741
  end
710
742
 
711
- def has_password?
712
- return @has_password if defined?(@has_password)
713
- return false unless account || session_value
714
- @has_password = !!get_password_hash
715
- end
716
-
717
743
  def password_hash_using_salt(password, salt)
718
744
  BCrypt::Engine.hash_secret(password, salt)
719
745
  end
@@ -756,7 +782,7 @@ module Rodauth
756
782
  end
757
783
 
758
784
  def compute_raw_hmac(data)
759
- raise ArgumentError, "hmac_secret not set" unless hmac_secret
785
+ raise ConfigurationError, "hmac_secret not set" unless hmac_secret
760
786
  compute_raw_hmac_with_secret(data, hmac_secret)
761
787
  end
762
788
 
@@ -869,9 +895,13 @@ module Rodauth
869
895
  false
870
896
  end
871
897
 
898
+ def use_scope_clear_session?
899
+ scope.respond_to?(:clear_session)
900
+ end
901
+
872
902
  def require_response(meth)
873
903
  send(meth)
874
- 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."
904
+ raise ConfigurationError, "#{meth.to_s.sub(/\A_/, '')} overridden without returning a response (should use redirect or request.halt)."
875
905
  end
876
906
 
877
907
  def set_session_value(key, value)
@@ -898,6 +928,7 @@ module Rodauth
898
928
 
899
929
  def _view_opts(page)
900
930
  opts = template_opts.dup
931
+ _merge_fixed_locals_opts(opts, '(rodauth: self.rodauth)')
901
932
  opts[:locals] = opts[:locals] ? opts[:locals].dup : {}
902
933
  opts[:locals][:rodauth] = self
903
934
  opts[:cache] = cache_templates
@@ -905,6 +936,14 @@ module Rodauth
905
936
  _template_opts(opts, page)
906
937
  end
907
938
 
939
+ def _merge_fixed_locals_opts(opts, fixed_locals)
940
+ if use_template_fixed_locals? && !opts[:locals]
941
+ fixed_locals_opts = {default_fixed_locals: fixed_locals}
942
+ fixed_locals_opts.merge!(opts[:template_opts]) if opts[:template_opts]
943
+ opts[:template_opts] = fixed_locals_opts
944
+ end
945
+ end
946
+
908
947
  # Set the template path only if there isn't an overridden template in the application.
909
948
  # Result should replace existing template opts.
910
949
  def _template_opts(opts, page)
@@ -916,6 +955,10 @@ module Rodauth
916
955
  end
917
956
 
918
957
  def _view(meth, page)
958
+ unless scope.respond_to?(meth)
959
+ raise ConfigurationError, "attempted to render a built-in view/email template (#{page.inspect}), but rendering is disabled"
960
+ end
961
+
919
962
  scope.send(meth, _view_opts(page))
920
963
  end
921
964
  end
@@ -36,12 +36,12 @@ module Rodauth
36
36
  throw_error_reason(:invalid_password, invalid_password_error_status, password_param, invalid_password_message)
37
37
  end
38
38
 
39
- login = param(login_param)
39
+ login = login_param_value
40
40
  unless login_meets_requirements?(login)
41
41
  throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
42
42
  end
43
43
 
44
- if require_login_confirmation? && login != param(login_confirm_param)
44
+ if require_login_confirmation? && !login_confirmation_matches?(login, param(login_confirm_param))
45
45
  throw_error_reason(:logins_do_not_match, unmatched_field_error_status, login_param, logins_do_not_match_message)
46
46
  end
47
47
 
@@ -40,12 +40,12 @@ module Rodauth
40
40
  end
41
41
 
42
42
  r.post do
43
- login = param(login_param)
43
+ login = login_param_value
44
44
  password = param(password_param)
45
45
  new_account(login)
46
46
 
47
47
  catch_error do
48
- if require_login_confirmation? && login != param(login_confirm_param)
48
+ if require_login_confirmation? && !login_confirmation_matches?(login, param(login_confirm_param))
49
49
  throw_error_reason(:logins_do_not_match, unmatched_field_error_status, login_param, logins_do_not_match_message)
50
50
  end
51
51
 
@@ -56,7 +56,7 @@ module Rodauth
56
56
  before_email_auth_request_route
57
57
 
58
58
  r.post do
59
- if account_from_login(param(login_param)) && open_account?
59
+ if account_from_login(login_param_value) && open_account?
60
60
  _email_auth_request
61
61
  end
62
62
 
@@ -163,6 +163,10 @@ module Rodauth
163
163
  methods
164
164
  end
165
165
 
166
+ def email_auth_email_recently_sent?
167
+ (email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within)
168
+ end
169
+
166
170
  private
167
171
 
168
172
  def _multi_phase_login_forms
@@ -171,10 +175,6 @@ module Rodauth
171
175
  forms
172
176
  end
173
177
 
174
- def email_auth_email_recently_sent?
175
- (email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within)
176
- end
177
-
178
178
  def _email_auth_request
179
179
  if email_auth_email_recently_sent?
180
180
  set_redirect_error_flash email_auth_email_recently_sent_error_flash
@@ -223,14 +223,14 @@ module Rodauth
223
223
  end
224
224
 
225
225
  def _handle_account_id_for_login(_)
226
- raise InternalRequestError, "no login provided" unless login = param_or_nil(login_param)
227
- raise InternalRequestError, "no account for login" unless account = account_from_login(login)
226
+ raise InternalRequestError, "no login provided" unless param_or_nil(login_param)
227
+ raise InternalRequestError, "no account for login" unless account = account_from_login(login_param_value)
228
228
  _return_from_internal_request(account[account_id_column])
229
229
  end
230
230
 
231
231
  def _handle_account_exists?(_)
232
- raise InternalRequestError, "no login provided" unless login = param_or_nil(login_param)
233
- _return_from_internal_request(!!account_from_login(login))
232
+ raise InternalRequestError, "no login provided" unless param_or_nil(login_param)
233
+ _return_from_internal_request(!!account_from_login(login_param_value))
234
234
  end
235
235
 
236
236
  def _handle_lock_account(_)
@@ -72,6 +72,11 @@ module Rodauth
72
72
 
73
73
  private
74
74
 
75
+ def check_csrf?
76
+ return false if use_json?
77
+ super
78
+ end
79
+
75
80
  def _set_otp_unlock_info
76
81
  if use_json?
77
82
  json_response[:num_successes] = otp_unlock_num_successes
@@ -60,14 +60,11 @@ module Rodauth
60
60
 
61
61
  def clear_session
62
62
  super
63
- if use_jwt?
64
- session.clear
65
- set_jwt
66
- end
63
+ set_jwt if use_jwt?
67
64
  end
68
65
 
69
66
  def jwt_secret
70
- raise ArgumentError, "jwt_secret not set"
67
+ raise ConfigurationError, "jwt_secret not set"
71
68
  end
72
69
 
73
70
  def jwt_session_hash
@@ -104,16 +101,11 @@ module Rodauth
104
101
 
105
102
  private
106
103
 
107
- def check_csrf?
108
- return false if use_jwt?
109
- super
110
- end
111
-
112
104
  def _jwt_decode_opts
113
105
  jwt_decode_opts
114
106
  end
115
107
 
116
- if JWT::VERSION::MAJOR > 2 || (JWT::VERSION::MAJOR == 2 && JWT::VERSION::MINOR >= 4)
108
+ if JWT.gem_version >= Gem::Version.new("2.4")
117
109
  def _jwt_decode_secrets
118
110
  secrets = [jwt_secret, jwt_old_secret]
119
111
  secrets.compact!
@@ -158,5 +150,9 @@ module Rodauth
158
150
  def set_jwt
159
151
  set_jwt_token(session_jwt)
160
152
  end
153
+
154
+ def use_scope_clear_session?
155
+ super && !use_jwt?
156
+ end
161
157
  end
162
158
  end
@@ -70,7 +70,7 @@ module Rodauth
70
70
  before_unlock_account_request_route
71
71
 
72
72
  r.post do
73
- if account_from_login(param(login_param)) && get_unlock_account_key
73
+ if account_from_login(login_param_value) && get_unlock_account_key
74
74
  if unlock_account_email_recently_sent?
75
75
  set_redirect_error_flash unlock_account_email_recently_sent_error_flash
76
76
  redirect unlock_account_email_recently_sent_redirect
@@ -237,6 +237,10 @@ module Rodauth
237
237
  account_lockouts_ds.update(account_lockouts_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if account_lockouts_email_last_sent_column
238
238
  end
239
239
 
240
+ def unlock_account_email_recently_sent?
241
+ (email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within)
242
+ end
243
+
240
244
  private
241
245
 
242
246
  attr_reader :unlock_account_key_value
@@ -278,10 +282,6 @@ module Rodauth
278
282
  return_response unlock_account_request_view
279
283
  end
280
284
 
281
- def unlock_account_email_recently_sent?
282
- (email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within)
283
- end
284
-
285
285
  def use_date_arithmetic?
286
286
  super || db.database_type == :mysql
287
287
  end
@@ -45,7 +45,7 @@ module Rodauth
45
45
  view = :login_view
46
46
 
47
47
  catch_error do
48
- unless account_from_login(param(login_param))
48
+ unless account_from_login(login_param_value)
49
49
  throw_error_reason(:no_matching_login, no_matching_login_error_status, login_param, no_matching_login_message)
50
50
  end
51
51
 
@@ -36,6 +36,7 @@ module Rodauth
36
36
  )
37
37
 
38
38
  auth_methods(
39
+ :login_confirmation_matches?,
39
40
  :login_meets_requirements?,
40
41
  :login_valid_email?,
41
42
  :password_hash,
@@ -126,6 +127,18 @@ module Rodauth
126
127
  @login_requirement_message = message
127
128
  end
128
129
 
130
+ if RUBY_VERSION >= '2.4'
131
+ def login_confirmation_matches?(login, login_confirmation)
132
+ login.casecmp?(login_confirmation)
133
+ end
134
+ # :nocov:
135
+ else
136
+ def login_confirmation_matches?(login, login_confirmation)
137
+ login.casecmp(login_confirmation) == 0
138
+ end
139
+ # :nocov:
140
+ end
141
+
129
142
  def login_meets_length_requirements?(login)
130
143
  if login_minimum_length > login.length
131
144
  set_login_requirement_error_message(:login_too_short, login_too_short_message)
@@ -69,7 +69,7 @@ module Rodauth
69
69
 
70
70
  r.post do
71
71
  catch_error do
72
- unless account_from_login(param(login_param))
72
+ unless account_from_login(login_param_value)
73
73
  throw_error_reason(:no_matching_login, no_matching_login_error_status, login_param, no_matching_login_message)
74
74
  end
75
75
 
@@ -204,16 +204,16 @@ module Rodauth
204
204
  end
205
205
  end
206
206
 
207
+ def reset_password_email_recently_sent?
208
+ (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within)
209
+ end
210
+
207
211
  private
208
212
 
209
213
  def _login_form_footer_links
210
214
  super << [20, reset_password_request_path, reset_password_request_link_text]
211
215
  end
212
216
 
213
- def reset_password_email_recently_sent?
214
- (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within)
215
- end
216
-
217
217
  attr_reader :reset_password_key_value
218
218
 
219
219
  def after_login_failure
@@ -514,7 +514,7 @@ module Rodauth
514
514
  end
515
515
 
516
516
  def sms_send(phone, message)
517
- raise NotImplementedError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work"
517
+ raise ConfigurationError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work"
518
518
  end
519
519
 
520
520
  def update_sms(values)
@@ -124,23 +124,12 @@ module Rodauth
124
124
  end
125
125
 
126
126
  def authenticated?
127
- # False if not authenticated via single factor
128
- return false unless super
129
-
130
- # True if already authenticated via 2nd factor
131
- return true if two_factor_authenticated?
132
-
133
- # True if authenticated via single factor and 2nd factor not setup
134
- !uses_two_factor_authentication?
127
+ super && !two_factor_partially_authenticated?
135
128
  end
136
129
 
137
130
  def require_authentication
138
131
  super
139
-
140
- # Avoid database query if already authenticated via 2nd factor
141
- return if two_factor_authenticated?
142
-
143
- require_two_factor_authenticated if uses_two_factor_authentication?
132
+ require_two_factor_authenticated if two_factor_partially_authenticated?
144
133
  end
145
134
 
146
135
  def require_two_factor_setup
@@ -188,6 +177,10 @@ module Rodauth
188
177
  end
189
178
  end
190
179
 
180
+ def two_factor_partially_authenticated?
181
+ logged_in? && !two_factor_authenticated? && uses_two_factor_authentication?
182
+ end
183
+
191
184
  def two_factor_authenticated?
192
185
  authenticated_by && authenticated_by.length >= 2
193
186
  end
@@ -71,7 +71,7 @@ module Rodauth
71
71
  end
72
72
 
73
73
  r.post do
74
- if account_from_login(param(login_param)) && allow_resending_verify_account_email?
74
+ if account_from_login(login_param_value) && allow_resending_verify_account_email?
75
75
  if verify_account_email_recently_sent?
76
76
  set_redirect_error_flash verify_account_email_recently_sent_error_flash
77
77
  redirect verify_account_email_recently_sent_redirect
@@ -240,20 +240,20 @@ module Rodauth
240
240
  send_verify_account_email
241
241
  end
242
242
 
243
+ def verify_account_email_recently_sent?
244
+ account && (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within)
245
+ end
246
+
243
247
  private
244
248
 
245
249
  def _login_form_footer_links
246
250
  links = super
247
- if !param_or_nil(login_param) || ((account || account_from_login(param(login_param))) && allow_resending_verify_account_email?)
251
+ if !param_or_nil(login_param) || ((account || account_from_login(login_param_value)) && allow_resending_verify_account_email?)
248
252
  links << [30, verify_account_resend_path, verify_account_resend_link_text]
249
253
  end
250
254
  links
251
255
  end
252
256
 
253
- def verify_account_email_recently_sent?
254
- (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within)
255
- end
256
-
257
257
  attr_reader :verify_account_key_value
258
258
 
259
259
  def before_login_attempt
@@ -4,6 +4,7 @@ module Rodauth
4
4
  Feature.define(:webauthn_autofill, :WebauthnAutofill) do
5
5
  depends :webauthn_login
6
6
 
7
+ auth_value_method :webauthn_autofill?, true
7
8
  auth_value_method :webauthn_autofill_js, File.binread(File.expand_path('../../../../javascript/webauthn_autofill.js', __FILE__)).freeze
8
9
 
9
10
  translatable_method :webauthn_invalid_webauthn_id_message, "no webauthn key with given id found"
@@ -37,7 +38,7 @@ module Rodauth
37
38
 
38
39
  def _login_form_footer
39
40
  footer = super
40
- footer += render("webauthn-autofill") unless valid_login_entered?
41
+ footer += render("webauthn-autofill") if webauthn_autofill? && !valid_login_entered?
41
42
  footer
42
43
  end
43
44
 
@@ -74,7 +74,7 @@ module Rodauth
74
74
  end
75
75
 
76
76
  def account_from_webauthn_login
77
- account_from_login(param(login_param))
77
+ account_from_login(login_param_value)
78
78
  end
79
79
 
80
80
  def webauthn_login_options?
@@ -6,7 +6,7 @@ module Rodauth
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 36
9
+ MINOR = 38
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
data/lib/rodauth.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'securerandom'
4
4
 
5
5
  module Rodauth
6
+ class ConfigurationError < StandardError; end
7
+
6
8
  def self.lib(opts={}, &block)
7
9
  require 'roda'
8
10
  c = Class.new(Roda)
@@ -402,7 +404,11 @@ module Rodauth
402
404
  end
403
405
 
404
406
  module InstanceMethods
405
- def rodauth(name=nil)
407
+ def default_rodauth_name
408
+ nil
409
+ end
410
+
411
+ def rodauth(name=default_rodauth_name)
406
412
  if name
407
413
  (@_rodauths ||= {})[name] ||= self.class.rodauth(name).new(self)
408
414
  else
@@ -440,7 +446,7 @@ module Rodauth
440
446
  end
441
447
 
442
448
  module RequestMethods
443
- def rodauth(name=nil)
449
+ def rodauth(name=scope.default_rodauth_name)
444
450
  scope.rodauth(name).route!
445
451
  end
446
452
  end