rodauth 2.0.0 → 2.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG +58 -0
- data/README.rdoc +14 -0
- data/doc/base.rdoc +2 -0
- data/doc/guides/admin_activation.rdoc +46 -0
- data/doc/guides/already_authenticated.rdoc +10 -0
- data/doc/guides/alternative_login.rdoc +46 -0
- data/doc/guides/create_account_programmatically.rdoc +38 -0
- data/doc/guides/delay_password.rdoc +25 -0
- data/doc/guides/email_only.rdoc +16 -0
- data/doc/guides/i18n.rdoc +26 -0
- data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
- data/doc/guides/links.rdoc +12 -0
- data/doc/guides/login_return.rdoc +37 -0
- data/doc/guides/password_column.rdoc +25 -0
- data/doc/guides/password_confirmation.rdoc +37 -0
- data/doc/guides/password_requirements.rdoc +30 -0
- data/doc/guides/paths.rdoc +36 -0
- data/doc/guides/query_params.rdoc +9 -0
- data/doc/guides/redirects.rdoc +17 -0
- data/doc/guides/registration_field.rdoc +68 -0
- data/doc/guides/require_mfa.rdoc +30 -0
- data/doc/guides/reset_password_autologin.rdoc +21 -0
- data/doc/guides/status_column.rdoc +28 -0
- data/doc/guides/totp_or_recovery.rdoc +16 -0
- data/doc/jwt_refresh.rdoc +11 -0
- data/doc/login.rdoc +8 -0
- data/doc/login_password_requirements_base.rdoc +3 -0
- data/doc/otp.rdoc +1 -0
- data/doc/password_pepper.rdoc +44 -0
- data/doc/release_notes/2.1.0.txt +31 -0
- data/doc/release_notes/2.2.0.txt +39 -0
- data/doc/release_notes/2.3.0.txt +37 -0
- data/doc/release_notes/2.4.0.txt +22 -0
- data/doc/release_notes/2.5.0.txt +20 -0
- data/doc/verify_login_change.rdoc +1 -0
- data/lib/rodauth.rb +5 -5
- data/lib/rodauth/features/active_sessions.rb +5 -7
- data/lib/rodauth/features/audit_logging.rb +2 -0
- data/lib/rodauth/features/base.rb +21 -2
- data/lib/rodauth/features/change_password.rb +1 -1
- data/lib/rodauth/features/close_account.rb +8 -6
- data/lib/rodauth/features/create_account.rb +1 -0
- data/lib/rodauth/features/disallow_password_reuse.rb +4 -2
- data/lib/rodauth/features/email_auth.rb +2 -2
- data/lib/rodauth/features/http_basic_auth.rb +15 -2
- data/lib/rodauth/features/jwt.rb +12 -8
- data/lib/rodauth/features/jwt_cors.rb +15 -15
- data/lib/rodauth/features/jwt_refresh.rb +39 -10
- data/lib/rodauth/features/login.rb +23 -12
- data/lib/rodauth/features/login_password_requirements_base.rb +9 -4
- data/lib/rodauth/features/otp.rb +5 -1
- data/lib/rodauth/features/password_complexity.rb +4 -2
- data/lib/rodauth/features/password_pepper.rb +45 -0
- data/lib/rodauth/features/remember.rb +2 -0
- data/lib/rodauth/features/session_expiration.rb +1 -6
- data/lib/rodauth/features/single_session.rb +1 -1
- data/lib/rodauth/features/sms_codes.rb +0 -1
- data/lib/rodauth/features/two_factor_base.rb +4 -4
- data/lib/rodauth/features/verify_account.rb +5 -0
- data/lib/rodauth/features/verify_account_grace_period.rb +3 -5
- data/lib/rodauth/features/verify_login_change.rb +2 -1
- data/lib/rodauth/features/webauthn.rb +1 -3
- data/lib/rodauth/features/webauthn_login.rb +1 -1
- data/lib/rodauth/migrations.rb +16 -5
- data/lib/rodauth/version.rb +1 -1
- data/templates/password-field.str +1 -1
- metadata +37 -5
@@ -0,0 +1,37 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Configuration methods have been added for easier validation of
|
4
|
+
logins when logins must be valid email addresses (the default):
|
5
|
+
|
6
|
+
* login_valid_email?(login) can be used for full control of
|
7
|
+
determining whether the login is valid.
|
8
|
+
|
9
|
+
* login_email_regexp can be used to set the regexp used in the
|
10
|
+
default login_valid_email? check.
|
11
|
+
|
12
|
+
* login_not_valid_email_message can be used to set the field
|
13
|
+
error message if the login is not a valid email. Previously, this
|
14
|
+
value was hardcoded and not translatable.
|
15
|
+
|
16
|
+
* The {create,drop}_database_authentication_functions now work
|
17
|
+
correctly with uuid keys on PostgreSQL. All other parts of
|
18
|
+
Rodauth already worked correctly with uuid keys.
|
19
|
+
|
20
|
+
= Other Improvements
|
21
|
+
|
22
|
+
* The before_jwt_refresh_route hook is now called before the route
|
23
|
+
is taken. Previously, the configuration method had no effect.
|
24
|
+
|
25
|
+
* rodauth.login can now be used by external code to login the current
|
26
|
+
account (the account that rodauth.account returns). This should be
|
27
|
+
passed the authentication type string used to login, such as
|
28
|
+
password.
|
29
|
+
|
30
|
+
* The jwt_refresh route now returns an error for requests where a
|
31
|
+
valid access token for a logged in session is not provided. You
|
32
|
+
can use the jwt_refresh_without_access_token_message and
|
33
|
+
jwt_refresh_without_access_token_status configuration methods
|
34
|
+
to configure the error response.
|
35
|
+
|
36
|
+
* The new refresh token is now available to the after_refresh_token
|
37
|
+
hook by looking in json_response[jwt_refresh_token_key].
|
@@ -0,0 +1,22 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A password_pepper feature has been added. This allows you to use a
|
4
|
+
secret key (called a pepper) to append to passwords before hashing
|
5
|
+
and hash checking. Using this approach, if an attacker obtains the
|
6
|
+
password hash, it is unusable for cracking unless they can also
|
7
|
+
get access to the pepper.
|
8
|
+
|
9
|
+
The password_pepper feature also supports a list of previous peppers
|
10
|
+
that can be used to implement secret rotation and to support
|
11
|
+
compatibility with unpeppered passwords.
|
12
|
+
|
13
|
+
Rodauth by default uses database functions for password hash
|
14
|
+
checking on PostgreSQL, MySQL, and Microsoft SQL Server, which in
|
15
|
+
general provides more security than a password pepper, but both
|
16
|
+
approaches can be used simultaneously.
|
17
|
+
|
18
|
+
* A session_key_prefix configuration method has been added for
|
19
|
+
prefixing the values of all default session keys. This can be
|
20
|
+
useful if you are using multiple Rodauth configurations in the same
|
21
|
+
application and want to make sure the session keys for the separate
|
22
|
+
configurations do not overlap.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A login_return_to_requested_location_path configuration method has
|
4
|
+
been added to the login feature. This controls the path to redirect
|
5
|
+
to if using login_return_to_requested_location?. By default, this
|
6
|
+
is the same as the fullpath of the request that required login if
|
7
|
+
that request was a GET request, and nil if that request was not a
|
8
|
+
GET request. Previously, the fullpath of that request was used even
|
9
|
+
if it was not a GET request, which caused problems as browsers use a
|
10
|
+
GET request for redirects, and it is a bad idea to redirect to a path
|
11
|
+
that may not handle GET requests.
|
12
|
+
|
13
|
+
* A change_login_needs_verification_notice_flash configuration method
|
14
|
+
has been added to the verify_login_change feature, for allowing
|
15
|
+
translations when using the feature and not using the
|
16
|
+
change_login_notice_flash configuration method.
|
17
|
+
|
18
|
+
= Other Improvements
|
19
|
+
|
20
|
+
* new_password_label is now translatable.
|
@@ -14,6 +14,7 @@ control. Depends on the change login and email base features.
|
|
14
14
|
== Auth Value Methods
|
15
15
|
|
16
16
|
no_matching_verify_login_change_key_error_flash :: The flash error message to show when an invalid verify login change key is used.
|
17
|
+
change_login_needs_verification_notice_flash :: The flash notice to show after changing a login when using this feature, if +change_login_notice_flash+ is not overridden.
|
17
18
|
verify_login_change_additional_form_tags :: HTML fragment containing additional form tags to use on the verify login change form.
|
18
19
|
verify_login_change_autologin? :: Whether to autologin the user after successful login change verification, false by default.
|
19
20
|
verify_login_change_button :: The text to use for the verify login change button.
|
data/lib/rodauth.rb
CHANGED
@@ -119,7 +119,7 @@ module Rodauth
|
|
119
119
|
|
120
120
|
define_method(handle_meth) do
|
121
121
|
request.is send(route_meth) do
|
122
|
-
|
122
|
+
check_csrf if check_csrf?
|
123
123
|
before_rodauth
|
124
124
|
send(internal_handle_meth, request)
|
125
125
|
end
|
@@ -137,7 +137,9 @@ module Rodauth
|
|
137
137
|
feature.module_eval(&block)
|
138
138
|
configuration.def_configuration_methods(feature)
|
139
139
|
|
140
|
+
# :nocov:
|
140
141
|
if constant
|
142
|
+
# :nocov:
|
141
143
|
Rodauth.const_set(constant, feature)
|
142
144
|
Rodauth::FeatureConfiguration.const_set(constant, configuration)
|
143
145
|
end
|
@@ -336,10 +338,8 @@ module Rodauth
|
|
336
338
|
end
|
337
339
|
|
338
340
|
def freeze
|
339
|
-
|
340
|
-
|
341
|
-
opts[:rodauths].freeze
|
342
|
-
end
|
341
|
+
opts[:rodauths].each_value(&:freeze)
|
342
|
+
opts[:rodauths].freeze
|
343
343
|
super
|
344
344
|
end
|
345
345
|
end
|
@@ -118,14 +118,12 @@ module Rodauth
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def before_logout
|
121
|
-
if
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
remove_current_session
|
126
|
-
end
|
121
|
+
if param_or_nil(global_logout_param)
|
122
|
+
remove_all_active_sessions
|
123
|
+
else
|
124
|
+
remove_current_session
|
127
125
|
end
|
128
|
-
super
|
126
|
+
super
|
129
127
|
end
|
130
128
|
|
131
129
|
def session_inactivity_deadline_condition
|
@@ -82,7 +82,9 @@ module Rodauth
|
|
82
82
|
|
83
83
|
def audit_log_ds
|
84
84
|
ds = db[audit_logging_table]
|
85
|
+
# :nocov:
|
85
86
|
if db.database_type == :postgres
|
87
|
+
# :nocov:
|
86
88
|
# For PostgreSQL, use RETURNING NULL. This allows the feature
|
87
89
|
# to be used with INSERT but not SELECT permissions on the
|
88
90
|
# table, useful for audit logging where the database user
|
@@ -47,6 +47,7 @@ module Rodauth
|
|
47
47
|
session_key :authenticated_by_session_key, :authenticated_by
|
48
48
|
session_key :autologin_type_session_key, :autologin_type
|
49
49
|
auth_value_method :prefix, ''
|
50
|
+
auth_value_method :session_key_prefix, nil
|
50
51
|
auth_value_method :require_bcrypt?, true
|
51
52
|
auth_value_method :mark_input_fields_as_required?, true
|
52
53
|
auth_value_method :mark_input_fields_with_autocomplete?, true
|
@@ -82,6 +83,7 @@ module Rodauth
|
|
82
83
|
:already_logged_in,
|
83
84
|
:authenticated?,
|
84
85
|
:autocomplete_for_field?,
|
86
|
+
:check_csrf,
|
85
87
|
:clear_session,
|
86
88
|
:csrf_tag,
|
87
89
|
:function_name,
|
@@ -254,6 +256,10 @@ module Rodauth
|
|
254
256
|
Sequel::DATABASES.first
|
255
257
|
end
|
256
258
|
|
259
|
+
def password_field_autocomplete_value
|
260
|
+
@password_field_autocomplete_value || 'current-password'
|
261
|
+
end
|
262
|
+
|
257
263
|
# If the account_password_hash_column is set, the password hash is verified in
|
258
264
|
# ruby, it will not use a database function to do so, it will check the password
|
259
265
|
# hash using bcrypt.
|
@@ -333,6 +339,10 @@ module Rodauth
|
|
333
339
|
@account = _account_from_session
|
334
340
|
end
|
335
341
|
|
342
|
+
def check_csrf
|
343
|
+
scope.check_csrf!(check_csrf_opts, &check_csrf_block)
|
344
|
+
end
|
345
|
+
|
336
346
|
def csrf_tag(path=request.path)
|
337
347
|
return unless scope.respond_to?(:csrf_tag)
|
338
348
|
|
@@ -384,9 +394,9 @@ module Rodauth
|
|
384
394
|
def password_match?(password)
|
385
395
|
if hash = get_password_hash
|
386
396
|
if account_password_hash_column || !use_database_authentication_functions?
|
387
|
-
|
397
|
+
password_hash_match?(hash, password)
|
388
398
|
else
|
389
|
-
|
399
|
+
database_function_password_match?(:rodauth_valid_password_hash, account_id, password, hash)
|
390
400
|
end
|
391
401
|
end
|
392
402
|
end
|
@@ -449,6 +459,14 @@ module Rodauth
|
|
449
459
|
|
450
460
|
private
|
451
461
|
|
462
|
+
def database_function_password_match?(name, hash_id, password, salt)
|
463
|
+
db.get(Sequel.function(function_name(name), hash_id, BCrypt::Engine.hash_secret(password, salt)))
|
464
|
+
end
|
465
|
+
|
466
|
+
def password_hash_match?(hash, password)
|
467
|
+
BCrypt::Password.new(hash) == password
|
468
|
+
end
|
469
|
+
|
452
470
|
def convert_token_key(key)
|
453
471
|
if key && hmac_secret
|
454
472
|
compute_hmac(key)
|
@@ -484,6 +502,7 @@ module Rodauth
|
|
484
502
|
end
|
485
503
|
|
486
504
|
def convert_session_key(key)
|
505
|
+
key = "#{session_key_prefix}#{key}".to_sym if session_key_prefix
|
487
506
|
scope.opts[:sessions_convert_symbols] ? key.to_s : key
|
488
507
|
end
|
489
508
|
|
@@ -33,7 +33,11 @@ module Rodauth
|
|
33
33
|
end
|
34
34
|
|
35
35
|
r.post do
|
36
|
-
|
36
|
+
catch_error do
|
37
|
+
if close_account_requires_password? && !password_match?(param(password_param))
|
38
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
39
|
+
end
|
40
|
+
|
37
41
|
transaction do
|
38
42
|
before_close_account
|
39
43
|
close_account
|
@@ -46,12 +50,10 @@ module Rodauth
|
|
46
50
|
|
47
51
|
set_notice_flash close_account_notice_flash
|
48
52
|
redirect close_account_redirect
|
49
|
-
else
|
50
|
-
set_response_error_status(invalid_password_error_status)
|
51
|
-
set_field_error(password_param, invalid_password_message)
|
52
|
-
set_error_flash close_account_error_flash
|
53
|
-
close_account_view
|
54
53
|
end
|
54
|
+
|
55
|
+
set_error_flash close_account_error_flash
|
56
|
+
close_account_view
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -51,11 +51,13 @@ module Rodauth
|
|
51
51
|
return true if salts.empty?
|
52
52
|
|
53
53
|
salts.any? do |hash_id, salt|
|
54
|
-
|
54
|
+
database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt)
|
55
55
|
end
|
56
56
|
else
|
57
57
|
# :nocov:
|
58
|
-
previous_password_ds.select_map(previous_password_hash_column).any?
|
58
|
+
previous_password_ds.select_map(previous_password_hash_column).any? do |hash|
|
59
|
+
password_hash_match?(hash, password)
|
60
|
+
end
|
59
61
|
# :nocov:
|
60
62
|
end
|
61
63
|
|
@@ -94,7 +94,7 @@ module Rodauth
|
|
94
94
|
redirect email_auth_email_sent_redirect
|
95
95
|
end
|
96
96
|
|
97
|
-
|
97
|
+
login('email_auth')
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
@@ -215,7 +215,7 @@ module Rodauth
|
|
215
215
|
# that allows login access to the account becomes a
|
216
216
|
# security liability, and it is best to remove it.
|
217
217
|
remove_email_auth_key
|
218
|
-
super
|
218
|
+
super
|
219
219
|
end
|
220
220
|
|
221
221
|
def after_close_account
|
@@ -5,11 +5,20 @@ module Rodauth
|
|
5
5
|
auth_value_method :http_basic_auth_realm, "protected"
|
6
6
|
auth_value_method :require_http_basic_auth?, false
|
7
7
|
|
8
|
+
def logged_in?
|
9
|
+
ret = super
|
10
|
+
|
11
|
+
if !ret && !defined?(@checked_http_basic_auth)
|
12
|
+
http_basic_auth
|
13
|
+
ret = super
|
14
|
+
end
|
15
|
+
|
16
|
+
ret
|
17
|
+
end
|
18
|
+
|
8
19
|
def require_login
|
9
20
|
if require_http_basic_auth?
|
10
21
|
require_http_basic_auth
|
11
|
-
elsif !logged_in?
|
12
|
-
http_basic_auth
|
13
22
|
end
|
14
23
|
|
15
24
|
super
|
@@ -23,6 +32,9 @@ module Rodauth
|
|
23
32
|
end
|
24
33
|
|
25
34
|
def http_basic_auth
|
35
|
+
return @checked_http_basic_auth if defined?(@checked_http_basic_auth)
|
36
|
+
|
37
|
+
@checked_http_basic_auth = nil
|
26
38
|
return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1])
|
27
39
|
|
28
40
|
username, password = token.unpack("m*").first.split(/:/, 2)
|
@@ -50,6 +62,7 @@ module Rodauth
|
|
50
62
|
after_login
|
51
63
|
end
|
52
64
|
|
65
|
+
@checked_http_basic_auth = true
|
53
66
|
return true
|
54
67
|
end
|
55
68
|
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -50,7 +50,7 @@ module Rodauth
|
|
50
50
|
json_response[json_response_error_key] = invalid_jwt_format_error_message
|
51
51
|
response.status ||= json_response_error_status
|
52
52
|
response['Content-Type'] ||= json_response_content_type
|
53
|
-
response.write(
|
53
|
+
response.write(_json_response_body(json_response))
|
54
54
|
request.halt
|
55
55
|
end
|
56
56
|
|
@@ -138,15 +138,25 @@ module Rodauth
|
|
138
138
|
!!(jwt_token && jwt_payload)
|
139
139
|
end
|
140
140
|
|
141
|
+
def view(page, title)
|
142
|
+
return super unless use_jwt?
|
143
|
+
return_json_response
|
144
|
+
end
|
145
|
+
|
141
146
|
private
|
142
147
|
|
148
|
+
def check_csrf?
|
149
|
+
return false if use_jwt?
|
150
|
+
super
|
151
|
+
end
|
152
|
+
|
143
153
|
def before_rodauth
|
144
154
|
if json_request?
|
145
155
|
if jwt_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp
|
146
156
|
response.status = 406
|
147
157
|
json_response[json_response_error_key] = json_not_accepted_error_message
|
148
158
|
response['Content-Type'] ||= json_response_content_type
|
149
|
-
response.write(
|
159
|
+
response.write(_json_response_body(json_response))
|
150
160
|
request.halt
|
151
161
|
end
|
152
162
|
|
@@ -251,12 +261,6 @@ module Rodauth
|
|
251
261
|
@json_response ||= {}
|
252
262
|
end
|
253
263
|
|
254
|
-
def _view(meth, page)
|
255
|
-
return super unless use_jwt?
|
256
|
-
return super if meth == :render
|
257
|
-
return_json_response
|
258
|
-
end
|
259
|
-
|
260
264
|
def _json_response_body(hash)
|
261
265
|
request.send(:convert_to_json, hash)
|
262
266
|
end
|
@@ -13,27 +13,27 @@ module Rodauth
|
|
13
13
|
auth_methods(:jwt_cors_allow?)
|
14
14
|
|
15
15
|
def jwt_cors_allow?
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
16
|
+
return false unless origin = request.env['HTTP_ORIGIN']
|
17
|
+
|
18
|
+
case allowed = jwt_cors_allow_origin
|
19
|
+
when String
|
20
|
+
timing_safe_eql?(origin, allowed)
|
21
|
+
when Array
|
22
|
+
allowed.any?{|s| timing_safe_eql?(origin, s)}
|
23
|
+
when Regexp
|
24
|
+
allowed =~ origin
|
25
|
+
when true
|
26
|
+
true
|
27
|
+
else
|
28
|
+
false
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
34
|
def before_rodauth
|
35
|
-
if
|
36
|
-
response['Access-Control-Allow-Origin'] =
|
35
|
+
if jwt_cors_allow?
|
36
|
+
response['Access-Control-Allow-Origin'] = request.env['HTTP_ORIGIN']
|
37
37
|
|
38
38
|
# Handle CORS preflight request
|
39
39
|
if request.request_method == 'OPTIONS'
|
@@ -19,23 +19,29 @@ module Rodauth
|
|
19
19
|
auth_value_method :jwt_refresh_token_key_column, :key
|
20
20
|
auth_value_method :jwt_refresh_token_key_param, 'refresh_token'
|
21
21
|
auth_value_method :jwt_refresh_token_table, :account_jwt_refresh_keys
|
22
|
+
translatable_method :jwt_refresh_without_access_token_message, 'no JWT access token provided during refresh'
|
23
|
+
auth_value_method :jwt_refresh_without_access_token_status, 401
|
22
24
|
|
23
25
|
auth_private_methods(
|
24
26
|
:account_from_refresh_token
|
25
27
|
)
|
26
28
|
|
27
29
|
route do |r|
|
30
|
+
before_jwt_refresh_route
|
31
|
+
|
28
32
|
r.post do
|
29
|
-
if
|
30
|
-
|
33
|
+
if !session_value
|
34
|
+
response.status ||= jwt_refresh_without_access_token_status
|
35
|
+
json_response[json_response_error_key] = jwt_refresh_without_access_token_message
|
36
|
+
elsif (refresh_token = param_or_nil(jwt_refresh_token_key_param)) && account_from_refresh_token(refresh_token)
|
31
37
|
transaction do
|
32
38
|
before_refresh_token
|
33
39
|
formatted_token = generate_refresh_token
|
34
40
|
remove_jwt_refresh_token_key(refresh_token)
|
41
|
+
json_response[jwt_refresh_token_key] = formatted_token
|
42
|
+
json_response[jwt_access_token_key] = session_jwt
|
35
43
|
after_refresh_token
|
36
44
|
end
|
37
|
-
json_response[jwt_refresh_token_key] = formatted_token
|
38
|
-
json_response[jwt_access_token_key] = session_jwt
|
39
45
|
else
|
40
46
|
json_response[json_response_error_key] = jwt_refresh_invalid_token_message
|
41
47
|
response.status ||= json_response_error_status
|
@@ -80,14 +86,10 @@ module Rodauth
|
|
80
86
|
private
|
81
87
|
|
82
88
|
def _account_from_refresh_token(token)
|
83
|
-
id,
|
84
|
-
return unless id && token
|
85
|
-
|
86
|
-
token_id, key = split_token(token)
|
87
|
-
return unless token_id && key
|
89
|
+
id, token_id, key = _account_refresh_token_split(token)
|
88
90
|
|
91
|
+
return unless key
|
89
92
|
return unless actual = get_active_refresh_token(id, token_id)
|
90
|
-
|
91
93
|
return unless timing_safe_eql?(key, convert_token_key(actual))
|
92
94
|
|
93
95
|
ds = account_ds(id)
|
@@ -95,6 +97,16 @@ module Rodauth
|
|
95
97
|
ds.first
|
96
98
|
end
|
97
99
|
|
100
|
+
def _account_refresh_token_split(token)
|
101
|
+
id, token = split_token(token)
|
102
|
+
return unless id && token
|
103
|
+
|
104
|
+
token_id, key = split_token(token)
|
105
|
+
return unless token_id && key
|
106
|
+
|
107
|
+
[id, token_id, key]
|
108
|
+
end
|
109
|
+
|
98
110
|
def get_active_refresh_token(account_id, token_id)
|
99
111
|
jwt_refresh_token_account_ds(account_id).
|
100
112
|
where(Sequel::CURRENT_TIMESTAMP > jwt_refresh_token_deadline_column).
|
@@ -134,6 +146,23 @@ module Rodauth
|
|
134
146
|
hash
|
135
147
|
end
|
136
148
|
|
149
|
+
def before_logout
|
150
|
+
if token = param_or_nil(jwt_refresh_token_key_param)
|
151
|
+
if token == 'all'
|
152
|
+
jwt_refresh_token_account_ds(session_value).delete
|
153
|
+
else
|
154
|
+
id, token_id, key = _account_refresh_token_split(token)
|
155
|
+
|
156
|
+
if id && token_id && key && (actual = get_active_refresh_token(session_value, token_id)) && timing_safe_eql?(key, convert_token_key(actual))
|
157
|
+
jwt_refresh_token_account_ds(id).
|
158
|
+
where(jwt_refresh_token_id_column=>token_id).
|
159
|
+
delete
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
super if defined?(super)
|
164
|
+
end
|
165
|
+
|
137
166
|
def after_close_account
|
138
167
|
jwt_refresh_token_account_ds(account_id).delete
|
139
168
|
super if defined?(super)
|