rodauth 2.46.0 → 2.48.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rodauth/features/active_sessions.rb +2 -2
  3. data/lib/rodauth/features/argon2.rb +6 -6
  4. data/lib/rodauth/features/audit_logging.rb +4 -3
  5. data/lib/rodauth/features/base.rb +84 -30
  6. data/lib/rodauth/features/change_password.rb +11 -2
  7. data/lib/rodauth/features/confirm_password.rb +1 -1
  8. data/lib/rodauth/features/create_account.rb +2 -1
  9. data/lib/rodauth/features/disallow_password_reuse.rb +2 -2
  10. data/lib/rodauth/features/email_auth.rb +13 -4
  11. data/lib/rodauth/features/email_base.rb +36 -1
  12. data/lib/rodauth/features/http_basic_auth.rb +6 -1
  13. data/lib/rodauth/features/internal_request.rb +4 -0
  14. data/lib/rodauth/features/json.rb +5 -1
  15. data/lib/rodauth/features/jwt.rb +9 -3
  16. data/lib/rodauth/features/jwt_cors.rb +2 -0
  17. data/lib/rodauth/features/jwt_refresh.rb +33 -18
  18. data/lib/rodauth/features/lockout.rb +24 -21
  19. data/lib/rodauth/features/login.rb +12 -4
  20. data/lib/rodauth/features/login_password_requirements_base.rb +43 -13
  21. data/lib/rodauth/features/otp.rb +21 -12
  22. data/lib/rodauth/features/otp_unlock.rb +2 -2
  23. data/lib/rodauth/features/password_complexity.rb +2 -2
  24. data/lib/rodauth/features/password_expiration.rb +0 -1
  25. data/lib/rodauth/features/password_grace_period.rb +8 -0
  26. data/lib/rodauth/features/password_pepper.rb +11 -1
  27. data/lib/rodauth/features/recovery_codes.rb +4 -1
  28. data/lib/rodauth/features/remember.rb +4 -2
  29. data/lib/rodauth/features/require_domain.rb +12 -0
  30. data/lib/rodauth/features/require_hmac_secret.rb +12 -0
  31. data/lib/rodauth/features/reset_password.rb +25 -20
  32. data/lib/rodauth/features/single_session.rb +6 -2
  33. data/lib/rodauth/features/sms_codes.rb +4 -1
  34. data/lib/rodauth/features/two_factor_base.rb +2 -1
  35. data/lib/rodauth/features/update_password_hash.rb +5 -1
  36. data/lib/rodauth/features/verify_account.rb +29 -20
  37. data/lib/rodauth/features/verify_login_change.rb +19 -11
  38. data/lib/rodauth/features/webauthn.rb +16 -11
  39. data/lib/rodauth/features/webauthn_login.rb +1 -10
  40. data/lib/rodauth/features/webauthn_verify_account.rb +0 -4
  41. data/lib/rodauth/version.rb +1 -1
  42. data/lib/rodauth.rb +14 -12
  43. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f2f00f0b0ca5043b852e3f544a666c066b5df09445a4d0031351e37d101bdbf
4
- data.tar.gz: 387de7af25a3d6f4823cc09818d374ff99f1a35ffe8a0329efe107810667cdd6
3
+ metadata.gz: b9a5a66fe82750a982d493fd5e6558b4fef6f94f1761257b3242ff53014eb81b
4
+ data.tar.gz: ecd3f766f3b4d96acd2c7e651d71d1ad0cbb4e60e49db3d5e396a304866b733f
5
5
  SHA512:
6
- metadata.gz: 26088c4dc223cdc9f72a07a0de27bff3a3678b29264a46554733d103106443988ac524553f8afa4cb7a457d0cf51c25646adbdf8bd158a7488db449dfc09f7ee
7
- data.tar.gz: da69371a181d62e7ebb74274c56503565e64be26a808dede0df3f7b7c1e230efa5442f35d99609c138439703d2f97335968a0ee720c83c9819ae0de363269046
6
+ metadata.gz: 4471aa257cc7761633fca706db570b61cf4315f22d512c90a2419666f2c1bfd97647196a71fa8fd0d85f1cf16cede1beb69dce20ba6d98a29c52727aa6342c37
7
+ data.tar.gz: 0c3ac3e520ab620528ec8dd125cdee8092adea4ad6a2e1c877e30a10598219d0ad8abcb15eb40e1509561587323d8f05f653b68b3d7d112bd2cf44c9a6b8e6e5
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rodauth
4
4
  Feature.define(:active_sessions, :ActiveSessions) do
