booth 0.0.1
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 +7 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE.md +22 -0
- data/README.md +372 -0
- data/app/assets/config/booth_manifest.js +15 -0
- data/app/assets/images/booth/browsers/README.md +2 -0
- data/app/assets/images/booth/browsers/chrome.svg +1 -0
- data/app/assets/images/booth/browsers/edge.svg +1 -0
- data/app/assets/images/booth/browsers/firefox.svg +1 -0
- data/app/assets/images/booth/browsers/internet_explorer.svg +1 -0
- data/app/assets/images/booth/browsers/opera.svg +1 -0
- data/app/assets/images/booth/browsers/safari.svg +1 -0
- data/app/assets/images/booth/browsers/unknown.svg +1 -0
- data/app/assets/images/booth/platforms/README.md +2 -0
- data/app/assets/images/booth/platforms/android.svg +6 -0
- data/app/assets/images/booth/platforms/apple.svg +6 -0
- data/app/assets/images/booth/platforms/linux.svg +6 -0
- data/app/assets/images/booth/platforms/unknown.svg +1 -0
- data/app/assets/images/booth/platforms/windows.svg +6 -0
- data/app/assets/javascripts/booth/all.js +162 -0
- data/app/assets/javascripts/booth/all.js.map +1 -0
- data/app/assets/javascripts/booth/booth.ts +194 -0
- data/app/assets/javascripts/booth/webauthn-json.ts +99 -0
- data/config/locales/de.yml +84 -0
- data/config/locales/en.yml +79 -0
- data/lib/booth/adminland/credentials/create.rb +30 -0
- data/lib/booth/adminland/onboardings/create.rb +63 -0
- data/lib/booth/adminland/onboardings/destroy.rb +50 -0
- data/lib/booth/adminland/onboardings/find.rb +93 -0
- data/lib/booth/adminland/onboardings/index.rb +23 -0
- data/lib/booth/adminland/periodic_cleanup.rb +11 -0
- data/lib/booth/adminland/recoveries/consume.rb +70 -0
- data/lib/booth/adminland.rb +48 -0
- data/lib/booth/audits/register/added_otp.rb +22 -0
- data/lib/booth/audits/register/changed_otp.rb +22 -0
- data/lib/booth/audits/register/completed_onboarding.rb +22 -0
- data/lib/booth/audits/register/correct_otp.rb +42 -0
- data/lib/booth/audits/register/correct_password.rb +43 -0
- data/lib/booth/audits/register/logout.rb +22 -0
- data/lib/booth/audits/register/requested_password_reset.rb +22 -0
- data/lib/booth/audits/register/wrong_otp.rb +22 -0
- data/lib/booth/audits/register/wrong_password.rb +25 -0
- data/lib/booth/authenticators/confirm.rb +34 -0
- data/lib/booth/authenticators/credential_mode_after_confirmation.rb +25 -0
- data/lib/booth/authenticators/step.rb +19 -0
- data/lib/booth/concerns/action.rb +58 -0
- data/lib/booth/concerns/transition.rb +17 -0
- data/lib/booth/configuration.rb +116 -0
- data/lib/booth/configure.rb +37 -0
- data/lib/booth/contests/get.rb +36 -0
- data/lib/booth/contests/respond.rb +78 -0
- data/lib/booth/contests/set_for_login.rb +28 -0
- data/lib/booth/cooldowns/distance_of_time.rb +46 -0
- data/lib/booth/cooldowns/otp.rb +22 -0
- data/lib/booth/cooldowns/password.rb +44 -0
- data/lib/booth/cooldowns/password_reset.rb +24 -0
- data/lib/booth/cooldowns/strategies/exponential.rb +82 -0
- data/lib/booth/cooldowns/strategies/global.rb +62 -0
- data/lib/booth/cooldowns/strategies/result.rb +22 -0
- data/lib/booth/credentials/create.rb +28 -0
- data/lib/booth/credentials/create_with_onboarding.rb +26 -0
- data/lib/booth/credentials/find_by_username.rb +45 -0
- data/lib/booth/credentials/mode.rb +69 -0
- data/lib/booth/credentials/modes/otp_addable.rb +23 -0
- data/lib/booth/credentials/modes/otp_changeable.rb +23 -0
- data/lib/booth/credentials/modes/otp_manageable.rb +17 -0
- data/lib/booth/credentials/modes/otp_removable.rb +23 -0
- data/lib/booth/credentials/modes/password_addable.rb +29 -0
- data/lib/booth/credentials/modes/password_changeable.rb +31 -0
- data/lib/booth/credentials/modes/password_manageable.rb +17 -0
- data/lib/booth/credentials/modes/password_removable.rb +24 -0
- data/lib/booth/credentials/modes/password_removal_requires_user_verifiable_webauth.rb +16 -0
- data/lib/booth/credentials/modes/webauth_addable.rb +26 -0
- data/lib/booth/credentials/modes/webauth_manageable.rb +16 -0
- data/lib/booth/credentials/modes/webauth_removable.rb +25 -0
- data/lib/booth/credentials/otp_authentication.rb +59 -0
- data/lib/booth/credentials/password_authentication.rb +72 -0
- data/lib/booth/credentials/webauth_challenge.rb +28 -0
- data/lib/booth/engine.rb +25 -0
- data/lib/booth/errors.rb +86 -0
- data/lib/booth/geolocation.rb +20 -0
- data/lib/booth/hooks/after_fetch.rb +54 -0
- data/lib/booth/hooks/before_logout.rb +29 -0
- data/lib/booth/hooks/serialize_from_session.rb +24 -0
- data/lib/booth/hooks/serialize_into_session.rb +14 -0
- data/lib/booth/logger.rb +41 -0
- data/lib/booth/logging.rb +59 -0
- data/lib/booth/method_object.rb +73 -0
- data/lib/booth/mode.rb +22 -0
- data/lib/booth/models/application_record.rb +7 -0
- data/lib/booth/models/audit.rb +24 -0
- data/lib/booth/models/authenticator.rb +45 -0
- data/lib/booth/models/concerns/modeable.rb +50 -0
- data/lib/booth/models/concerns/otpable.rb +37 -0
- data/lib/booth/models/concerns/passwordable.rb +58 -0
- data/lib/booth/models/contest.rb +55 -0
- data/lib/booth/models/contests/scopes/recently_created.rb +23 -0
- data/lib/booth/models/contests/scopes/recently_responded.rb +32 -0
- data/lib/booth/models/credential.rb +61 -0
- data/lib/booth/models/onboarding.rb +61 -0
- data/lib/booth/models/password_reset.rb +41 -0
- data/lib/booth/models/recovery.rb +32 -0
- data/lib/booth/models/registration.rb +10 -0
- data/lib/booth/models/session.rb +47 -0
- data/lib/booth/models/user_agent.rb +50 -0
- data/lib/booth/modes/base.rb +25 -0
- data/lib/booth/modes/username_and_password.rb +7 -0
- data/lib/booth/modes/username_and_webauth.rb +7 -0
- data/lib/booth/modes/username_password_and_otp.rb +7 -0
- data/lib/booth/modes/username_password_and_webauth.rb +7 -0
- data/lib/booth/onboardings/find.rb +35 -0
- data/lib/booth/onboardings/propagate_to_credential.rb +63 -0
- data/lib/booth/onboardings/step.rb +68 -0
- data/lib/booth/password_resets/create.rb +57 -0
- data/lib/booth/password_resets/find.rb +36 -0
- data/lib/booth/password_resets/propagate_to_credential.rb +36 -0
- data/lib/booth/password_resets/step.rb +18 -0
- data/lib/booth/recoveries/create.rb +45 -0
- data/lib/booth/request.rb +106 -0
- data/lib/booth/requests/agent.rb +14 -0
- data/lib/booth/requests/authentication.rb +47 -0
- data/lib/booth/requests/ip.rb +28 -0
- data/lib/booth/requests/return_path.rb +34 -0
- data/lib/booth/requests/session.rb +106 -0
- data/lib/booth/requests/storage.rb +62 -0
- data/lib/booth/requests/storages/login.rb +108 -0
- data/lib/booth/requests/storages/otp.rb +54 -0
- data/lib/booth/requests/storages/password.rb +49 -0
- data/lib/booth/requests/storages/password_reset.rb +35 -0
- data/lib/booth/requests/storages/recovery.rb +35 -0
- data/lib/booth/requests/storages/registration.rb +27 -0
- data/lib/booth/requests/storages/webauth.rb +38 -0
- data/lib/booth/requests/sudo.rb +110 -0
- data/lib/booth/routes/userland.rb +80 -0
- data/lib/booth/sessions/create_and_login.rb +46 -0
- data/lib/booth/sessions/historical_locations.rb +18 -0
- data/lib/booth/sessions/index.rb +59 -0
- data/lib/booth/sessions/revoke.rb +51 -0
- data/lib/booth/sessions/revoke_all_others.rb +43 -0
- data/lib/booth/sessions/to_passport.rb +51 -0
- data/lib/booth/syntaxes/contest_code.rb +58 -0
- data/lib/booth/syntaxes/email.rb +97 -0
- data/lib/booth/syntaxes/ip.rb +37 -0
- data/lib/booth/syntaxes/otp.rb +57 -0
- data/lib/booth/syntaxes/scope.rb +21 -0
- data/lib/booth/syntaxes/scope_comparison.rb +28 -0
- data/lib/booth/syntaxes/secret_key.rb +64 -0
- data/lib/booth/syntaxes/username.rb +85 -0
- data/lib/booth/syntaxes/uuid.rb +23 -0
- data/lib/booth/test/helpers.rb +63 -0
- data/lib/booth/test/support/assert_all_partials_were_covered.rb +63 -0
- data/lib/booth/test/support/assert_logged_in.rb +49 -0
- data/lib/booth/test/support/assert_logged_out.rb +30 -0
- data/lib/booth/test/support/assert_partial.rb +29 -0
- data/lib/booth/test/support/force_login.rb +26 -0
- data/lib/booth/test/support/get_session_value.rb +35 -0
- data/lib/booth/test/support/otp_code_from_session.rb +30 -0
- data/lib/booth/test/support/soft_reset_session.rb +22 -0
- data/lib/booth/test/userland/logins/missing_authenticators.rb +72 -0
- data/lib/booth/test/userland/logins/missing_onboarding.rb +35 -0
- data/lib/booth/test/userland/logins/username_and_password.rb +40 -0
- data/lib/booth/test/userland/logins/username_and_webauth.rb +75 -0
- data/lib/booth/test/userland/logins/username_password_and_otp.rb +45 -0
- data/lib/booth/test/userland/logins/username_password_and_webauth.rb +86 -0
- data/lib/booth/test/userland/onboardings/already_logged_in.rb +64 -0
- data/lib/booth/test/userland/onboardings/otp.rb +63 -0
- data/lib/booth/test/userland/onboardings/password.rb +49 -0
- data/lib/booth/test/userland/onboardings/timeout.rb +47 -0
- data/lib/booth/test/userland/otps/manage.rb +86 -0
- data/lib/booth/test/userland/password_resets/reset.rb +102 -0
- data/lib/booth/test/userland.rb +38 -0
- data/lib/booth/test/webauthn/disable.rb +17 -0
- data/lib/booth/test/webauthn/enable.rb +19 -0
- data/lib/booth/test/webauthn/virtual_authenticators/create.rb +38 -0
- data/lib/booth/test/webauthn/virtual_authenticators/destroy.rb +20 -0
- data/lib/booth/test.rb +53 -0
- data/lib/booth/to_struct.rb +11 -0
- data/lib/booth/userland/extract_flash_messages.rb +35 -0
- data/lib/booth/userland/logins/create.rb +28 -0
- data/lib/booth/userland/logins/destroy.rb +37 -0
- data/lib/booth/userland/logins/new.rb +70 -0
- data/lib/booth/userland/logins/transitions/create/choose_username.rb +41 -0
- data/lib/booth/userland/logins/transitions/create/enter_otp.rb +70 -0
- data/lib/booth/userland/logins/transitions/create/skip_remotes.rb +24 -0
- data/lib/booth/userland/logins/transitions/create/verify_password.rb +70 -0
- data/lib/booth/userland/logins/transitions/create/webauth_authentication_initiation.rb +55 -0
- data/lib/booth/userland/logins/transitions/create/webauth_authentication_verification.rb +80 -0
- data/lib/booth/userland/logins/transitions/new/already_logged_in.rb +21 -0
- data/lib/booth/userland/logins/transitions/new/fallible.rb +27 -0
- data/lib/booth/userland/logins/transitions/new/mode_first_time.rb +20 -0
- data/lib/booth/userland/logins/transitions/new/mode_username_and_password.rb +20 -0
- data/lib/booth/userland/logins/transitions/new/mode_username_and_webauth.rb +26 -0
- data/lib/booth/userland/logins/transitions/new/mode_username_password_and_otp.rb +24 -0
- data/lib/booth/userland/logins/transitions/new/mode_username_password_and_webauth.rb +24 -0
- data/lib/booth/userland/logins/transitions/new/no_username_chosen.rb +19 -0
- data/lib/booth/userland/logins/transitions/new/remote_session_available.rb +52 -0
- data/lib/booth/userland/logins/transitions/new/timed_out.rb +25 -0
- data/lib/booth/userland/onboardings/show.rb +74 -0
- data/lib/booth/userland/onboardings/transitions/update/choose_mode.rb +58 -0
- data/lib/booth/userland/onboardings/transitions/update/choose_password.rb +41 -0
- data/lib/booth/userland/onboardings/transitions/update/choose_webauth_nickname.rb +50 -0
- data/lib/booth/userland/onboardings/transitions/update/confirm_otp.rb +58 -0
- data/lib/booth/userland/onboardings/transitions/update/confirm_password.rb +49 -0
- data/lib/booth/userland/onboardings/transitions/update/register_otp.rb +31 -0
- data/lib/booth/userland/onboardings/transitions/update/reset_otp.rb +40 -0
- data/lib/booth/userland/onboardings/transitions/update/reset_password.rb +35 -0
- data/lib/booth/userland/onboardings/transitions/update/reset_webauth.rb +46 -0
- data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_initiation.rb +40 -0
- data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_verification.rb +59 -0
- data/lib/booth/userland/onboardings/transitions/update/webauth_registration_initiation.rb +46 -0
- data/lib/booth/userland/onboardings/transitions/update/webauth_registration_verification.rb +56 -0
- data/lib/booth/userland/onboardings/update.rb +68 -0
- data/lib/booth/userland/otps/destroy.rb +42 -0
- data/lib/booth/userland/otps/edit.rb +72 -0
- data/lib/booth/userland/otps/guards/manageable.rb +21 -0
- data/lib/booth/userland/otps/guards/sudo.rb +23 -0
- data/lib/booth/userland/otps/show.rb +36 -0
- data/lib/booth/userland/otps/sudo.rb +51 -0
- data/lib/booth/userland/otps/transitions/update/confirm.rb +84 -0
- data/lib/booth/userland/otps/transitions/update/register.rb +40 -0
- data/lib/booth/userland/otps/transitions/update/reset.rb +31 -0
- data/lib/booth/userland/otps/update.rb +34 -0
- data/lib/booth/userland/password_resets/create.rb +73 -0
- data/lib/booth/userland/password_resets/guards/logged_out.rb +21 -0
- data/lib/booth/userland/password_resets/new.rb +57 -0
- data/lib/booth/userland/password_resets/show.rb +77 -0
- data/lib/booth/userland/password_resets/transitions/update/choose_password.rb +48 -0
- data/lib/booth/userland/password_resets/transitions/update/confirm_password.rb +54 -0
- data/lib/booth/userland/password_resets/transitions/update/reset_password.rb +29 -0
- data/lib/booth/userland/password_resets/update.rb +65 -0
- data/lib/booth/userland/passwords/destroy.rb +41 -0
- data/lib/booth/userland/passwords/edit.rb +54 -0
- data/lib/booth/userland/passwords/guards/manageable.rb +21 -0
- data/lib/booth/userland/passwords/guards/removable.rb +21 -0
- data/lib/booth/userland/passwords/guards/sudo.rb +21 -0
- data/lib/booth/userland/passwords/remove.rb +34 -0
- data/lib/booth/userland/passwords/show.rb +32 -0
- data/lib/booth/userland/passwords/sudo.rb +55 -0
- data/lib/booth/userland/passwords/transitions/remove/step.rb +27 -0
- data/lib/booth/userland/passwords/transitions/update/choose_password.rb +62 -0
- data/lib/booth/userland/passwords/transitions/update/confirm_password.rb +82 -0
- data/lib/booth/userland/passwords/update.rb +33 -0
- data/lib/booth/userland/personal_contests/show.rb +60 -0
- data/lib/booth/userland/personal_contests/update.rb +37 -0
- data/lib/booth/userland/recoveries/create.rb +48 -0
- data/lib/booth/userland/recoveries/new.rb +35 -0
- data/lib/booth/userland/registrations/create.rb +56 -0
- data/lib/booth/userland/registrations/new.rb +39 -0
- data/lib/booth/userland/sessions/destroy_one_or_other.rb +41 -0
- data/lib/booth/userland/sessions/index.rb +27 -0
- data/lib/booth/userland/sessions/show.rb +31 -0
- data/lib/booth/userland/sessions/transitions/destroy/enter_password.rb +50 -0
- data/lib/booth/userland/sessions/transitions/destroy/enter_webauth.rb +56 -0
- data/lib/booth/userland/sessions/transitions/destroy/verify_password.rb +83 -0
- data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_initiation.rb +38 -0
- data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_verification.rb +61 -0
- data/lib/booth/userland/sessions/transitions/show/enter_webauth.rb +56 -0
- data/lib/booth/userland/webauths/create.rb +83 -0
- data/lib/booth/userland/webauths/destroy.rb +60 -0
- data/lib/booth/userland/webauths/guards/manageable.rb +21 -0
- data/lib/booth/userland/webauths/guards/sudo.rb +22 -0
- data/lib/booth/userland/webauths/index.rb +43 -0
- data/lib/booth/userland/webauths/new.rb +70 -0
- data/lib/booth/userland/webauths/sudo.rb +25 -0
- data/lib/booth/userland/webauths/transitions/create/authentication_initiation.rb +52 -0
- data/lib/booth/userland/webauths/transitions/create/authentication_verification.rb +64 -0
- data/lib/booth/userland/webauths/transitions/create/choose_nickname.rb +50 -0
- data/lib/booth/userland/webauths/transitions/create/registration_initiation.rb +61 -0
- data/lib/booth/userland/webauths/transitions/create/registration_verification.rb +68 -0
- data/lib/booth/userland/webauths/transitions/create/reset.rb +36 -0
- data/lib/booth/userland/webauths/transitions/new/step.rb +23 -0
- data/lib/booth/userland/webauths/transitions/sudo/authentication_initiation.rb +47 -0
- data/lib/booth/userland/webauths/transitions/sudo/authentication_verification.rb +34 -0
- data/lib/booth/userland.rb +192 -0
- data/lib/booth/version.rb +3 -0
- data/lib/booth/webauth/authentication_verification.rb +68 -0
- data/lib/booth/webauth/demand_user_verification.rb +29 -0
- data/lib/booth/webauth/options_for_create.rb +46 -0
- data/lib/booth/webauth/options_for_get.rb +29 -0
- data/lib/booth.rb +267 -0
- data/lib/generators/booth/migration/migration_generator.rb +25 -0
- data/lib/generators/booth/migration/templates/add_credential_to_users.erb +18 -0
- data/lib/generators/booth/migration/templates/create_booth_mode_types.erb +20 -0
- data/lib/generators/booth/migration/templates/create_booth_tables.erb +135 -0
- metadata +861 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Sessions
|
|
4
|
+
module Transitions
|
|
5
|
+
module Show
|
|
6
|
+
class EnterWebauth
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
!params[:webauth]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
if sudo.webauth?
|
|
15
|
+
if session_id_param
|
|
16
|
+
debug { 'Having webauth sudo, revoking the desired session...' }
|
|
17
|
+
return ::Booth::Sessions::Revoke.call credential_id: authentication.credential_id,
|
|
18
|
+
session_id: session_id_param
|
|
19
|
+
else
|
|
20
|
+
debug { 'Having webauth sudo, revoking all other sessions...' }
|
|
21
|
+
return ::Booth::Sessions::RevokeAllOthers.call credential_id: authentication.credential_id,
|
|
22
|
+
surviving_session_id: authentication.session_id
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if session_id_param
|
|
27
|
+
Tron.failure :need_sudo_to_show_session,
|
|
28
|
+
step: :enter_webauth_to_show,
|
|
29
|
+
session_id: session_id_param
|
|
30
|
+
else
|
|
31
|
+
Tron.failure :need_sudo_to_show_all_other_sessions,
|
|
32
|
+
step: :enter_webauth_to_show_all_others
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def session_id_param
|
|
39
|
+
# If params[:id] is a UUID, then it's an ID for a `Booth::Models::Session` in the DB.
|
|
40
|
+
# If params[:id] is something else, then it's just a WebAuth Ceremony argument.
|
|
41
|
+
::Booth::Syntaxes::Uuid.call(params[:id], raise_if_invalid: false).uuid
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def authentication
|
|
45
|
+
request.authentication
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def sudo
|
|
49
|
+
request.sudo
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
class Create
|
|
5
|
+
include ::Booth::Concerns::Action
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
request.must_be_post!
|
|
9
|
+
request.must_be_logged_in!
|
|
10
|
+
|
|
11
|
+
::Booth::Userland::Webauths::Guards::Manageable.call(credential:) { return _1 }
|
|
12
|
+
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return _1 }
|
|
13
|
+
|
|
14
|
+
do_ensure_authenticator
|
|
15
|
+
.on_success { do_require_editable_authentictor }
|
|
16
|
+
.on_success { do_transition }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def do_ensure_authenticator
|
|
22
|
+
return Tron.success :authenticator_exists if storage.authenticator
|
|
23
|
+
|
|
24
|
+
# TODO: test that one credential cannot have multiple deviceless authenticators
|
|
25
|
+
authenticator = credential.authenticators.find_or_initialize_by(device_id: nil)
|
|
26
|
+
authenticator.generate_webauth_id
|
|
27
|
+
|
|
28
|
+
webauth = ::Booth::Webauth::OptionsForCreate.call(
|
|
29
|
+
webauthn_id: authenticator.generate_webauth_id,
|
|
30
|
+
username: credential.username,
|
|
31
|
+
requires_user_verification: enforce_user_verification?
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
authenticator.challenge = webauth.challenge
|
|
35
|
+
|
|
36
|
+
if authenticator.save
|
|
37
|
+
storage.authenticator_id = authenticator.id
|
|
38
|
+
return Tron.success :authenticator_created if storage.authenticator
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Tron.failure :authenticator_creation_failed
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def do_require_editable_authentictor
|
|
45
|
+
if %i[register choose_nickname confirm].include?(storage.authenticator.step)
|
|
46
|
+
return Tron.success :authenticator_is_editable
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Tron.failure :authenticator_already_completed
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def transitions
|
|
53
|
+
[
|
|
54
|
+
::Booth::Userland::Webauths::Transitions::Create::AuthenticationInitiation,
|
|
55
|
+
::Booth::Userland::Webauths::Transitions::Create::AuthenticationVerification,
|
|
56
|
+
::Booth::Userland::Webauths::Transitions::Create::ChooseNickname,
|
|
57
|
+
::Booth::Userland::Webauths::Transitions::Create::RegistrationInitiation,
|
|
58
|
+
::Booth::Userland::Webauths::Transitions::Create::RegistrationVerification,
|
|
59
|
+
::Booth::Userland::Webauths::Transitions::Create::Reset,
|
|
60
|
+
]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# When switching from with-password login to passwordless,
|
|
64
|
+
# we need to enable a way to add user-verifiable webauth tokens,
|
|
65
|
+
# even though we are currently not passwordless. That's what the param is for.
|
|
66
|
+
def enforce_user_verification?
|
|
67
|
+
::Booth::Webauth::DemandUserVerification.call(
|
|
68
|
+
credential:,
|
|
69
|
+
force: request.params[:enforce_user_verification].present?
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def storage
|
|
74
|
+
request.storage.webauth
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def credential
|
|
78
|
+
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
class Destroy
|
|
5
|
+
include ::Booth::Concerns::Action
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
request.must_be_delete!
|
|
9
|
+
request.must_be_html!
|
|
10
|
+
request.must_be_logged_in!
|
|
11
|
+
|
|
12
|
+
::Booth::Userland::Webauths::Guards::Manageable.call(credential:) { return _1 }
|
|
13
|
+
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return _1 }
|
|
14
|
+
|
|
15
|
+
do_require_eligible_credential
|
|
16
|
+
.on_success { do_find_authenticator }
|
|
17
|
+
.on_success { do_destroy_authenticator }
|
|
18
|
+
.on_success { do_update_credential }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def do_require_eligible_credential
|
|
24
|
+
return Tron.success :can_remove_webauth if ::Booth::Credentials::Modes::WebauthRemovable.call(credential)
|
|
25
|
+
|
|
26
|
+
Tron.failure :credential_not_webauth_removable, public_message: I18n.t('booth.webauth_irremovable')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def do_find_authenticator
|
|
30
|
+
@authenticator = credential.authenticators.find_by(id: params[:id])
|
|
31
|
+
return Tron.success :found_authenticator if @authenticator
|
|
32
|
+
|
|
33
|
+
Tron.failure :authenticator_not_found, public_message: I18n.t('booth.authenticator_not_found')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def do_destroy_authenticator
|
|
37
|
+
return Tron.success :authenticator_removed if @authenticator.destroy
|
|
38
|
+
|
|
39
|
+
Tron.failure :could_not_remove_authenticator, public_message: I18n.t('booth.authenticator_removal_failed')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def do_update_credential
|
|
43
|
+
if credential.authenticators.present?
|
|
44
|
+
return Tron.success(:some_authenticator_removed, public_message: I18n.t('booth.some_authenticator_removed'))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if credential.mode_username_and_password!
|
|
48
|
+
return Tron.success :webauth_removed, public_message: I18n.t('booth.webauth_removed')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
raise "Could not switch Credential #{credential.id} to `username_and_password`: #{credential.errors.to_a}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def credential
|
|
55
|
+
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Guards
|
|
5
|
+
class Manageable
|
|
6
|
+
include ::Booth::Logging
|
|
7
|
+
include ::Booth::MethodObject
|
|
8
|
+
|
|
9
|
+
option :credential
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
return if ::Booth::Credentials::Modes::WebauthManageable.call(credential)
|
|
13
|
+
|
|
14
|
+
debug { 'Webauth is not relevant to this credential' }
|
|
15
|
+
yield Tron.failure :webauth_not_configurable, public_message: I18n.t('booth.webauth_unavailable')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Guards
|
|
5
|
+
class Sudo
|
|
6
|
+
include ::Booth::MethodObject
|
|
7
|
+
|
|
8
|
+
option :request
|
|
9
|
+
option :credential
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
return if credential.mode_first_time?
|
|
13
|
+
return if credential.mode_username_and_password?
|
|
14
|
+
return if credential.mode_username_password_and_otp?
|
|
15
|
+
|
|
16
|
+
request.sudo.guard_with_webauth { yield _1 }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
class Index
|
|
5
|
+
include ::Booth::Concerns::Action
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
request.must_be_get!
|
|
9
|
+
request.must_be_html!
|
|
10
|
+
request.must_be_logged_in!
|
|
11
|
+
|
|
12
|
+
::Booth::Userland::Webauths::Guards::Manageable.call(credential:) { return _1 }
|
|
13
|
+
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return _1 }
|
|
14
|
+
|
|
15
|
+
do_index
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def do_index
|
|
21
|
+
request.storage.webauth.reset if params[:reset]
|
|
22
|
+
|
|
23
|
+
Tron.success :current_webauth, step: :index,
|
|
24
|
+
authenticators: authenticator_structs
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def authenticator_structs
|
|
28
|
+
credential.authenticators.registered_scope.map do |authenticator|
|
|
29
|
+
::Booth::ToStruct.call(
|
|
30
|
+
authenticator.attributes
|
|
31
|
+
.symbolize_keys
|
|
32
|
+
.slice(:id, :confirmed_at, :nickname, :supports_user_verification)
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def credential
|
|
38
|
+
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
class New
|
|
5
|
+
include ::Booth::Concerns::Action
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
request.must_be_get!
|
|
9
|
+
request.must_be_html!
|
|
10
|
+
request.must_be_logged_in!
|
|
11
|
+
|
|
12
|
+
::Booth::Userland::Webauths::Guards::Manageable.call(credential:) { return _1 }
|
|
13
|
+
|
|
14
|
+
# This check must come before the Sudo requirement.
|
|
15
|
+
if authenticator&.confirmed?
|
|
16
|
+
return Tron.success :completed, step: :completed,
|
|
17
|
+
nickname: authenticator&.nickname,
|
|
18
|
+
should_add_more_authenticators: should_add_more_authenticators?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return _1 }
|
|
22
|
+
|
|
23
|
+
do_new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def do_new
|
|
29
|
+
debug { "WebAuthn Authenticator registration in step #{step}" }
|
|
30
|
+
# debug { authenticator.inspect }
|
|
31
|
+
|
|
32
|
+
Tron.success :add_webauth, step:,
|
|
33
|
+
nickname: authenticator&.nickname,
|
|
34
|
+
enforce_user_verification: enforce_user_verification?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def step
|
|
38
|
+
return :register unless authenticator
|
|
39
|
+
|
|
40
|
+
authenticator.step
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def should_add_more_authenticators?
|
|
44
|
+
return true if credential.authenticators.registered_scope.count < 2
|
|
45
|
+
return false unless request.authentication.mode == :username_and_webauth
|
|
46
|
+
|
|
47
|
+
credential.authenticators.registered_scope.supports_user_verification_scope.count < 2
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# When switching from with-password login to passwordless,
|
|
51
|
+
# we need to enable a way to add user-verifiable webauth tokens,
|
|
52
|
+
# even though we are currently not passwordless. That's what the param is for.
|
|
53
|
+
def enforce_user_verification?
|
|
54
|
+
::Booth::Webauth::DemandUserVerification.call(
|
|
55
|
+
credential:,
|
|
56
|
+
force: params[:enforce_user_verification].present?
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def authenticator
|
|
61
|
+
request.storage.webauth.authenticator
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def credential
|
|
65
|
+
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
class Sudo
|
|
5
|
+
include ::Booth::Concerns::Action
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
request.must_be_post!
|
|
9
|
+
request.must_be_logged_in!
|
|
10
|
+
|
|
11
|
+
do_transition
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def transitions
|
|
17
|
+
[
|
|
18
|
+
::Booth::Userland::Webauths::Transitions::Sudo::AuthenticationInitiation,
|
|
19
|
+
::Booth::Userland::Webauths::Transitions::Sudo::AuthenticationVerification,
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Transitions
|
|
5
|
+
module Create
|
|
6
|
+
class AuthenticationInitiation
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
params.key?(:test) && !params&.key?(:webauth)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
do_challenge
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def do_challenge
|
|
20
|
+
webauth = ::Booth::Webauth::OptionsForGet.call(
|
|
21
|
+
allowed_device_ids: authenticator.device_id,
|
|
22
|
+
requires_user_verification: enforce_user_verification?
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
debug { "Remembering test challenge #{webauth.challenge.inspect}" }
|
|
26
|
+
|
|
27
|
+
if authenticator.update challenge: webauth.challenge
|
|
28
|
+
Tron.success :test_challenge_created, public_json: webauth.as_json, http_status: :created
|
|
29
|
+
else
|
|
30
|
+
Tron.failure :storing_test_challenge_failed
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def authenticator
|
|
35
|
+
request.storage.webauth.authenticator
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def credential
|
|
39
|
+
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# I.e. modes `first_time` and `username_and_webauth`.
|
|
43
|
+
# TODO: Also when force-adding an authenticator for removing a password.
|
|
44
|
+
def enforce_user_verification?
|
|
45
|
+
!credential.passworded?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Transitions
|
|
5
|
+
module Create
|
|
6
|
+
class AuthenticationVerification
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
params.key?(:test) && params[:webauth].key?(:rawId)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
request.must_be_json!
|
|
15
|
+
|
|
16
|
+
do_verify_response
|
|
17
|
+
.on_success { do_persist_confirmation }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def do_verify_response
|
|
23
|
+
debug { 'Verifying challenge...' }
|
|
24
|
+
# TODO: Replate with ::Booth::Webauth::AuthenticationVerification
|
|
25
|
+
webauth.verify(authenticator.challenge,
|
|
26
|
+
public_key: authenticator.public_key,
|
|
27
|
+
sign_count: authenticator.sign_count)
|
|
28
|
+
|
|
29
|
+
Tron.success :challenge_response_correct
|
|
30
|
+
rescue WebAuthn::Error => e
|
|
31
|
+
debug { "Webauth Handshake failed: #{e.message}" }
|
|
32
|
+
Tron.failure :invalid_challenge_response
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def do_persist_confirmation
|
|
36
|
+
confirmation = ::Booth::Authenticators::Confirm.call(authenticator:,
|
|
37
|
+
sign_count: webauth.sign_count)
|
|
38
|
+
|
|
39
|
+
if confirmation.success?
|
|
40
|
+
return Tron.success(:webauth_authentication_verification_successful, public_json: {},
|
|
41
|
+
http_status: :created)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Tron.failure :storing_response_failed, public_json: {},
|
|
45
|
+
http_status: :bad_request
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def webauth
|
|
49
|
+
@webauth ||= ::WebAuthn::Credential.from_get(webauth_params)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def webauth_params
|
|
53
|
+
params.require(:webauth).permit!
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def authenticator
|
|
57
|
+
request.storage.webauth.authenticator
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Transitions
|
|
5
|
+
module Create
|
|
6
|
+
class ChooseNickname
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
params.dig(:webauth, :nickname)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
do_check_blank_nickname
|
|
15
|
+
.on_success { do_update_authenticator }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def do_check_blank_nickname
|
|
21
|
+
return Tron.success :nickname_is_present if nickname_param.present?
|
|
22
|
+
|
|
23
|
+
debug { 'The nickname was blank' }
|
|
24
|
+
Tron.failure :nickname_is_blank, public_message: I18n.t('booth.blank_nickname')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def do_update_authenticator
|
|
28
|
+
if authenticator.update nickname: nickname_param
|
|
29
|
+
debug { 'The nickname successfully changed' }
|
|
30
|
+
Tron.success :nickname_saved
|
|
31
|
+
else
|
|
32
|
+
public_message = authenticator.errors.to_a.to_sentence
|
|
33
|
+
debug { "The nickname could not be updated: #{public_message}" }
|
|
34
|
+
Tron.failure :nickname_failed, public_message:
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def nickname_param
|
|
39
|
+
params.require(:webauth).permit(:nickname)[:nickname]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def authenticator
|
|
43
|
+
request.storage.webauth.authenticator
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Transitions
|
|
5
|
+
module Create
|
|
6
|
+
class RegistrationInitiation
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
params&.key?(:register) && !params&.key?(:webauth)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
do_require_authenticator
|
|
15
|
+
.on_success { do_challenge }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def do_require_authenticator
|
|
21
|
+
return Tron.success :authenticator_waiting_for_registration if storage.authenticator.step == :register
|
|
22
|
+
|
|
23
|
+
Tron.failure :authenticator_not_registerable
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def do_challenge
|
|
27
|
+
debug { "Remembering registration challenge #{webauth.challenge.inspect}" }
|
|
28
|
+
|
|
29
|
+
if storage.authenticator.update challenge: webauth.challenge,
|
|
30
|
+
supports_user_verification: enforce_user_verification?
|
|
31
|
+
return Tron.success :registration_challenge_created, public_json: webauth.as_json,
|
|
32
|
+
http_status: :created
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Tron.failure :storing_registration_challenge_failed
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def webauth
|
|
39
|
+
@webauth ||= ::Booth::Webauth::OptionsForCreate.call(
|
|
40
|
+
webauthn_id: storage.authenticator.webauthn_id,
|
|
41
|
+
username: storage.authenticator.credential.username,
|
|
42
|
+
requires_user_verification: enforce_user_verification?
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def enforce_user_verification?
|
|
47
|
+
::Booth::Webauth::DemandUserVerification.call(
|
|
48
|
+
credential: storage.authenticator.credential,
|
|
49
|
+
force: request.params[:enforce_user_verification].present?
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def storage
|
|
54
|
+
request.storage.webauth
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Userland
|
|
3
|
+
module Webauths
|
|
4
|
+
module Transitions
|
|
5
|
+
module Create
|
|
6
|
+
class RegistrationVerification
|
|
7
|
+
include ::Booth::Concerns::Transition
|
|
8
|
+
|
|
9
|
+
def self.applicable?(params:)
|
|
10
|
+
params&.key?(:register) && params[:webauth]&.key?(:rawId)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
do_verify_response
|
|
15
|
+
.on_success { do_persist_keys }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def do_verify_response
|
|
21
|
+
debug { "Verifying challenge #{authenticator.challenge.inspect}" }
|
|
22
|
+
# TODO: Replate with ::Booth::Webauth::RegistrationVerification (?)
|
|
23
|
+
webauth.verify(authenticator.challenge)
|
|
24
|
+
|
|
25
|
+
Tron.success :challenge_response_correct
|
|
26
|
+
rescue WebAuthn::Error => e
|
|
27
|
+
debug { "Webauth Handshake failed: #{e.message}" }
|
|
28
|
+
Tron.failure :invalid_challenge_response
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def do_persist_keys
|
|
32
|
+
debug { "Persisting authenticator information, the sign count is now at #{webauth.sign_count}..." }
|
|
33
|
+
if authenticator.update(device_id: ::Base64.strict_encode64(webauth.raw_id),
|
|
34
|
+
public_key: webauth.public_key,
|
|
35
|
+
sign_count: webauth.sign_count)
|
|
36
|
+
|
|
37
|
+
return Tron.success :challenge_accepted, public_json: {}, http_status: :created
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Tron.failure :storing_response_failed
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def webauth
|
|
44
|
+
@webauth ||= ::WebAuthn::Credential.from_create(webauth_params)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def webauth_params
|
|
48
|
+
params.require(:webauth).permit!
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# def credential
|
|
52
|
+
# @credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
53
|
+
# end
|
|
54
|
+
|
|
55
|
+
def mode_after_update
|
|
56
|
+
@mode_after_update ||= ::Booth::Webauth::ModeAfterAdd.call(credential:,
|
|
57
|
+
authenticator:)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def authenticator
|
|
61
|
+
request.storage.webauth.authenticator
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|