rodauth 2.36.0 → 2.38.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rodauth/features/base.rb +52 -9
- data/lib/rodauth/features/change_login.rb +2 -2
- data/lib/rodauth/features/create_account.rb +2 -2
- data/lib/rodauth/features/email_auth.rb +5 -5
- data/lib/rodauth/features/internal_request.rb +4 -4
- data/lib/rodauth/features/json.rb +5 -0
- data/lib/rodauth/features/jwt.rb +7 -11
- data/lib/rodauth/features/lockout.rb +5 -5
- data/lib/rodauth/features/login.rb +1 -1
- data/lib/rodauth/features/login_password_requirements_base.rb +13 -0
- data/lib/rodauth/features/reset_password.rb +5 -5
- data/lib/rodauth/features/sms_codes.rb +1 -1
- data/lib/rodauth/features/two_factor_base.rb +6 -13
- data/lib/rodauth/features/verify_account.rb +6 -6
- data/lib/rodauth/features/webauthn_autofill.rb +2 -1
- data/lib/rodauth/features/webauthn_login.rb +1 -1
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +8 -2
- metadata +3 -261
- data/CHANGELOG +0 -521
- data/README.rdoc +0 -1555
- data/doc/account_expiration.rdoc +0 -41
- data/doc/active_sessions.rdoc +0 -56
- data/doc/argon2.rdoc +0 -54
- data/doc/audit_logging.rdoc +0 -44
- data/doc/base.rdoc +0 -123
- data/doc/change_login.rdoc +0 -25
- data/doc/change_password.rdoc +0 -26
- data/doc/change_password_notify.rdoc +0 -14
- data/doc/close_account.rdoc +0 -26
- data/doc/confirm_password.rdoc +0 -32
- data/doc/create_account.rdoc +0 -27
- data/doc/disallow_common_passwords.rdoc +0 -17
- data/doc/disallow_password_reuse.rdoc +0 -30
- data/doc/email_auth.rdoc +0 -55
- data/doc/email_base.rdoc +0 -18
- data/doc/error_reasons.rdoc +0 -77
- data/doc/guides/admin_activation.rdoc +0 -46
- data/doc/guides/already_authenticated.rdoc +0 -10
- data/doc/guides/alternative_login.rdoc +0 -46
- data/doc/guides/change_table_and_column_names.rdoc +0 -19
- data/doc/guides/create_account_programmatically.rdoc +0 -38
- data/doc/guides/delay_password.rdoc +0 -25
- data/doc/guides/email_only.rdoc +0 -16
- data/doc/guides/i18n.rdoc +0 -29
- data/doc/guides/internals.rdoc +0 -233
- data/doc/guides/links.rdoc +0 -12
- data/doc/guides/login_return.rdoc +0 -37
- data/doc/guides/migrate_password_hash_algorithm.rdoc +0 -15
- data/doc/guides/password_column.rdoc +0 -25
- data/doc/guides/password_confirmation.rdoc +0 -37
- data/doc/guides/password_requirements.rdoc +0 -43
- data/doc/guides/paths.rdoc +0 -51
- data/doc/guides/query_params.rdoc +0 -9
- data/doc/guides/redirects.rdoc +0 -17
- data/doc/guides/registration_field.rdoc +0 -68
- data/doc/guides/render_confirmation.rdoc +0 -17
- data/doc/guides/require_mfa.rdoc +0 -30
- data/doc/guides/reset_password_autologin.rdoc +0 -21
- data/doc/guides/share_configuration.rdoc +0 -34
- data/doc/guides/status_column.rdoc +0 -28
- data/doc/guides/totp_or_recovery.rdoc +0 -16
- data/doc/http_basic_auth.rdoc +0 -18
- data/doc/internal_request.rdoc +0 -539
- data/doc/json.rdoc +0 -56
- data/doc/jwt.rdoc +0 -52
- data/doc/jwt_cors.rdoc +0 -22
- data/doc/jwt_refresh.rdoc +0 -58
- data/doc/lockout.rdoc +0 -73
- data/doc/login.rdoc +0 -39
- data/doc/login_password_requirements_base.rdoc +0 -44
- data/doc/logout.rdoc +0 -22
- data/doc/otp.rdoc +0 -93
- data/doc/otp_lockout_email.rdoc +0 -30
- data/doc/otp_modify_email.rdoc +0 -19
- data/doc/otp_unlock.rdoc +0 -58
- data/doc/password_complexity.rdoc +0 -34
- data/doc/password_expiration.rdoc +0 -38
- data/doc/password_grace_period.rdoc +0 -24
- data/doc/password_pepper.rdoc +0 -52
- data/doc/path_class_methods.rdoc +0 -10
- data/doc/recovery_codes.rdoc +0 -61
- data/doc/release_notes/1.0.0.txt +0 -443
- data/doc/release_notes/1.1.0.txt +0 -8
- data/doc/release_notes/1.10.0.txt +0 -80
- data/doc/release_notes/1.11.0.txt +0 -32
- data/doc/release_notes/1.12.0.txt +0 -61
- data/doc/release_notes/1.13.0.txt +0 -34
- data/doc/release_notes/1.14.0.txt +0 -19
- data/doc/release_notes/1.15.0.txt +0 -21
- data/doc/release_notes/1.16.0.txt +0 -31
- data/doc/release_notes/1.17.0.txt +0 -23
- data/doc/release_notes/1.18.0.txt +0 -26
- data/doc/release_notes/1.19.0.txt +0 -116
- data/doc/release_notes/1.2.0.txt +0 -18
- data/doc/release_notes/1.20.0.txt +0 -175
- data/doc/release_notes/1.21.0.txt +0 -12
- data/doc/release_notes/1.22.0.txt +0 -11
- data/doc/release_notes/1.23.0.txt +0 -32
- data/doc/release_notes/1.3.0.txt +0 -21
- data/doc/release_notes/1.4.0.txt +0 -11
- data/doc/release_notes/1.5.0.txt +0 -74
- data/doc/release_notes/1.6.0.txt +0 -37
- data/doc/release_notes/1.7.0.txt +0 -6
- data/doc/release_notes/1.8.0.txt +0 -14
- data/doc/release_notes/1.9.0.txt +0 -15
- data/doc/release_notes/2.0.0.txt +0 -361
- data/doc/release_notes/2.1.0.txt +0 -31
- data/doc/release_notes/2.10.0.txt +0 -47
- data/doc/release_notes/2.11.0.txt +0 -31
- data/doc/release_notes/2.12.0.txt +0 -17
- data/doc/release_notes/2.13.0.txt +0 -19
- data/doc/release_notes/2.14.0.txt +0 -17
- data/doc/release_notes/2.15.0.txt +0 -48
- data/doc/release_notes/2.16.0.txt +0 -20
- data/doc/release_notes/2.17.0.txt +0 -10
- data/doc/release_notes/2.18.0.txt +0 -27
- data/doc/release_notes/2.19.0.txt +0 -61
- data/doc/release_notes/2.2.0.txt +0 -39
- data/doc/release_notes/2.20.0.txt +0 -10
- data/doc/release_notes/2.21.0.txt +0 -28
- data/doc/release_notes/2.22.0.txt +0 -43
- data/doc/release_notes/2.23.0.txt +0 -15
- data/doc/release_notes/2.24.0.txt +0 -15
- data/doc/release_notes/2.25.0.txt +0 -8
- data/doc/release_notes/2.26.0.txt +0 -45
- data/doc/release_notes/2.27.0.txt +0 -35
- data/doc/release_notes/2.28.0.txt +0 -16
- data/doc/release_notes/2.29.0.txt +0 -27
- data/doc/release_notes/2.3.0.txt +0 -37
- data/doc/release_notes/2.30.0.txt +0 -15
- data/doc/release_notes/2.31.0.txt +0 -47
- data/doc/release_notes/2.32.0.txt +0 -65
- data/doc/release_notes/2.33.0.txt +0 -18
- data/doc/release_notes/2.34.0.txt +0 -36
- data/doc/release_notes/2.35.0.txt +0 -22
- data/doc/release_notes/2.36.0.txt +0 -35
- data/doc/release_notes/2.4.0.txt +0 -22
- data/doc/release_notes/2.5.0.txt +0 -20
- data/doc/release_notes/2.6.0.txt +0 -37
- data/doc/release_notes/2.7.0.txt +0 -33
- data/doc/release_notes/2.8.0.txt +0 -20
- data/doc/release_notes/2.9.0.txt +0 -21
- data/doc/remember.rdoc +0 -79
- data/doc/reset_password.rdoc +0 -66
- data/doc/reset_password_notify.rdoc +0 -17
- data/doc/session_expiration.rdoc +0 -28
- data/doc/single_session.rdoc +0 -37
- data/doc/sms_codes.rdoc +0 -138
- data/doc/two_factor_base.rdoc +0 -70
- data/doc/update_password_hash.rdoc +0 -7
- data/doc/verify_account.rdoc +0 -67
- data/doc/verify_account_grace_period.rdoc +0 -19
- data/doc/verify_login_change.rdoc +0 -59
- data/doc/webauthn.rdoc +0 -118
- data/doc/webauthn_autofill.rdoc +0 -19
- data/doc/webauthn_login.rdoc +0 -16
- data/doc/webauthn_modify_email.rdoc +0 -19
- data/doc/webauthn_verify_account.rdoc +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f79db7dec7147665538bf0b4f8e0c6c554d88396b294f19e5a5ed26543c31d1c
|
4
|
+
data.tar.gz: ea377861679a55895bc325b1cf3932970b0de9c773615ba2daecb5805704ae02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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
|
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 =
|
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
|
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 =
|
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
|
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(
|
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
|
227
|
-
raise InternalRequestError, "no account for login" unless account = account_from_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
|
233
|
-
_return_from_internal_request(!!account_from_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(_)
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -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
|
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
|
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(
|
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(
|
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(
|
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
|
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
|
-
|
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(
|
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(
|
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")
|
41
|
+
footer += render("webauthn-autofill") if webauthn_autofill? && !valid_login_entered?
|
41
42
|
footer
|
42
43
|
end
|
43
44
|
|
data/lib/rodauth/version.rb
CHANGED
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
|
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=
|
449
|
+
def rodauth(name=scope.default_rodauth_name)
|
444
450
|
scope.rodauth(name).route!
|
445
451
|
end
|
446
452
|
end
|