5
- depends :logout
5
+ depends :logout, :require_hmac_secret
6
6
 
7
7
  error_flash 'This session has been logged out'
8
8
  redirect
@@ -235,7 +235,7 @@ module Rodauth
235
235
 
236
236
  def active_sessions_ds
237
237
  db[active_sessions_table].
238
- where(active_sessions_account_id_column=>session_value || account_id)
238
+ where(active_sessions_account_id_column=>account_id_or_session_value)
239
239
  end
240
240
 
241
241
  def use_date_arithmetic?
@@ -2,11 +2,11 @@
2
2
 
3
3
  require 'argon2'
4
4
 
5
- # :nocov:
5
+ # simplecov:disable
6
6
  if !defined?(Argon2::VERSION) || Argon2::VERSION < '2'
7
7
  raise LoadError, "argon2 version 1.x not supported as it does not support argon2id hashes"
8
8
  end
9
- # :nocov:
9
+ # simplecov:enable
10
10
 
11
11
  module Rodauth
12
12
  Feature.define(:argon2, :Argon2) do
@@ -36,12 +36,12 @@ module Rodauth
36
36
  def argon2_salt_option
37
37
  :salt_do_not_supply
38
38
  end
39
- # :nocov:
39
+ # simplecov:disable
40
40
  else
41
41
  def argon2_salt_option
42
42
  :salt_for_testing_purposes_only
43
43
  end
44
- # :nocov:
44
+ # simplecov:enable
45
45
  end
46
46
 
47
47
  def password_hash_cost
@@ -78,7 +78,7 @@ module Rodauth
78
78
  def argon2_hash_cost
79
79
  { t_cost: 1, m_cost: 5, p_cost: 1 }
80
80
  end
81
- # :nocov:
81
+ # simplecov:disable
82
82
  else
83
83
  def argon2_hash_cost
84
84
  { t_cost: 2, m_cost: 16, p_cost: 1 }
@@ -102,7 +102,7 @@ module Rodauth
102
102
  end
103
103
  end
104
104
  end
105
- # :nocov:
105
+ # simplecov:enable
106
106
 
107
107
  def argon2_hash_algorithm?(hash)
108
108
  hash.start_with?('$argon2id$')
@@ -30,8 +30,9 @@ module Rodauth
30
30
 
31
31
  def hook_action(hook_type, action)
32
32
  super
33
+
33
34
  # In after_logout, session is already cleared, so use before_logout in that case
34
- if (hook_type == :after || action == :logout) && (id = account ? account_id : session_value)
35
+ if (hook_type == :after || action == :logout) && (id = account_id_or_session_value)
35
36
  add_audit_log(id, action)
36
37
  end
37
38
  end
@@ -82,9 +83,9 @@ module Rodauth
82
83
 
83
84
  def audit_log_ds
84
85
  ds = db[audit_logging_table]
85
- # :nocov:
86
+ # simplecov:disable
86
87
  if db.database_type == :postgres
87
- # :nocov:
88
+ # simplecov:enable
88
89
  # For PostgreSQL, use RETURNING NULL. This allows the feature
89
90
  # to be used with INSERT but not SELECT permissions on the
90
91
  # table, useful for audit logging where the database user
@@ -2,7 +2,7 @@
2
2
 
3
3
  begin
4
4
  require "rack/version"
5
- # :nocov:
5
+ # simplecov:disable
6
6
  rescue LoadError
7
7
  require "rack"
8
8
  else
@@ -13,7 +13,7 @@ else
13
13
  require "rack"
14
14
  end
15
15
  end
16
- # :nocov:
16
+ # simplecov:enable
17
17
 
18
18
  module Rodauth
19
19
  Feature.define(:base, :Base) do
