rodauth 2.4.0 → 2.9.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 +42 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +10 -2
- data/doc/base.rdoc +2 -1
- data/doc/json.rdoc +47 -0
- data/doc/jwt.rdoc +1 -28
- data/doc/jwt_refresh.rdoc +8 -0
- data/doc/login.rdoc +1 -0
- data/doc/recovery_codes.rdoc +2 -1
- data/doc/release_notes/2.5.0.txt +20 -0
- data/doc/release_notes/2.6.0.txt +37 -0
- data/doc/release_notes/2.7.0.txt +33 -0
- data/doc/release_notes/2.8.0.txt +20 -0
- data/doc/release_notes/2.9.0.txt +21 -0
- data/doc/remember.rdoc +1 -1
- 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 +14 -6
- data/lib/rodauth/features/base.rb +7 -2
- data/lib/rodauth/features/change_password.rb +1 -1
- data/lib/rodauth/features/confirm_password.rb +2 -2
- data/lib/rodauth/features/json.rb +189 -0
- data/lib/rodauth/features/jwt.rb +22 -170
- data/lib/rodauth/features/jwt_refresh.rb +63 -13
- data/lib/rodauth/features/login.rb +8 -2
- data/lib/rodauth/features/otp.rb +0 -2
- data/lib/rodauth/features/recovery_codes.rb +22 -1
- data/lib/rodauth/features/remember.rb +6 -1
- data/lib/rodauth/features/verify_account.rb +6 -7
- data/lib/rodauth/features/verify_login_change.rb +2 -1
- data/lib/rodauth/features/webauthn_verify_account.rb +1 -1
- data/lib/rodauth/version.rb +1 -1
- metadata +35 -22
@@ -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
@@ -66,6 +66,7 @@ module Rodauth
|
|
66
66
|
define_method(meth) do |&block|
|
67
67
|
@auth.send(:define_method, meth, &block)
|
68
68
|
@auth.send(:private, meth) if priv
|
69
|
+
@auth.send(:alias_method, meth, meth)
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
@@ -74,6 +75,7 @@ module Rodauth
|
|
74
75
|
define_method(meth) do |&block|
|
75
76
|
@auth.send(:define_method, umeth, &block)
|
76
77
|
@auth.send(:private, umeth)
|
78
|
+
@auth.send(:alias_method, umeth, umeth)
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
@@ -82,6 +84,7 @@ module Rodauth
|
|
82
84
|
block ||= proc{v}
|
83
85
|
@auth.send(:define_method, meth, &block)
|
84
86
|
@auth.send(:private, meth) if priv
|
87
|
+
@auth.send(:alias_method, meth, meth)
|
85
88
|
end
|
86
89
|
end
|
87
90
|
end
|
@@ -120,8 +123,10 @@ module Rodauth
|
|
120
123
|
define_method(handle_meth) do
|
121
124
|
request.is send(route_meth) do
|
122
125
|
check_csrf if check_csrf?
|
123
|
-
|
124
|
-
|
126
|
+
_around_rodauth do
|
127
|
+
before_rodauth
|
128
|
+
send(internal_handle_meth, request)
|
129
|
+
end
|
125
130
|
end
|
126
131
|
end
|
127
132
|
|
@@ -238,6 +243,7 @@ module Rodauth
|
|
238
243
|
instance_variable_set(iv, send(umeth))
|
239
244
|
end
|
240
245
|
end
|
246
|
+
alias_method(meth, meth)
|
241
247
|
auth_private_methods(meth)
|
242
248
|
end
|
243
249
|
|
@@ -288,15 +294,17 @@ module Rodauth
|
|
288
294
|
end
|
289
295
|
|
290
296
|
def enable(*features)
|
291
|
-
|
292
|
-
|
293
|
-
|
297
|
+
features.each do |feature|
|
298
|
+
next if @auth.features.include?(feature)
|
299
|
+
load_feature(feature)
|
300
|
+
@auth.features << feature
|
301
|
+
end
|
294
302
|
end
|
295
303
|
|
296
304
|
private
|
297
305
|
|
298
306
|
def load_feature(feature_name)
|
299
|
-
require "rodauth/features/#{feature_name}"
|
307
|
+
require "rodauth/features/#{feature_name}" unless FEATURES[feature_name]
|
300
308
|
feature = FEATURES[feature_name]
|
301
309
|
enable(*feature.dependencies)
|
302
310
|
extend feature.configuration
|
@@ -102,7 +102,6 @@ module Rodauth
|
|
102
102
|
:set_redirect_error_flash,
|
103
103
|
:set_title,
|
104
104
|
:translate,
|
105
|
-
:unverified_account_message,
|
106
105
|
:update_session
|
107
106
|
)
|
108
107
|
|
@@ -111,7 +110,8 @@ module Rodauth
|
|
111
110
|
:account_from_session,
|
112
111
|
:field_attributes,
|
113
112
|
:field_error_attributes,
|
114
|
-
:formatted_field_error
|
113
|
+
:formatted_field_error,
|
114
|
+
:around_rodauth
|
115
115
|
)
|
116
116
|
|
117
117
|
configuration_module_eval do
|
@@ -260,6 +260,7 @@ module Rodauth
|
|
260
260
|
@password_field_autocomplete_value || 'current-password'
|
261
261
|
end
|
262
262
|
|
263
|
+
alias account_password_hash_column account_password_hash_column
|
263
264
|
# If the account_password_hash_column is set, the password hash is verified in
|
264
265
|
# ruby, it will not use a database function to do so, it will check the password
|
265
266
|
# hash using bcrypt.
|
@@ -459,6 +460,10 @@ module Rodauth
|
|
459
460
|
|
460
461
|
private
|
461
462
|
|
463
|
+
def _around_rodauth
|
464
|
+
yield
|
465
|
+
end
|
466
|
+
|
462
467
|
def database_function_password_match?(name, hash_id, password, salt)
|
463
468
|
db.get(Sequel.function(function_name(name), hash_id, BCrypt::Engine.hash_secret(password, salt)))
|
464
469
|
end
|
@@ -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
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:json, :Json) do
|
5
|
+
translatable_method :json_not_accepted_error_message, 'Unsupported Accept header. Must accept "application/json" or compatible content type'
|
6
|
+
translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
|
7
|
+
auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
|
8
|
+
auth_value_method :json_check_accept?, true
|
9
|
+
auth_value_method :json_request_content_type_regexp, /\bapplication\/(?:vnd\.api\+)?json\b/i
|
10
|
+
auth_value_method :json_response_content_type, 'application/json'
|
11
|
+
auth_value_method :json_response_custom_error_status?, true
|
12
|
+
auth_value_method :json_response_error_status, 400
|
13
|
+
auth_value_method :json_response_error_key, "error"
|
14
|
+
auth_value_method :json_response_field_error_key, "field-error"
|
15
|
+
auth_value_method :json_response_success_key, "success"
|
16
|
+
translatable_method :non_json_request_error_message, 'Only JSON format requests are allowed'
|
17
|
+
|
18
|
+
auth_value_methods(
|
19
|
+
:only_json?,
|
20
|
+
:use_json?,
|
21
|
+
)
|
22
|
+
|
23
|
+
auth_methods(
|
24
|
+
:json_request?,
|
25
|
+
)
|
26
|
+
|
27
|
+
auth_private_methods :json_response_body
|
28
|
+
|
29
|
+
def set_field_error(field, message)
|
30
|
+
return super unless use_json?
|
31
|
+
json_response[json_response_field_error_key] = [field, message]
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_error_flash(message)
|
35
|
+
return super unless use_json?
|
36
|
+
json_response[json_response_error_key] = message
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_redirect_error_flash(message)
|
40
|
+
return super unless use_json?
|
41
|
+
json_response[json_response_error_key] = message
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_notice_flash(message)
|
45
|
+
return super unless use_json?
|
46
|
+
json_response[json_response_success_key] = message if include_success_messages?
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_notice_now_flash(message)
|
50
|
+
return super unless use_json?
|
51
|
+
json_response[json_response_success_key] = message if include_success_messages?
|
52
|
+
end
|
53
|
+
|
54
|
+
def json_request?
|
55
|
+
return @json_request if defined?(@json_request)
|
56
|
+
@json_request = request.content_type =~ json_request_content_type_regexp
|
57
|
+
end
|
58
|
+
|
59
|
+
def use_json?
|
60
|
+
json_request? || only_json?
|
61
|
+
end
|
62
|
+
|
63
|
+
def view(page, title)
|
64
|
+
return super unless use_json?
|
65
|
+
return_json_response
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def before_view_recovery_codes
|
71
|
+
super if defined?(super)
|
72
|
+
if use_json?
|
73
|
+
json_response[:codes] = recovery_codes
|
74
|
+
json_response[json_response_success_key] ||= "" if include_success_messages?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def before_webauthn_setup_route
|
79
|
+
super if defined?(super)
|
80
|
+
if use_json? && !param_or_nil(webauthn_setup_param)
|
81
|
+
cred = new_webauthn_credential
|
82
|
+
json_response[webauthn_setup_param] = cred.as_json
|
83
|
+
json_response[webauthn_setup_challenge_param] = cred.challenge
|
84
|
+
json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def before_webauthn_auth_route
|
89
|
+
super if defined?(super)
|
90
|
+
if use_json? && !param_or_nil(webauthn_auth_param)
|
91
|
+
cred = webauth_credential_options_for_get
|
92
|
+
json_response[webauthn_auth_param] = cred.as_json
|
93
|
+
json_response[webauthn_auth_challenge_param] = cred.challenge
|
94
|
+
json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def before_webauthn_login_route
|
99
|
+
super if defined?(super)
|
100
|
+
if use_json? && !param_or_nil(webauthn_auth_param) && account_from_login(param(login_param))
|
101
|
+
cred = webauth_credential_options_for_get
|
102
|
+
json_response[webauthn_auth_param] = cred.as_json
|
103
|
+
json_response[webauthn_auth_challenge_param] = cred.challenge
|
104
|
+
json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def before_webauthn_remove_route
|
109
|
+
super if defined?(super)
|
110
|
+
if use_json? && !param_or_nil(webauthn_remove_param)
|
111
|
+
json_response[webauthn_remove_param] = account_webauthn_usage
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def before_otp_setup_route
|
116
|
+
super if defined?(super)
|
117
|
+
if use_json? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param)
|
118
|
+
_otp_tmp_key(otp_new_secret)
|
119
|
+
json_response[otp_setup_param] = otp_user_key
|
120
|
+
json_response[otp_setup_raw_param] = otp_key
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def before_rodauth
|
125
|
+
if json_request?
|
126
|
+
if json_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp
|
127
|
+
response.status = 406
|
128
|
+
json_response[json_response_error_key] = json_not_accepted_error_message
|
129
|
+
_return_json_response
|
130
|
+
end
|
131
|
+
|
132
|
+
unless request.post?
|
133
|
+
response.status = 405
|
134
|
+
response.headers['Allow'] = 'POST'
|
135
|
+
json_response[json_response_error_key] = json_non_post_error_message
|
136
|
+
return_json_response
|
137
|
+
end
|
138
|
+
elsif only_json?
|
139
|
+
response.status = json_response_error_status
|
140
|
+
response.write non_json_request_error_message
|
141
|
+
request.halt
|
142
|
+
end
|
143
|
+
|
144
|
+
super
|
145
|
+
end
|
146
|
+
|
147
|
+
def redirect(_)
|
148
|
+
return super unless use_json?
|
149
|
+
return_json_response
|
150
|
+
end
|
151
|
+
|
152
|
+
def return_json_response
|
153
|
+
_return_json_response
|
154
|
+
end
|
155
|
+
|
156
|
+
def _return_json_response
|
157
|
+
response.status ||= json_response_error_status if json_response[json_response_error_key]
|
158
|
+
response['Content-Type'] ||= json_response_content_type
|
159
|
+
response.write(_json_response_body(json_response))
|
160
|
+
request.halt
|
161
|
+
end
|
162
|
+
|
163
|
+
def include_success_messages?
|
164
|
+
!json_response_success_key.nil?
|
165
|
+
end
|
166
|
+
|
167
|
+
def _json_response_body(hash)
|
168
|
+
request.send(:convert_to_json, hash)
|
169
|
+
end
|
170
|
+
|
171
|
+
def json_response
|
172
|
+
@json_response ||= {}
|
173
|
+
end
|
174
|
+
|
175
|
+
def set_redirect_error_status(status)
|
176
|
+
if use_json? && json_response_custom_error_status?
|
177
|
+
response.status = status
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_response_error_status(status)
|
182
|
+
if use_json? && !json_response_custom_error_status?
|
183
|
+
status = json_response_error_status
|
184
|
+
end
|
185
|
+
|
186
|
+
super
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -4,41 +4,29 @@ require 'jwt'
|
|
4
4
|
|
5
5
|
module Rodauth
|
6
6
|
Feature.define(:jwt, :Jwt) do
|
7
|
+
depends :json
|
8
|
+
|
7
9
|
translatable_method :invalid_jwt_format_error_message, "invalid JWT format or claim in Authorization header"
|
8
|
-
translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
|
9
|
-
translatable_method :json_not_accepted_error_message, 'Unsupported Accept header. Must accept "application/json" or compatible content type'
|
10
|
-
auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
|
11
|
-
auth_value_method :json_request_content_type_regexp, /\bapplication\/(?:vnd\.api\+)?json\b/i
|
12
|
-
auth_value_method :json_response_content_type, 'application/json'
|
13
|
-
auth_value_method :json_response_error_status, 400
|
14
|
-
auth_value_method :json_response_custom_error_status?, true
|
15
|
-
auth_value_method :json_response_error_key, "error"
|
16
|
-
auth_value_method :json_response_field_error_key, "field-error"
|
17
|
-
auth_value_method :json_response_success_key, "success"
|
18
10
|
auth_value_method :jwt_algorithm, "HS256"
|
19
11
|
auth_value_method :jwt_authorization_ignore, /\A(?:Basic|Digest) /
|
20
12
|
auth_value_method :jwt_authorization_remove, /\ABearer:?\s+/
|
21
|
-
auth_value_method :jwt_check_accept?, true
|
22
13
|
auth_value_method :jwt_decode_opts, {}.freeze
|
23
14
|
auth_value_method :jwt_session_key, nil
|
24
15
|
auth_value_method :jwt_symbolize_deeply?, false
|
25
|
-
translatable_method :non_json_request_error_message, 'Only JSON format requests are allowed'
|
26
16
|
|
27
17
|
auth_value_methods(
|
28
|
-
:only_json?,
|
29
18
|
:jwt_secret,
|
30
19
|
:use_jwt?
|
31
20
|
)
|
32
21
|
|
33
22
|
auth_methods(
|
34
|
-
:json_request?,
|
35
23
|
:jwt_session_hash,
|
36
24
|
:jwt_token,
|
37
25
|
:session_jwt,
|
38
26
|
:set_jwt_token
|
39
27
|
)
|
40
28
|
|
41
|
-
|
29
|
+
def_deprecated_alias :json_check_accept?, :jwt_check_accept?
|
42
30
|
|
43
31
|
def session
|
44
32
|
return @session if defined?(@session)
|
@@ -47,11 +35,8 @@ module Rodauth
|
|
47
35
|
s = {}
|
48
36
|
if jwt_token
|
49
37
|
unless session_data = jwt_payload
|
50
|
-
json_response[json_response_error_key]
|
51
|
-
|
52
|
-
response['Content-Type'] ||= json_response_content_type
|
53
|
-
response.write(_json_response_body(json_response))
|
54
|
-
request.halt
|
38
|
+
json_response[json_response_error_key] ||= invalid_jwt_format_error_message
|
39
|
+
_return_json_response
|
55
40
|
end
|
56
41
|
|
57
42
|
if jwt_session_key
|
@@ -73,37 +58,10 @@ module Rodauth
|
|
73
58
|
|
74
59
|
def clear_session
|
75
60
|
super
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
return super unless use_jwt?
|
81
|
-
json_response[json_response_field_error_key] = [field, message]
|
82
|
-
end
|
83
|
-
|
84
|
-
def set_error_flash(message)
|
85
|
-
return super unless use_jwt?
|
86
|
-
json_response[json_response_error_key] = message
|
87
|
-
end
|
88
|
-
|
89
|
-
def set_redirect_error_flash(message)
|
90
|
-
return super unless use_jwt?
|
91
|
-
json_response[json_response_error_key] = message
|
92
|
-
end
|
93
|
-
|
94
|
-
def set_notice_flash(message)
|
95
|
-
return super unless use_jwt?
|
96
|
-
json_response[json_response_success_key] = message if include_success_messages?
|
97
|
-
end
|
98
|
-
|
99
|
-
def set_notice_now_flash(message)
|
100
|
-
return super unless use_jwt?
|
101
|
-
json_response[json_response_success_key] = message if include_success_messages?
|
102
|
-
end
|
103
|
-
|
104
|
-
def json_request?
|
105
|
-
return @json_request if defined?(@json_request)
|
106
|
-
@json_request = request.content_type =~ json_request_content_type_regexp
|
61
|
+
if use_jwt?
|
62
|
+
session.clear
|
63
|
+
set_jwt
|
64
|
+
end
|
107
65
|
end
|
108
66
|
|
109
67
|
def jwt_secret
|
@@ -131,16 +89,15 @@ module Rodauth
|
|
131
89
|
end
|
132
90
|
|
133
91
|
def use_jwt?
|
134
|
-
|
92
|
+
use_json?
|
135
93
|
end
|
136
94
|
|
137
|
-
def
|
138
|
-
|
95
|
+
def use_json?
|
96
|
+
jwt_token || super
|
139
97
|
end
|
140
98
|
|
141
|
-
def
|
142
|
-
|
143
|
-
return_json_response
|
99
|
+
def valid_jwt?
|
100
|
+
!!(jwt_token && jwt_payload)
|
144
101
|
end
|
145
102
|
|
146
103
|
private
|
@@ -150,99 +107,19 @@ module Rodauth
|
|
150
107
|
super
|
151
108
|
end
|
152
109
|
|
153
|
-
def
|
154
|
-
|
155
|
-
if jwt_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp
|
156
|
-
response.status = 406
|
157
|
-
json_response[json_response_error_key] = json_not_accepted_error_message
|
158
|
-
response['Content-Type'] ||= json_response_content_type
|
159
|
-
response.write(_json_response_body(json_response))
|
160
|
-
request.halt
|
161
|
-
end
|
162
|
-
|
163
|
-
unless request.post?
|
164
|
-
response.status = 405
|
165
|
-
response.headers['Allow'] = 'POST'
|
166
|
-
json_response[json_response_error_key] = json_non_post_error_message
|
167
|
-
return_json_response
|
168
|
-
end
|
169
|
-
elsif only_json?
|
170
|
-
response.status = json_response_error_status
|
171
|
-
response.write non_json_request_error_message
|
172
|
-
request.halt
|
173
|
-
end
|
174
|
-
|
175
|
-
super
|
176
|
-
end
|
177
|
-
|
178
|
-
def before_view_recovery_codes
|
179
|
-
super if defined?(super)
|
180
|
-
if use_jwt?
|
181
|
-
json_response[:codes] = recovery_codes
|
182
|
-
json_response[json_response_success_key] ||= "" if include_success_messages?
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def before_webauthn_setup_route
|
187
|
-
super if defined?(super)
|
188
|
-
if use_jwt? && !param_or_nil(webauthn_setup_param)
|
189
|
-
cred = new_webauthn_credential
|
190
|
-
json_response[webauthn_setup_param] = cred.as_json
|
191
|
-
json_response[webauthn_setup_challenge_param] = cred.challenge
|
192
|
-
json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def before_webauthn_auth_route
|
197
|
-
super if defined?(super)
|
198
|
-
if use_jwt? && !param_or_nil(webauthn_auth_param)
|
199
|
-
cred = webauth_credential_options_for_get
|
200
|
-
json_response[webauthn_auth_param] = cred.as_json
|
201
|
-
json_response[webauthn_auth_challenge_param] = cred.challenge
|
202
|
-
json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def before_webauthn_login_route
|
207
|
-
super if defined?(super)
|
208
|
-
if use_jwt? && !param_or_nil(webauthn_auth_param) && account_from_login(param(login_param))
|
209
|
-
cred = webauth_credential_options_for_get
|
210
|
-
json_response[webauthn_auth_param] = cred.as_json
|
211
|
-
json_response[webauthn_auth_challenge_param] = cred.challenge
|
212
|
-
json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def before_webauthn_remove_route
|
217
|
-
super if defined?(super)
|
218
|
-
if use_jwt? && !param_or_nil(webauthn_remove_param)
|
219
|
-
json_response[webauthn_remove_param] = account_webauthn_usage
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def before_otp_setup_route
|
224
|
-
super if defined?(super)
|
225
|
-
if use_jwt? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param)
|
226
|
-
_otp_tmp_key(otp_new_secret)
|
227
|
-
json_response[otp_setup_param] = otp_user_key
|
228
|
-
json_response[otp_setup_raw_param] = otp_key
|
229
|
-
end
|
110
|
+
def _jwt_decode_opts
|
111
|
+
jwt_decode_opts
|
230
112
|
end
|
231
113
|
|
232
114
|
def jwt_payload
|
233
115
|
return @jwt_payload if defined?(@jwt_payload)
|
234
|
-
@jwt_payload = JWT.decode(jwt_token, jwt_secret, true,
|
235
|
-
rescue JWT::DecodeError
|
236
|
-
|
116
|
+
@jwt_payload = JWT.decode(jwt_token, jwt_secret, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0]
|
117
|
+
rescue JWT::DecodeError => e
|
118
|
+
rescue_jwt_payload(e)
|
237
119
|
end
|
238
120
|
|
239
|
-
def
|
240
|
-
|
241
|
-
return_json_response
|
242
|
-
end
|
243
|
-
|
244
|
-
def include_success_messages?
|
245
|
-
!json_response_success_key.nil?
|
121
|
+
def rescue_jwt_payload(_)
|
122
|
+
@jwt_payload = false
|
246
123
|
end
|
247
124
|
|
248
125
|
def set_session_value(key, value)
|
@@ -257,38 +134,13 @@ module Rodauth
|
|
257
134
|
value
|
258
135
|
end
|
259
136
|
|
260
|
-
def json_response
|
261
|
-
@json_response ||= {}
|
262
|
-
end
|
263
|
-
|
264
|
-
def _json_response_body(hash)
|
265
|
-
request.send(:convert_to_json, hash)
|
266
|
-
end
|
267
|
-
|
268
137
|
def return_json_response
|
269
|
-
response.status ||= json_response_error_status if json_response[json_response_error_key]
|
270
138
|
set_jwt
|
271
|
-
|
272
|
-
response.write(_json_response_body(json_response))
|
273
|
-
request.halt
|
139
|
+
super
|
274
140
|
end
|
275
141
|
|
276
142
|
def set_jwt
|
277
143
|
set_jwt_token(session_jwt)
|
278
144
|
end
|
279
|
-
|
280
|
-
def set_redirect_error_status(status)
|
281
|
-
if use_jwt? && json_response_custom_error_status?
|
282
|
-
response.status = status
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def set_response_error_status(status)
|
287
|
-
if use_jwt? && !json_response_custom_error_status?
|
288
|
-
status = json_response_error_status
|
289
|
-
end
|
290
|
-
|
291
|
-
super
|
292
|
-
end
|
293
145
|
end
|
294
146
|
end
|