rodauth 2.30.0 → 2.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +14 -0
- data/README.rdoc +3 -0
- data/doc/error_reasons.rdoc +1 -0
- data/doc/guides/registration_field.rdoc +1 -1
- data/doc/guides/render_confirmation.rdoc +17 -0
- data/doc/internal_request.rdoc +76 -0
- data/doc/json.rdoc +1 -0
- data/doc/jwt.rdoc +6 -1
- data/doc/jwt_refresh.rdoc +1 -1
- data/doc/release_notes/2.31.0.txt +47 -0
- data/doc/webauthn_autofill.rdoc +5 -0
- data/doc/webauthn_login.rdoc +1 -0
- data/lib/rodauth/features/argon2.rb +31 -12
- data/lib/rodauth/features/internal_request.rb +47 -1
- data/lib/rodauth/features/json.rb +6 -1
- data/lib/rodauth/features/webauthn.rb +6 -0
- data/lib/rodauth/features/webauthn_autofill.rb +7 -1
- data/lib/rodauth/features/webauthn_login.rb +19 -0
- data/lib/rodauth/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34f1a4bc530025482c6550375de67bf9dcbe4ea75ca9f4f775d179805629e115
|
4
|
+
data.tar.gz: 75339b39caf60463c076459e1fc21fd4d2291e4bc756c4cc5107edf7462c0186
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e66e012fca7c52bd25206fd7711921148e8372a249047687e5429074dd9ea64461dcbbda1a0412b16920270814fef9fa5954dda701ae30af66e7a78a7975855
|
7
|
+
data.tar.gz: f7c9ec1aac7150a3fa1b4d2af3e8278f16ef3982d5cd84b6b2984d41b5f5b70f60e9f7c0e099d338055201d820bd099093609b2683086c5a8ec9fdf53dee2e58
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
=== 2.31.0 (2023-08-22)
|
2
|
+
|
3
|
+
* Make clear_session work correctly for internal requests (janko) (#359)
|
4
|
+
|
5
|
+
* Support webauthn_invalid_webauthn_id_message configuration method in the webauthn_autofill feature (janko) (#356)
|
6
|
+
|
7
|
+
* Support webauth features in the internal_request feature (janko) (#355)
|
8
|
+
|
9
|
+
* Allow WebAuthn login to count for two factors if user verification is provided (janko) (#354)
|
10
|
+
|
11
|
+
* Allow explicit use of p_cost in argon2 feature if using argon2 2.1+ (estebanz01) (#353)
|
12
|
+
|
13
|
+
* Add json_response_error? configuration method to json feature, for whether response indicates an error (opya) (#340)
|
14
|
+
|
1
15
|
=== 2.30.0 (2023-05-22)
|
2
16
|
|
3
17
|
* Make load_memory in the remember feature not raise NoMethodError if logged in when the account no longer exists (jeremyevans) (#331)
|
data/README.rdoc
CHANGED
@@ -98,6 +98,9 @@ rqrcode :: Used by the otp feature
|
|
98
98
|
jwt :: Used by the jwt feature
|
99
99
|
webauthn :: Used by the webauthn feature
|
100
100
|
|
101
|
+
You can use <tt>gem install --development rodauth</tt> to install
|
102
|
+
the development dependencies in order to run tests.
|
103
|
+
|
101
104
|
== Security
|
102
105
|
|
103
106
|
=== Password Hash Access Via Database Functions
|
data/doc/error_reasons.rdoc
CHANGED
@@ -38,6 +38,7 @@ Rodauth will call +set_error_reason+ with:
|
|
38
38
|
* :invalid_verify_account_key
|
39
39
|
* :invalid_verify_login_change_key
|
40
40
|
* :invalid_webauthn_auth_param
|
41
|
+
* :invalid_webauthn_id
|
41
42
|
* :invalid_webauthn_remove_param
|
42
43
|
* :invalid_webauthn_setup_param
|
43
44
|
* :invalid_webauthn_sign_count
|
@@ -0,0 +1,17 @@
|
|
1
|
+
= Render confirmation view
|
2
|
+
|
3
|
+
Most Rodauth actions redirect and display a flash notice after they're succesfully performed. However, in some cases you may wish to render a view confirming that the action was succesful, for nicer user experience.
|
4
|
+
|
5
|
+
For example, when the user creates an account, you might render a page with a call to action to verify their account. Assuming you've created an +account_created+ view template alongside your other Rodauth templates, you can configure the following:
|
6
|
+
|
7
|
+
after_create_account do
|
8
|
+
# render "account_created" view template with page title of "Account created!"
|
9
|
+
return_response view("account_created", "Account created!")
|
10
|
+
end
|
11
|
+
|
12
|
+
Similarly, when the user has requested a password reset, you can render a page telling them to check their email:
|
13
|
+
|
14
|
+
after_reset_password_request do
|
15
|
+
# render "password_reset_sent" view template with page title of "Password sent!"
|
16
|
+
return_response view("password_reset_sent", "Password sent!")
|
17
|
+
end
|
data/doc/internal_request.rdoc
CHANGED
@@ -461,3 +461,79 @@ account login, before the change.
|
|
461
461
|
|
462
462
|
Options:
|
463
463
|
+:verify_login_change_key+ :: The verify login change key for the account. This allows verifying login changes by key, without knowing the account id or login.
|
464
|
+
|
465
|
+
=== WebAuthn
|
466
|
+
|
467
|
+
==== webauthn_setup_params (requires account)
|
468
|
+
|
469
|
+
The +webauthn_setup_params+ method returns a hash with +:webauthn_setup+,
|
470
|
+
+:webauthn_setup_challenge+ and +:webauthn_setup_challenge_hmac+ keys.
|
471
|
+
|
472
|
+
The +:webauthn_setup+ options should be provided to the client for WebAuthn
|
473
|
+
registration, while +:webauthn_setup_challenge+ and
|
474
|
+
+webauthn_setup_challenge_hmac+ should be passed to the +webauthn_setup+
|
475
|
+
method.
|
476
|
+
|
477
|
+
==== webauthn_setup (requires account)
|
478
|
+
|
479
|
+
The +webauthn_setup+ method creates a WebAuthn credential for the account.
|
480
|
+
|
481
|
+
Options:
|
482
|
+
+:webauthn_setup+ :: The WebAuthn credential provided by the client during registration.
|
483
|
+
+:webauthn_setup_challenge+ :: The WebAuthn challenge generated for registration.
|
484
|
+
+:webauthn_setup_challenge_hmac+ :: The HMAC of the WebAuthn challenge generated for registration.
|
485
|
+
|
486
|
+
==== webauthn_auth_params (requires account)
|
487
|
+
|
488
|
+
The +webauthn_auth_params+ method returns a hash with +:webauthn_auth+,
|
489
|
+
+:webauthn_auth_challenge+ and +:webauthn_auth_challenge_hmac+ keys.
|
490
|
+
|
491
|
+
The +:webauthn_auth+ options should be provided to the client for WebAuthn
|
492
|
+
authentication, while +:webauthn_auth_challenge+ and
|
493
|
+
+webauthn_auth_challenge_hmac+ should be passed to the +webauthn_auth+ method.
|
494
|
+
|
495
|
+
==== webauthn_auth (requires account)
|
496
|
+
|
497
|
+
The +webauthn_auth+ method determines if the given WebAuthn credential is valid
|
498
|
+
for the account.
|
499
|
+
|
500
|
+
Options:
|
501
|
+
+:webauthn_auth+ :: The WebAuthn credential provided by the client during authentication.
|
502
|
+
+:webauthn_auth_challenge+ :: The WebAuthn challenge generated for authentication.
|
503
|
+
+:webauthn_auth_challenge_hmac+ :: The HMAC of the WebAuthn challenge generated for authentication.
|
504
|
+
|
505
|
+
==== webauthn_remove (requires account)
|
506
|
+
|
507
|
+
The +webauthn_remove+ methods deletes the given WebAuthn credential for the
|
508
|
+
account.
|
509
|
+
|
510
|
+
Options:
|
511
|
+
+:webauthn_remove+ :: The ID of the WebAuthn credential to delete.
|
512
|
+
|
513
|
+
=== WebAuthn Login
|
514
|
+
|
515
|
+
==== webauthn_login_params (requires account or login)
|
516
|
+
|
517
|
+
The +webauthn_login_params+ method returns a hash with +:webauthn_auth+,
|
518
|
+
+:webauthn_auth_challenge+ and +:webauthn_auth_challenge_hmac+ keys.
|
519
|
+
|
520
|
+
The +:webauthn_auth+ options should be provided to the client for WebAuthn
|
521
|
+
authentication, while +:webauthn_auth_challenge+ and
|
522
|
+
+webauthn_auth_challenge_hmac+ should be passed to the +webauthn_login+ method.
|
523
|
+
|
524
|
+
==== webauthn_login (requires account or login)
|
525
|
+
|
526
|
+
The +webauthn_login+ method determines if the given WebAuthn credential is
|
527
|
+
valid for the given account.
|
528
|
+
|
529
|
+
This method will return the account id if the WebAuthn credential is valid.
|
530
|
+
|
531
|
+
Options:
|
532
|
+
+:webauthn_auth+ :: The WebAuthn credential provided by the client during authentication.
|
533
|
+
+:webauthn_auth_challenge+ :: The WebAuthn challenge generated for authentication.
|
534
|
+
+:webauthn_auth_challenge_hmac+ :: The HMAC of the WebAuthn challenge generated for authentication.
|
535
|
+
|
536
|
+
=== WebAuthn Autofill
|
537
|
+
|
538
|
+
Enabling this feature modifies +webauthn_login_params+ and +webauthn_login+
|
539
|
+
methods not to require an account or login.
|
data/doc/json.rdoc
CHANGED
@@ -41,6 +41,7 @@ json_not_accepted_error_message :: The error message to display if +json_check_a
|
|
41
41
|
json_request_content_type_regexp :: The regexp to use to recognize a request as a json request.
|
42
42
|
json_response_content_type :: The content type to set for json responses, <tt>application/json</tt> by default.
|
43
43
|
json_response_custom_error_status? :: Whether to use custom error statuses, instead of always using +json_response_error_status+, true by default, can be set to false for backwards compatibility with Rodauth 1.
|
44
|
+
json_response_error? :: Whether the current JSON response indicates an error. By default, returns whether +json_response_error_key+ is set.
|
44
45
|
json_response_error_key :: The JSON result key containing an error message, +error+ by default.
|
45
46
|
json_response_error_status :: The HTTP status code to use for JSON error responses if not using custom error statuses, 400 by default.
|
46
47
|
json_response_field_error_key :: The JSON result key containing an field error message, <tt>field-error</tt> by default.
|
data/doc/jwt.rdoc
CHANGED
@@ -5,7 +5,7 @@ that ship with Rodauth, using JWT (JSON Web Tokens) to hold the
|
|
5
5
|
session information. It depends on the json feature.
|
6
6
|
|
7
7
|
In order to use this feature, you have to set the +jwt_secret+ configuration
|
8
|
-
option the secret used to cryptographically protect the token.
|
8
|
+
option with the secret used to cryptographically protect the token.
|
9
9
|
|
10
10
|
To use this JSON API, when processing responses for requests to a Rodauth
|
11
11
|
endpoint, check for the Authorization header, and use the value of the
|
@@ -26,6 +26,11 @@ request in your Roda app, you can call the +rodauth.valid_jwt?+ method. If
|
|
26
26
|
+rodauth.valid_jwt?+ returns true, the contents of the jwt can be retrieved
|
27
27
|
from +rodauth.session+.
|
28
28
|
|
29
|
+
Logging the session out does not invalidate the previous JWT token by default.
|
30
|
+
If you would like this behavior, you can use the active_sessions feature, which
|
31
|
+
stores session identifiers in the database and deletes them when the session
|
32
|
+
expires. This provides a whitelist approach of revoking JWT tokens.
|
33
|
+
|
29
34
|
== Auth Value Methods
|
30
35
|
|
31
36
|
invalid_jwt_format_error_message :: The error message to use when a JWT with an invalid format is submitted in the Authorization header.
|
data/doc/jwt_refresh.rdoc
CHANGED
@@ -7,7 +7,7 @@ When this feature is used, the access and refresh token are provided
|
|
7
7
|
at login in the response body (the access token is still provided in the Authorization
|
8
8
|
header), and for any subsequent POST to <tt>/jwt-refresh</tt>.
|
9
9
|
|
10
|
-
Note that using the refresh token
|
10
|
+
Note that using the refresh token invalidates the token and creates
|
11
11
|
a new access token with an updated lifetime. However, it does not invalidate
|
12
12
|
older access tokens. Older access tokens remain valid until they expire. You
|
13
13
|
can use the active_sessions feature if you want previous access tokens to be invalid
|
@@ -0,0 +1,47 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* The internal_request feature now supports WebAuthn, using
|
4
|
+
the following methods:
|
5
|
+
|
6
|
+
* With the webauthn feature:
|
7
|
+
* webauthn_setup_params
|
8
|
+
* webauthn_setup
|
9
|
+
* webauthn_auth_params
|
10
|
+
* webauthn_auth
|
11
|
+
* webauthn_remove
|
12
|
+
|
13
|
+
* With the webauthn_login feature:
|
14
|
+
* webauthn_login_params
|
15
|
+
* webauthn_login
|
16
|
+
|
17
|
+
* A webauthn_login_user_verification_additional_factor? configuration
|
18
|
+
method has been added to the webauthn_login feature. By default,
|
19
|
+
this method returns false. If you configure the method to return
|
20
|
+
true, and the WebAuthn credential provided specifies that it
|
21
|
+
verified the user, then this will treat the user verification as
|
22
|
+
a second factor, so the user will be considered multifactor
|
23
|
+
authenticated after successful login. You should only set this
|
24
|
+
method to true if you consider the WebAuthn user verification
|
25
|
+
strong enough to be a independent factor.
|
26
|
+
|
27
|
+
* A json_response_error? configuration method has been added to the
|
28
|
+
json feature. This should return whether the current response
|
29
|
+
should be treated as an error by the json feature. By default,
|
30
|
+
it is true if json_response_error_key is set in the response,
|
31
|
+
since that is the default place that Rodauth stores errors when
|
32
|
+
using the json feature.
|
33
|
+
|
34
|
+
* A webauthn_invalid_webauthn_id_message configuration method has
|
35
|
+
been added for customizing the error message used for invalid
|
36
|
+
WebAuthn IDs.
|
37
|
+
|
38
|
+
= Other Improvements
|
39
|
+
|
40
|
+
* The argon2 feature now supports setting the Argon2 p_cost if
|
41
|
+
argon2 2.1+ is installed.
|
42
|
+
|
43
|
+
* An :invalid_webauthn_id error reason is now used for invalid
|
44
|
+
WebAuthn IDs.
|
45
|
+
|
46
|
+
* The clear_session method now works as expected for internal
|
47
|
+
requests.
|
data/doc/webauthn_autofill.rdoc
CHANGED
@@ -4,10 +4,15 @@ The webauthn_autofill feature enables autofill UI (aka "conditional mediation")
|
|
4
4
|
for WebAuthn credentials, logging the user in on selection. It depends on the
|
5
5
|
webauthn_login feature.
|
6
6
|
|
7
|
+
This feature allows generating WebAuthn credential options and submitting a
|
8
|
+
WebAuthn login request without providing a login, which can be used
|
9
|
+
independently from the autofill UI.
|
10
|
+
|
7
11
|
== Auth Value Methods
|
8
12
|
|
9
13
|
webauthn_autofill_js :: The javascript code to execute on the login page to enable autofill UI.
|
10
14
|
webauthn_autofill_js_route :: The route to the webauthn autofill javascript file.
|
15
|
+
webauthn_invalid_webauthn_id_message :: The error message to show when provided WebAuthn ID wasn't found in the database.
|
11
16
|
|
12
17
|
== Auth Methods
|
13
18
|
|
data/doc/webauthn_login.rdoc
CHANGED
@@ -5,6 +5,7 @@ WebAuthn. It depends on the login and webauthn features.
|
|
5
5
|
|
6
6
|
== Auth Value Methods
|
7
7
|
|
8
|
+
webauthn_login_user_verification_additional_factor? :: Whether passwordless login via WebAuthn should consider user verification as 2nd factor when using multifactor authentication, false by default. Setting this to true means that the app trusts the user verification done by the authenticator is strong enough to be considered an additional factor.
|
8
9
|
webauthn_login_error_flash :: The flash error to show if there is a failure during passwordless login via WebAuthn.
|
9
10
|
webauthn_login_failure_redirect :: Whether to redirect if there is a failure during passwordless login via WebAuthn.
|
10
11
|
webauthn_login_route :: The route to the webauthn login action.
|
@@ -43,7 +43,7 @@ module Rodauth
|
|
43
43
|
|
44
44
|
def password_hash_cost
|
45
45
|
return super unless use_argon2?
|
46
|
-
argon2_hash_cost
|
46
|
+
argon2_hash_cost
|
47
47
|
end
|
48
48
|
|
49
49
|
def password_hash_match?(hash, password)
|
@@ -60,21 +60,40 @@ module Rodauth
|
|
60
60
|
::Argon2::Password.new(argon2_params).create(password)
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
|
63
|
+
if Argon2::VERSION >= '2.1'
|
64
|
+
def extract_password_hash_cost(hash)
|
65
|
+
return super unless argon2_hash_algorithm?(hash )
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
/\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+),p=(\d+)/ =~ hash
|
68
|
+
{ t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i, p_cost: $3.to_i }
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
if ENV['RACK_ENV'] == 'test'
|
72
|
+
def argon2_hash_cost
|
73
|
+
{ t_cost: 1, m_cost: 3, p_cost: 1 }
|
74
|
+
end
|
75
|
+
# :nocov:
|
76
|
+
else
|
77
|
+
def argon2_hash_cost
|
78
|
+
{ t_cost: 2, m_cost: 16, p_cost: 1 }
|
79
|
+
end
|
73
80
|
end
|
74
|
-
# :nocov:
|
75
81
|
else
|
76
|
-
def
|
77
|
-
|
82
|
+
def extract_password_hash_cost(hash)
|
83
|
+
return super unless argon2_hash_algorithm?(hash )
|
84
|
+
|
85
|
+
/\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+)/ =~ hash
|
86
|
+
{ t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i }
|
87
|
+
end
|
88
|
+
|
89
|
+
if ENV['RACK_ENV'] == 'test'
|
90
|
+
def argon2_hash_cost
|
91
|
+
{ t_cost: 1, m_cost: 3 }
|
92
|
+
end
|
93
|
+
else
|
94
|
+
def argon2_hash_cost
|
95
|
+
{ t_cost: 2, m_cost: 16 }
|
96
|
+
end
|
78
97
|
end
|
79
98
|
end
|
80
99
|
# :nocov:
|
@@ -50,6 +50,10 @@ module Rodauth
|
|
50
50
|
@params[k]
|
51
51
|
end
|
52
52
|
|
53
|
+
def clear_session
|
54
|
+
@session.clear
|
55
|
+
end
|
56
|
+
|
53
57
|
def set_error_flash(message)
|
54
58
|
@flash = message
|
55
59
|
_handle_internal_request_error
|
@@ -81,6 +85,24 @@ module Rodauth
|
|
81
85
|
_return_from_internal_request(recovery_codes)
|
82
86
|
end
|
83
87
|
|
88
|
+
def webauthn_setup_view
|
89
|
+
cred = new_webauthn_credential
|
90
|
+
_return_from_internal_request({
|
91
|
+
webauthn_setup: cred.as_json,
|
92
|
+
webauthn_setup_challenge: cred.challenge,
|
93
|
+
webauthn_setup_challenge_hmac: compute_hmac(cred.challenge)
|
94
|
+
})
|
95
|
+
end
|
96
|
+
|
97
|
+
def webauthn_auth_view
|
98
|
+
cred = webauthn_credential_options_for_get
|
99
|
+
_return_from_internal_request({
|
100
|
+
webauthn_auth: cred.as_json,
|
101
|
+
webauthn_auth_challenge: cred.challenge,
|
102
|
+
webauthn_auth_challenge_hmac: compute_hmac(cred.challenge)
|
103
|
+
})
|
104
|
+
end
|
105
|
+
|
84
106
|
def handle_internal_request(meth)
|
85
107
|
catch(:halt) do
|
86
108
|
_around_rodauth do
|
@@ -153,6 +175,11 @@ module Rodauth
|
|
153
175
|
_set_login_param_from_account
|
154
176
|
end
|
155
177
|
|
178
|
+
def before_webauthn_login_route
|
179
|
+
super
|
180
|
+
_set_login_param_from_account
|
181
|
+
end
|
182
|
+
|
156
183
|
def account_from_key(token, status_id=nil)
|
157
184
|
return super unless session_value
|
158
185
|
return unless yield session_value
|
@@ -232,6 +259,25 @@ module Rodauth
|
|
232
259
|
_handle_otp_setup(request)
|
233
260
|
end
|
234
261
|
|
262
|
+
def _handle_webauthn_setup_params(request)
|
263
|
+
request.env['REQUEST_METHOD'] = 'GET'
|
264
|
+
_handle_webauthn_setup(request)
|
265
|
+
end
|
266
|
+
|
267
|
+
def _handle_webauthn_auth_params(request)
|
268
|
+
request.env['REQUEST_METHOD'] = 'GET'
|
269
|
+
_handle_webauthn_auth(request)
|
270
|
+
end
|
271
|
+
|
272
|
+
def _handle_webauthn_login_params(request)
|
273
|
+
_set_login_param_from_account
|
274
|
+
unless webauthn_login_options?
|
275
|
+
raise InternalRequestError, "no login provided" unless param_or_nil(login_param)
|
276
|
+
raise InternalRequestError, "no account for login"
|
277
|
+
end
|
278
|
+
webauthn_auth_view
|
279
|
+
end
|
280
|
+
|
235
281
|
def _predicate_internal_request(meth, request)
|
236
282
|
_return_false_on_error!
|
237
283
|
_set_internal_request_return_value(true)
|
@@ -302,7 +348,7 @@ module Rodauth
|
|
302
348
|
session[rodauth.session_key] = account_id
|
303
349
|
unless authenticated_by = opts.delete(:authenticated_by)
|
304
350
|
authenticated_by = case route
|
305
|
-
when :otp_auth, :sms_request, :sms_auth, :recovery_auth, :valid_otp_auth?, :valid_sms_auth?, :valid_recovery_auth?
|
351
|
+
when :otp_auth, :sms_request, :sms_auth, :recovery_auth, :webauthn_auth, :webauthn_auth_params, :valid_otp_auth?, :valid_sms_auth?, :valid_recovery_auth?
|
306
352
|
['internal1']
|
307
353
|
else
|
308
354
|
['internal1', 'internal2']
|
@@ -22,6 +22,7 @@ module Rodauth
|
|
22
22
|
|
23
23
|
auth_methods(
|
24
24
|
:json_request?,
|
25
|
+
:json_response_error?
|
25
26
|
)
|
26
27
|
|
27
28
|
auth_private_methods :json_response_body
|
@@ -65,6 +66,10 @@ module Rodauth
|
|
65
66
|
return_json_response
|
66
67
|
end
|
67
68
|
|
69
|
+
def json_response_error?
|
70
|
+
!!json_response[json_response_error_key]
|
71
|
+
end
|
72
|
+
|
68
73
|
private
|
69
74
|
|
70
75
|
def before_two_factor_manage_route
|
@@ -172,7 +177,7 @@ module Rodauth
|
|
172
177
|
end
|
173
178
|
|
174
179
|
def _return_json_response
|
175
|
-
response.status ||= json_response_error_status if
|
180
|
+
response.status ||= json_response_error_status if json_response_error?
|
176
181
|
response['Content-Type'] ||= json_response_content_type
|
177
182
|
return_response _json_response_body(json_response)
|
178
183
|
end
|
@@ -112,6 +112,12 @@ module Rodauth
|
|
112
112
|
|
113
113
|
def_deprecated_alias :webauthn_credential_options_for_get, :webauth_credential_options_for_get
|
114
114
|
|
115
|
+
internal_request_method :webauthn_setup_params
|
116
|
+
internal_request_method :webauthn_setup
|
117
|
+
internal_request_method :webauthn_auth_params
|
118
|
+
internal_request_method :webauthn_auth
|
119
|
+
internal_request_method :webauthn_remove
|
120
|
+
|
115
121
|
route(:webauthn_auth_js) do |r|
|
116
122
|
before_webauthn_auth_js_route
|
117
123
|
r.get do
|
@@ -6,6 +6,8 @@ module Rodauth
|
|
6
6
|
|
7
7
|
auth_value_method :webauthn_autofill_js, File.binread(File.expand_path('../../../../javascript/webauthn_autofill.js', __FILE__)).freeze
|
8
8
|
|
9
|
+
translatable_method :webauthn_invalid_webauthn_id_message, "no webauthn key with given id found"
|
10
|
+
|
9
11
|
route(:webauthn_autofill_js) do |r|
|
10
12
|
before_webauthn_autofill_js_route
|
11
13
|
r.get do
|
@@ -47,7 +49,11 @@ module Rodauth
|
|
47
49
|
.where(webauthn_keys_webauthn_id_column => credential_id)
|
48
50
|
.get(webauthn_keys_account_id_column)
|
49
51
|
|
50
|
-
|
52
|
+
unless account_id
|
53
|
+
throw_error_reason(:invalid_webauthn_id, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_webauthn_id_message)
|
54
|
+
end
|
55
|
+
|
56
|
+
@account = account_ds(account_id).first
|
51
57
|
end
|
52
58
|
|
53
59
|
def webauthn_login_options?
|
@@ -10,6 +10,11 @@ module Rodauth
|
|
10
10
|
|
11
11
|
error_flash "There was an error authenticating via WebAuthn"
|
12
12
|
|
13
|
+
auth_value_method :webauthn_login_user_verification_additional_factor?, false
|
14
|
+
|
15
|
+
internal_request_method :webauthn_login_params
|
16
|
+
internal_request_method :webauthn_login
|
17
|
+
|
13
18
|
route(:webauthn_login) do |r|
|
14
19
|
check_already_logged_in
|
15
20
|
before_webauthn_login_route
|
@@ -24,6 +29,9 @@ module Rodauth
|
|
24
29
|
before_webauthn_login
|
25
30
|
login('webauthn') do
|
26
31
|
webauthn_update_session(webauthn_credential.id)
|
32
|
+
if webauthn_login_verification_factor?(webauthn_credential)
|
33
|
+
two_factor_update_session('webauthn-verification')
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
|
@@ -48,12 +56,23 @@ module Rodauth
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
59
|
+
def webauthn_user_verification
|
60
|
+
return 'preferred' if webauthn_login_user_verification_additional_factor?
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
51
64
|
def use_multi_phase_login?
|
52
65
|
true
|
53
66
|
end
|
54
67
|
|
55
68
|
private
|
56
69
|
|
70
|
+
def webauthn_login_verification_factor?(webauthn_credential)
|
71
|
+
webauthn_login_user_verification_additional_factor? &&
|
72
|
+
webauthn_credential.response.authenticator_data.user_verified? &&
|
73
|
+
uses_two_factor_authentication?
|
74
|
+
end
|
75
|
+
|
57
76
|
def account_from_webauthn_login
|
58
77
|
account_from_login(param(login_param))
|
59
78
|
end
|
data/lib/rodauth/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.31.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -348,6 +348,7 @@ extra_rdoc_files:
|
|
348
348
|
- doc/release_notes/2.29.0.txt
|
349
349
|
- doc/release_notes/2.3.0.txt
|
350
350
|
- doc/release_notes/2.30.0.txt
|
351
|
+
- doc/release_notes/2.31.0.txt
|
351
352
|
- doc/release_notes/2.4.0.txt
|
352
353
|
- doc/release_notes/2.5.0.txt
|
353
354
|
- doc/release_notes/2.6.0.txt
|
@@ -394,6 +395,7 @@ files:
|
|
394
395
|
- doc/guides/query_params.rdoc
|
395
396
|
- doc/guides/redirects.rdoc
|
396
397
|
- doc/guides/registration_field.rdoc
|
398
|
+
- doc/guides/render_confirmation.rdoc
|
397
399
|
- doc/guides/require_mfa.rdoc
|
398
400
|
- doc/guides/reset_password_autologin.rdoc
|
399
401
|
- doc/guides/share_configuration.rdoc
|
@@ -465,6 +467,7 @@ files:
|
|
465
467
|
- doc/release_notes/2.29.0.txt
|
466
468
|
- doc/release_notes/2.3.0.txt
|
467
469
|
- doc/release_notes/2.30.0.txt
|
470
|
+
- doc/release_notes/2.31.0.txt
|
468
471
|
- doc/release_notes/2.4.0.txt
|
469
472
|
- doc/release_notes/2.5.0.txt
|
470
473
|
- doc/release_notes/2.6.0.txt
|