@@ -68,6 +68,7 @@ module Rodauth
68
68
  auth_value_method :prefix, ''
69
69
  auth_value_method :session_key_prefix, nil
70
70
  auth_value_method :require_bcrypt?, true
71
+ auth_value_method :return_to_path_max_size, 2048
71
72
  auth_value_method :mark_input_fields_as_required?, true
72
73
  auth_value_method :mark_input_fields_with_autocomplete?, true
73
74
  auth_value_method :mark_input_fields_with_inputmode?, true
@@ -101,6 +102,7 @@ module Rodauth
101
102
 
102
103
  auth_methods(
103
104
  :account_id,
105
+ :account_reload,
104
106
  :account_session_value,
105
107
  :already_logged_in,
106
108
  :authenticated?,
@@ -115,6 +117,7 @@ module Rodauth
115
117
  :inputmode_for_field?,
116
118
  :logged_in?,
117
119
  :login_required,
120
+ :missing_recommended_configuration,
118
121
  :normalize_login,
119
122
  :null_byte_parameter_value,
120
123
  :open_account?,
@@ -130,7 +133,8 @@ module Rodauth
130
133
  :set_error_reason,
131
134
  :set_title,
132
135
  :translate,
133
- :update_session
136
+ :update_session,
137
+ :valid_return_to_path?
134
138
  )
135
139
 
136
140
  auth_private_methods(
@@ -164,7 +168,9 @@ module Rodauth
164
168
  :@current_route,
165
169
  :@field_errors,
166
170
  :@password_field_autocomplete_value,
167
- :@has_password
171
+ :@has_password,
172
+ :@account_retrieval_type,
173
+ :@existing_password_hash_or_salt
168
174
  )
169
175
 
170
176
  attr_reader :scope
@@ -309,7 +315,7 @@ module Rodauth
309
315
  alias logged_in? session_value
310
316
 
311
317
  def account_from_login(login)
312
- @account = _account_from_login(login)
318
+ set_account(_account_from_login(login), :login)
313
319
  end
314
320
 
315
321
  def open_account?
@@ -337,10 +343,12 @@ module Rodauth
337
343
  end
338
344
 
339
345
  def check_already_logged_in
346
+ # RODAUTH3: Move to login feature
340
347
  already_logged_in if logged_in?
341
348
  end
342
349
 
343
350
  def already_logged_in
351
+ # RODAUTH3: Move to login feature, halt or redirect by default
344
352
  nil
345
353
  end
346
354
 
@@ -414,15 +422,15 @@ module Rodauth
414
422
  end
415
423
 
416
424
  def account!
417
- account || (session_value && account_from_session)
425
+ @account_retrieval_type ? @account : (session_value && account_from_session)
418
426
  end
419
427
 
420
428
  def account_from_session
421
- @account = _account_from_session
429
+ set_account(_account_from_session, :session)
422
430
  end
423
431
 
424
432
  def account_from_id(id, status_id=nil)
425
- @account = _account_from_id(id, status_id)
433
+ set_account(_account_from_id(id, status_id), :id)
426
434
  end
427
435
 
428
436
  def check_csrf
@@ -435,9 +443,9 @@ module Rodauth
435
443
  if use_request_specific_csrf_tokens?
436
444
  scope.csrf_tag(path)
437
445
  else
438
- # :nocov:
446
+ # simplecov:disable
439
447
  scope.csrf_tag
440
- # :nocov:
448
+ # simplecov:enable
441
449
  end
442
450
  end
443
451
 
@@ -486,7 +494,7 @@ module Rodauth
486
494
  end
487
495
 
488
496
  def password_match?(password)
489
- if hash = get_password_hash
497
+ if @existing_password_hash_or_salt = hash = get_password_hash
490
498
  if account_password_hash_column || !use_database_authentication_functions?
491
499
  password_hash_match?(hash, password)
492
500
  else
@@ -579,7 +587,7 @@ module Rodauth
579
587
 
580
588
  def has_password?
581
589
  return @has_password unless @has_password.nil?
582
- return false unless account || session_value
590
+ return false unless account_id_or_session_value
583
591
  @has_password = !!get_password_hash
584
592
  end
585
593
 
