rodauth 2.1.0 → 2.6.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 +56 -0
- data/README.rdoc +14 -0
- data/doc/base.rdoc +3 -1
- 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 +17 -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.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/release_notes/2.6.0.txt +37 -0
- data/doc/verify_login_change.rdoc +1 -0
- data/javascript/webauthn_auth.js +9 -9
- data/javascript/webauthn_setup.js +9 -6
- data/lib/rodauth.rb +13 -9
- data/lib/rodauth/features/active_sessions.rb +5 -7
- data/lib/rodauth/features/audit_logging.rb +2 -0
- data/lib/rodauth/features/base.rb +18 -3
- data/lib/rodauth/features/change_password.rb +1 -1
- data/lib/rodauth/features/close_account.rb +8 -6
- data/lib/rodauth/features/confirm_password.rb +2 -2
- data/lib/rodauth/features/disallow_password_reuse.rb +4 -2
- data/lib/rodauth/features/email_auth.rb +2 -2
- data/lib/rodauth/features/jwt.rb +10 -7
- data/lib/rodauth/features/jwt_cors.rb +15 -15
- data/lib/rodauth/features/jwt_refresh.rb +76 -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 +10 -6
- data/lib/rodauth/features/verify_account_grace_period.rb +2 -4
- 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
- metadata +37 -5
@@ -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.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An around_rodauth configuration method has been added, which is
|
4
|
+
called around all Rodauth actions. This configuration method
|
5
|
+
is passed a block, and is useful for cases where you want to wrap
|
6
|
+
Rodauth's handling of the request.
|
7
|
+
|
8
|
+
For example, if you had a method named time_block in your Roda scope
|
9
|
+
that timed block execution and added a response header, you could
|
10
|
+
time Rodauth actions using something like:
|
11
|
+
|
12
|
+
around_rodauth do |&block|
|
13
|
+
scope.time_block('Rodauth') do
|
14
|
+
super(&block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
* The allow_refresh_with_expired_jwt_access_token? configuration has
|
19
|
+
been added to the jwt_refresh feature, allowing refreshing with an
|
20
|
+
expired but otherwise valid access token. When using this method,
|
21
|
+
it is required to have an hmac_secret specified, so that Rodauth
|
22
|
+
can make sure the access token matches the refresh token.
|
23
|
+
|
24
|
+
= Other Improvements
|
25
|
+
|
26
|
+
* The javascript for setting up a WebAuthn token has been fixed to
|
27
|
+
allow it to work correctly if there is already an existing
|
28
|
+
WebAuthn token for the account.
|
29
|
+
|
30
|
+
* The rodauth.setup_account_verification method has been promoted to
|
31
|
+
public API. You can use this method for automatically sending
|
32
|
+
account verification emails when automatically creating accounts.
|
33
|
+
|
34
|
+
* Rodauth no longer loads the same feature multiple times into a
|
35
|
+
single configuration. This didn't cause any problems before, but
|
36
|
+
could result in duplicate entries when looking at the loaded
|
37
|
+
features.
|
@@ -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/javascript/webauthn_auth.js
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
(function() {
|
2
|
+
var pack = function(v) { return btoa(String.fromCharCode.apply(null, new Uint8Array(v))).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); };
|
3
|
+
var unpack = function(v) { return Uint8Array.from(atob(v.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); };
|
2
4
|
var element = document.getElementById('webauthn-auth-form');
|
3
5
|
var f = function(e) {
|
4
6
|
//console.log(e);
|
5
7
|
e.preventDefault();
|
6
8
|
if (navigator.credentials) {
|
7
9
|
var opts = JSON.parse(element.getAttribute("data-credential-options"));
|
8
|
-
opts.challenge =
|
9
|
-
opts.allowCredentials.forEach(function(cred) {
|
10
|
-
cred.id = Uint8Array.from(atob(cred.id.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
|
11
|
-
});
|
10
|
+
opts.challenge = unpack(opts.challenge);
|
11
|
+
opts.allowCredentials.forEach(function(cred) { cred.id = unpack(cred.id); });
|
12
12
|
//console.log(opts);
|
13
13
|
navigator.credentials.get({publicKey: opts}).
|
14
14
|
then(function(cred){
|
15
15
|
//console.log(cred);
|
16
16
|
//window.cred = cred
|
17
17
|
|
18
|
-
var rawId =
|
18
|
+
var rawId = pack(cred.rawId);
|
19
19
|
var authValue = {
|
20
20
|
type: cred.type,
|
21
21
|
id: rawId,
|
22
22
|
rawId: rawId,
|
23
23
|
response: {
|
24
|
-
authenticatorData:
|
25
|
-
clientDataJSON:
|
26
|
-
signature:
|
24
|
+
authenticatorData: pack(cred.response.authenticatorData),
|
25
|
+
clientDataJSON: pack(cred.response.clientDataJSON),
|
26
|
+
signature: pack(cred.response.signature)
|
27
27
|
}
|
28
28
|
};
|
29
29
|
|
30
30
|
if (cred.response.userHandle) {
|
31
|
-
authValue.response.userHandle =
|
31
|
+
authValue.response.userHandle = pack(cred.response.userHandle);
|
32
32
|
}
|
33
33
|
|
34
34
|
document.getElementById('webauthn-auth').value = JSON.stringify(authValue);
|
@@ -1,26 +1,29 @@
|
|
1
1
|
(function() {
|
2
|
+
var pack = function(v) { return btoa(String.fromCharCode.apply(null, new Uint8Array(v))).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); };
|
3
|
+
var unpack = function(v) { return Uint8Array.from(atob(v.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); };
|
2
4
|
var element = document.getElementById('webauthn-setup-form');
|
3
5
|
var f = function(e) {
|
4
6
|
//console.log(e);
|
5
7
|
e.preventDefault();
|
6
8
|
if (navigator.credentials) {
|
7
9
|
var opts = JSON.parse(element.getAttribute("data-credential-options"));
|
8
|
-
opts.challenge =
|
9
|
-
opts.user.id =
|
10
|
+
opts.challenge = unpack(opts.challenge);
|
11
|
+
opts.user.id = unpack(opts.user.id);
|
12
|
+
opts.excludeCredentials.forEach(function(cred) { cred.id = unpack(cred.id); });
|
10
13
|
//console.log(opts);
|
11
14
|
navigator.credentials.create({publicKey: opts}).
|
12
15
|
then(function(cred){
|
13
16
|
//console.log(cred);
|
14
17
|
//window.cred = cred
|
15
|
-
|
16
|
-
var rawId =
|
18
|
+
|
19
|
+
var rawId = pack(cred.rawId);
|
17
20
|
document.getElementById('webauthn-setup').value = JSON.stringify({
|
18
21
|
type: cred.type,
|
19
22
|
id: rawId,
|
20
23
|
rawId: rawId,
|
21
24
|
response: {
|
22
|
-
attestationObject:
|
23
|
-
clientDataJSON:
|
25
|
+
attestationObject: pack(cred.response.attestationObject),
|
26
|
+
clientDataJSON: pack(cred.response.clientDataJSON)
|
24
27
|
}
|
25
28
|
});
|
26
29
|
element.removeEventListener("submit", f);
|
data/lib/rodauth.rb
CHANGED
@@ -120,8 +120,10 @@ module Rodauth
|
|
120
120
|
define_method(handle_meth) do
|
121
121
|
request.is send(route_meth) do
|
122
122
|
check_csrf if check_csrf?
|
123
|
-
|
124
|
-
|
123
|
+
_around_rodauth do
|
124
|
+
before_rodauth
|
125
|
+
send(internal_handle_meth, request)
|
126
|
+
end
|
125
127
|
end
|
126
128
|
end
|
127
129
|
|
@@ -137,7 +139,9 @@ module Rodauth
|
|
137
139
|
feature.module_eval(&block)
|
138
140
|
configuration.def_configuration_methods(feature)
|
139
141
|
|
142
|
+
# :nocov:
|
140
143
|
if constant
|
144
|
+
# :nocov:
|
141
145
|
Rodauth.const_set(constant, feature)
|
142
146
|
Rodauth::FeatureConfiguration.const_set(constant, configuration)
|
143
147
|
end
|
@@ -286,9 +290,11 @@ module Rodauth
|
|
286
290
|
end
|
287
291
|
|
288
292
|
def enable(*features)
|
289
|
-
|
290
|
-
|
291
|
-
|
293
|
+
features.each do |feature|
|
294
|
+
next if @auth.features.include?(feature)
|
295
|
+
load_feature(feature)
|
296
|
+
@auth.features << feature
|
297
|
+
end
|
292
298
|
end
|
293
299
|
|
294
300
|
private
|
@@ -336,10 +342,8 @@ module Rodauth
|
|
336
342
|
end
|
337
343
|
|
338
344
|
def freeze
|
339
|
-
|
340
|
-
|
341
|
-
opts[:rodauths].freeze
|
342
|
-
end
|
345
|
+
opts[:rodauths].each_value(&:freeze)
|
346
|
+
opts[:rodauths].freeze
|
343
347
|
super
|
344
348
|
end
|
345
349
|
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
|
@@ -110,7 +111,8 @@ module Rodauth
|
|
110
111
|
:account_from_session,
|
111
112
|
:field_attributes,
|
112
113
|
:field_error_attributes,
|
113
|
-
:formatted_field_error
|
114
|
+
:formatted_field_error,
|
115
|
+
:around_rodauth
|
114
116
|
)
|
115
117
|
|
116
118
|
configuration_module_eval do
|
@@ -393,9 +395,9 @@ module Rodauth
|
|
393
395
|
def password_match?(password)
|
394
396
|
if hash = get_password_hash
|
395
397
|
if account_password_hash_column || !use_database_authentication_functions?
|
396
|
-
|
398
|
+
password_hash_match?(hash, password)
|
397
399
|
else
|
398
|
-
|
400
|
+
database_function_password_match?(:rodauth_valid_password_hash, account_id, password, hash)
|
399
401
|
end
|
400
402
|
end
|
401
403
|
end
|
@@ -458,6 +460,18 @@ module Rodauth
|
|
458
460
|
|
459
461
|
private
|
460
462
|
|
463
|
+
def _around_rodauth
|
464
|
+
yield
|
465
|
+
end
|
466
|
+
|
467
|
+
def database_function_password_match?(name, hash_id, password, salt)
|
468
|
+
db.get(Sequel.function(function_name(name), hash_id, BCrypt::Engine.hash_secret(password, salt)))
|
469
|
+
end
|
470
|
+
|
471
|
+
def password_hash_match?(hash, password)
|
472
|
+
BCrypt::Password.new(hash) == password
|
473
|
+
end
|
474
|
+
|
461
475
|
def convert_token_key(key)
|
462
476
|
if key && hmac_secret
|
463
477
|
compute_hmac(key)
|
@@ -493,6 +507,7 @@ module Rodauth
|
|
493
507
|
end
|
494
508
|
|
495
509
|
def convert_session_key(key)
|
510
|
+
key = "#{session_key_prefix}#{key}".to_sym if session_key_prefix
|
496
511
|
scope.opts[:sessions_convert_symbols] ? key.to_s : key
|
497
512
|
end
|
498
513
|
|
@@ -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
|
|
@@ -26,11 +26,11 @@ module Rodauth
|
|
26
26
|
require_account_session
|
27
27
|
before_confirm_password_route
|
28
28
|
|
29
|
-
|
29
|
+
r.get do
|
30
30
|
confirm_password_view
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
r.post do
|
34
34
|
if password_match?(param(password_param))
|
35
35
|
transaction do
|
36
36
|
before_confirm_password
|
@@ -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
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -138,6 +138,11 @@ 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
|
|
143
148
|
def check_csrf?
|
@@ -224,9 +229,13 @@ module Rodauth
|
|
224
229
|
end
|
225
230
|
end
|
226
231
|
|
232
|
+
def _jwt_decode_opts
|
233
|
+
jwt_decode_opts
|
234
|
+
end
|
235
|
+
|
227
236
|
def jwt_payload
|
228
237
|
return @jwt_payload if defined?(@jwt_payload)
|
229
|
-
@jwt_payload = JWT.decode(jwt_token, jwt_secret, true,
|
238
|
+
@jwt_payload = JWT.decode(jwt_token, jwt_secret, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0]
|
230
239
|
rescue JWT::DecodeError
|
231
240
|
@jwt_payload = false
|
232
241
|
end
|
@@ -256,12 +265,6 @@ module Rodauth
|
|
256
265
|
@json_response ||= {}
|
257
266
|
end
|
258
267
|
|
259
|
-
def _view(meth, page)
|
260
|
-
return super unless use_jwt?
|
261
|
-
return super if meth == :render
|
262
|
-
return_json_response
|
263
|
-
end
|
264
|
-
|
265
268
|
def _json_response_body(hash)
|
266
269
|
request.send(:convert_to_json, hash)
|
267
270
|
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'
|