@@ -599,6 +607,14 @@ module Rodauth
599
607
  s
600
608
  end
601
609
 
610
+ def account_id_or_session_value
611
+ if @account
612
+ account_id
613
+ elsif !@account_retrieval_type
614
+ session_value
615
+ end
616
+ end
617
+
602
618
  if Rack.release >= '3'
603
619
  def set_response_header(key, value)
604
620
  response.headers[key] = value
@@ -607,7 +623,7 @@ module Rodauth
607
623
  def convert_response_header_key(key)
608
624
  key
609
625
  end
610
- # :nocov:
626
+ # simplecov:disable
611
627
  else
612
628
  def set_response_header(key, value)
613
629
  response.headers[convert_response_header_key(key)] = value
@@ -627,19 +643,20 @@ module Rodauth
627
643
  Content-Type
628
644
  Content-Length
629
645
  WWW-Authenticate
646
+ Vary
630
647
  END
631
648
  mixed_case_headers.freeze
632
649
  define_method(:convert_response_header_key) do |key|
633
650
  mixed_case_headers.fetch(key, key)
634
651
  end
635
652
  end
636
- # :nocov:
653
+ # simplecov:enable
637
654
 
638
655
  if RUBY_VERSION >= '2.1'
639
656
  def button_fixed_locals
640
657
  '(value:, opts:)'
641
658
  end
642
- # :nocov:
659
+ # simplecov:disable
643
660
  else
644
661
  # Work on Ruby 2.0 when using Tilt 2.6+, as Ruby 2.0 does
645
662
  # not support required keyword arguments.
@@ -647,7 +664,28 @@ module Rodauth
647
664
  '(value: nil, opts: nil)'
648
665
  end
649
666
  end
650
- # :nocov:
667
+ # simplecov:enable
668
+
669
+ def missing_recommended_configuration(msg)
670
+ # RODAUTH3: switch to raise ConfigurationError
671
+ warn msg
672
+ end
673
+
674
+ def account_reload(current_retrieval_type, new_retrieval_type)
675
+ return unless @current_route
676
+
677
+ if current_retrieval_type == new_retrieval_type
678
+ warn "account retrieved multiple times during rodauth route with retrieval type #{current_retrieval_type}"
679
+ else
680
+ raise Error, "account retrieved multiple times during rodauth route, original retrieval type: #{current_retrieval_type}, new retrieval type: #{new_retrieval_type}"
681
+ end
682
+ end
683
+
684
+ def set_account(account, retrieval_type)
685
+ account_reload(@account_retrieval_type, retrieval_type) if @account_retrieval_type
686
+ @account_retrieval_type = retrieval_type
687
+ @account = account
688
+ end
651
689
 
652
690
  def database_function_password_match?(name, hash_id, password, salt)
653
691
  db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
@@ -789,9 +827,9 @@ module Rodauth
789
827
  when :postgres, :mysql, :mssql
790
828
  true
791
829
  else
792
- # :nocov:
830
+ # simplecov:disable
793
831
  false
794
- # :nocov:
832
+ # simplecov:enable
795
833
  end
796
834
  end
797
835
 
@@ -805,9 +843,9 @@ module Rodauth
805
843
 
806
844
  def function_name(name)
807
845
  if db.database_type == :mssql
808
- # :nocov:
846
+ # simplecov:disable
809
847
  "dbo.#{name}"
810
- # :nocov:
848
+ # simplecov:enable
811
849
  else
812
850
  name
813
851
  end
@@ -823,14 +861,18 @@ module Rodauth
823
861
  if account_password_hash_column
824
862
  account[account_password_hash_column] if account!
825
863
  elsif use_database_authentication_functions?
826
- db.get(Sequel.function(function_name(:rodauth_get_salt), account ? account_id : session_value))
864
+ db.get(Sequel.function(function_name(:rodauth_get_salt), account_id_or_session_value))
827
865
  else
828
- # :nocov:
866
+ # simplecov:disable
829
867
  password_hash_ds.get(password_hash_column)
830
- # :nocov:
868
+ # simplecov:enable
831
869
  end
832
870
  end
833
871
 
872
+ def new_password_matches_current_password?(password)
873
+ password_match?(password)
874
+ end
875
+
834
876
  def _account_from_login(login)
835
877
  ds = account_table_ds.where(login_column=>login)
836
878
  ds = ds.select(*account_select) if account_select
@@ -895,7 +937,7 @@ module Rodauth
895
937
  end
896
938
 
897
939
  def password_hash_ds
898
- db[password_hash_table].where(password_hash_id_column=>account ? account_id : session_value)
940
+ db[password_hash_table].where(password_hash_id_column=>account_id_or_session_value)
899
941
  end
900
942
 
901
943
  # This is needed for jdbc/sqlite, which returns timestamp columns as strings
@@ -934,9 +976,9 @@ module Rodauth
934
976
  # UniqueConstraintViolation.
935
977
  def unique_constraint_violation_class
936
978
  if db.adapter_scheme == :jdbc && db.database_type == :sqlite
937
- # :nocov:
979
+ # simplecov:disable
938
980
  Sequel::ConstraintViolation
939
- # :nocov:
981
+ # simplecov:enable
940
982
  else
941
983
  Sequel::UniqueConstraintViolation
942
984
  end
@@ -952,9 +994,9 @@ module Rodauth
952
994
  # CURRENT_TIMESTAMP.
953
995
  def set_deadline_value(hash, column, interval)
954
996
  if set_deadline_values?
955
- # :nocov:
997
+ # simplecov:disable
956
998
  hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval)
957
- # :nocov:
999
+ # simplecov:enable
958
1000
  end
959
1001
  end
960
1002
 
@@ -968,9 +1010,21 @@ module Rodauth
968
1010
  false
969
1011
  end
970
1012
 
971
- # :nocov:
1013
+ def valid_return_to_path?(path)
1014
+ path[0] == "/" &&
1015
+ path[1] != "/" &&
1016
+ path.bytesize <= return_to_path_max_size
1017
+ end
1018
+
1019
+ def set_session_return_to_path(session_key, path=request.fullpath)
1020
+ if request.get? && valid_return_to_path?(path)
1021
+ set_session_value(session_key, path)
1022
+ end
1023
+ end
1024
+
1025
+ # simplecov:disable
972
1026
  if RUBY_VERSION >= '4.0'
973
- # :nocov:
1027
+ # simplecov:enable
974
1028
  def instance_variables_to_inspect
975
1029
  instance_variables.reject{|v| instance_variable_get(v).nil?}
976
1030
  end
@@ -17,6 +17,7 @@ module Rodauth
17
17
 
18
18
  translatable_method :new_password_label, 'New Password'
19
19
  auth_value_method :new_password_param, 'new-password'
20
+ translatable_method :concurrent_password_update_message, 'a concurrent password update prevented the password change'
20
21
 
21
22
  auth_value_methods(
22
23
  :change_password_requires_password?,
@@ -38,15 +39,19 @@ module Rodauth
38
39
  if change_password_requires_password? && !password_match?(param(password_param))
39
40
  throw_error_reason(:invalid_previous_password, invalid_password_error_status, password_param, invalid_previous_password_message)
40
41
  end
42
+ existing_hash = @existing_password_hash_or_salt || get_password_hash
41
43
 
42
44
  password = param(new_password_param)
43
45
  if require_password_confirmation? && password != param(password_confirm_param)
44
46
  throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, new_password_param, passwords_do_not_match_message)
45
47
  end
46
48
 
47
- if password_match?(password)
49
+ if new_password_matches_current_password?(password)
48
50
  throw_error_reason(:same_as_existing_password, invalid_field_error_status, new_password_param, same_as_existing_password_message)
49
51
  end
52
+ # Don't allow same as existing password check for new password to override
53
+ # known existing password hash/salt
54
+ @existing_password_hash_or_salt = existing_hash
50
55
 
51
56
  unless password_meets_requirements?(password)
52
57
  throw_error_status(invalid_field_error_status, new_password_param, password_does_not_meet_requirements_message)
@@ -54,7 +59,11 @@ module Rodauth
54
59
 
55
60
  transaction do
56
61
  before_change_password
57
- set_password(password)
62
+ begin
63
+ set_password(password)
64
+ rescue Rodauth::SetPasswordFailure
65
+ throw_error_reason(:concurrent_password_update, invalid_password_error_status, new_password_param, concurrent_password_update_message)
66
+ end
58
67
  clear_tokens(:change_password)
59
68
  after_change_password
60
69
  end
@@ -55,7 +55,7 @@ module Rodauth
55
55
  set_redirect_error_status(password_authentication_required_error_status)
56
56
  set_error_reason :password_authentication_required
57
57
  set_redirect_error_flash password_authentication_required_error_flash
58
- set_session_value(confirm_password_redirect_session_key, request.fullpath)
58
+ set_session_return_to_path(confirm_password_redirect_session_key)
59
59
  redirect password_authentication_required_redirect
60
60
  end
61
61
  end
@@ -94,7 +94,8 @@ module Rodauth
94
94
  end
95
95
 
96
96
  def new_account(login)
97
- @account = _new_account(login)
97
+ @account_retrieval_type = nil
98
+ set_account(_new_account(login), :new)
98
99
  end
99
100
 
100
101
  def save_account
@@ -59,11 +59,11 @@ module Rodauth
59
59
  database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt)
60
60
  end
61
61
  else
62
- # :nocov:
62
+ # simplecov:disable
63
63
  previous_password_ds.select_map(previous_password_hash_column).any? do |hash|
64
64
  password_hash_match?(hash, password)
65
65
  end
66
- # :nocov:
66
+ # simplecov:enable
67
67
  end
68
68
 
69
69
  return true unless match
@@ -89,8 +89,17 @@ module Rodauth
89
89
  end
90
90
 
91
91
  r.post do
92
- key = session[email_auth_session_key] || param(email_auth_key_param)
93
- unless account_from_email_auth_key(key)
92
+ key = session_param(email_auth_session_key, email_auth_key_param)
93
+ remove_session_value(email_auth_session_key)
94
+ select_key_for_update!
95
+
96
+ valid = transaction do
97
+ if key && account_from_email_auth_key(key)
98
+ remove_email_auth_key == 1
99
+ end
100
+ end
101
+
102
+ unless valid
94
103
  set_redirect_error_status(invalid_key_error_status)
95
104
  set_error_reason :invalid_email_auth_key
96
105
  set_redirect_error_flash email_auth_error_flash
@@ -131,7 +140,7 @@ module Rodauth
131
140
  end
132
141
 
133
142
  def account_from_email_auth_key(key)
134
- @account = _account_from_email_auth_key(key)
143
+ set_account(_account_from_email_auth_key(key), :email_auth)
135
144
  end
136
145
 
137
146
  def email_auth_email_link
@@ -141,7 +150,7 @@ module Rodauth
141
150
  def get_email_auth_key(id)
142
151
  ds = email_auth_ds(id)
143
152
  ds.where(Sequel::CURRENT_TIMESTAMP > email_auth_deadline_column).delete
144
- ds.get(email_auth_key_column)
153
+ apply_key_for_update(ds).get(email_auth_key_column)
145
154
  end
146
155
 
147
156
  def email_auth_request_form
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Rodauth
4
4
  Feature.define(:email_base, :EmailBase) do
5
+ depends :require_domain, :require_hmac_secret
6
+
5
7
  translatable_method :email_subject_prefix, ''
6
8
  auth_value_method :require_mail?, true
7
9
  auth_value_method :allow_raw_email_token?, false
@@ -9,7 +11,8 @@ module Rodauth
9
11
  redirect :default_post_email
10
12
 
11
13
  auth_value_methods(
12
- :email_from
14
+ :email_from,
15
+ :allow_param_fallback_for_session_param?
13
16
  )
14
17
 
15
18
  auth_methods(
@@ -18,6 +21,10 @@ module Rodauth
18
21
  :send_email
19
22
  )
20
23
 
24
+ uses_instance_variables(
25
+ :@key_for_update
26
+ )
27
+
21
28
  def post_configure
22
29
  super
23
30
  require 'mail' if require_mail?
@@ -58,10 +65,38 @@ module Rodauth
58
65
  "#{account_id}#{token_separator}#{convert_email_token_key(key)}"
59
66
  end
60
67
 
68
+ def session_param(session_key, param_name)
69
+ unless value = session[session_key]
70
+ if allow_param_fallback_for_session_param?
71
+ value = param(param_name)
72
+ end
73
+ end
74
+
75
+ value
76
+ end
77
+
78
+ def allow_param_fallback_for_session_param?
79
+ return super if defined?(super)
80
+ false
81
+ end
82
+
61
83
  def convert_email_token_key(key)
62
84
  convert_token_key(key)
63
85
  end
64
86
 
87
+ def select_key_for_update!
88
+ @key_for_update = true
89
+ end
90
+
91
+ def apply_key_for_update(ds)
92
+ if @key_for_update
93
+ @key_for_update = nil
94
+ ds.for_update
95
+ else
96
+ ds
97
+ end
98
+ end
99
+
65
100
  def account_from_key(token, status_id=nil)
66
101
  id, key = split_token(token)
67
102
  return unless key && (id = convert_token_id(id))
@@ -4,13 +4,14 @@ module Rodauth
4
4
  Feature.define(:http_basic_auth, :HttpBasicAuth) do
5
5
  auth_value_method :http_basic_auth_realm, "protected"
6
6
  auth_value_method :require_http_basic_auth?, false
7
+ auth_value_method :logged_in_fallback_to_http_basic_auth?, true # RODAUTH3: false
7
8
 
8
9
  uses_instance_variables(:@checked_http_basic_auth)
9
10
 
10
11
  def logged_in?
11
12
  ret = super
12
13
 
13
- if !ret && @checked_http_basic_auth.nil?
14
+ if !ret && logged_in_fallback_to_http_basic_auth? && @checked_http_basic_auth.nil?
14
15
  http_basic_auth
15
16
  ret = super
16
17
  end
@@ -33,6 +34,10 @@ module Rodauth
33
34
  end
34
35
  end
35
36
 
37
+ def account_from_session
38
+ @checked_http_basic_auth ? @account : super
39
+ end
40
+
36
41
  def http_basic_auth
37
42
  unless @checked_http_basic_auth.nil?
38
43
  return (@checked_http_basic_auth ? true : nil)
@@ -120,6 +120,10 @@ module Rodauth
120
120
 
121
121
  private
122
122
 
123
+ def allow_param_fallback_for_session_param?
124
+ true
125
+ end
126
+
123
127
  def internal_request?
124
128
  true
125
129
  end
@@ -8,7 +8,7 @@ module Rodauth
8
8
  translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
9
9
  auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
10
10
  auth_value_method :json_check_accept?, true
11
- auth_value_method :json_request_content_type_regexp, /\bapplication\/(?:vnd\.api\+)?json\b/i
11
+ auth_value_method :json_request_content_type_regexp, /\Aapplication\/(?:vnd\.api\+)?json\b/i
12
12
  auth_value_method :json_response_content_type, 'application/json'
13
13
  auth_value_method :json_response_custom_error_status?, true
14
14
  auth_value_method :json_response_error_status, 400
@@ -81,6 +81,10 @@ module Rodauth
81
81
  super
82
82
  end
83
83
 
84
+ def allow_param_fallback_for_session_param?
85
+ use_json?
86
+ end
87
+
84
88
  def _set_otp_unlock_info
85
89
  if use_json?
86
90
  json_response[:num_successes] = otp_unlock_num_successes
@@ -113,12 +113,12 @@ module Rodauth
113
113
  secrets.compact!
114
114
  secrets
115
115
  end
116
- # :nocov:
116
+ # simplecov:disable
117
117
  else
118
118
  def _jwt_decode_secrets
119
119
  jwt_secret
120
120
  end
121
- # :nocov:
121
+ # simplecov:enable
122
122
  end
123
123
 
124
124
  def jwt_payload
@@ -145,7 +145,13 @@ module Rodauth
145
145
  end
146
146
 
147
147
  def return_json_response
148
- set_jwt
148
+ case response.status
149
+ when 405, 406
150
+ response.headers.delete('authorization')
151
+ else
152
+ set_jwt
153
+ end
154
+
149
155
  super
150
156
  end
151
157