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,162 @@
|
|
|
1
|
+
/// <reference path='webauthn-json.ts' />
|
|
2
|
+
//= require rails-ujs
|
|
3
|
+
//= require @github/webauthn-json/dist/browser-global/webauthn-json.browser-global
|
|
4
|
+
var Booth;
|
|
5
|
+
(function (Booth) {
|
|
6
|
+
function reloadSoon() {
|
|
7
|
+
const seconds = Math.floor(Math.random() * 3) + 5; // 4,5,6 or 7 to avoid request bursts
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
console.debug('Reloading page...');
|
|
10
|
+
window.location.reload();
|
|
11
|
+
}, 1000 * seconds);
|
|
12
|
+
}
|
|
13
|
+
Booth.reloadSoon = reloadSoon;
|
|
14
|
+
let Webauth;
|
|
15
|
+
(function (Webauth) {
|
|
16
|
+
class Registration {
|
|
17
|
+
constructor(form, challengeData) {
|
|
18
|
+
this.form = form;
|
|
19
|
+
this.challengeData = challengeData;
|
|
20
|
+
this.call();
|
|
21
|
+
}
|
|
22
|
+
call() {
|
|
23
|
+
console.debug('WebAuthn Registration Ceremony - Initiation Phase Part 2 - START');
|
|
24
|
+
webauthnJSON.create({ publicKey: this.challengeData }).then((responseData) => {
|
|
25
|
+
console.debug(`WebAuthn Registration Ceremony - Initiation Phase Part 2 - SUCCESS - ${JSON.stringify(responseData)}`);
|
|
26
|
+
new Verification(this.form, responseData);
|
|
27
|
+
}).catch((error) => {
|
|
28
|
+
console.error(`WebAuthn Registration Ceremony - Verification Phase - ERROR - ${error.name} - ${error.message}`);
|
|
29
|
+
if (error.name == 'NotAllowedError') {
|
|
30
|
+
// Every webauth registration form needs this localized error message.
|
|
31
|
+
alert(this.form.dataset.boothIncompatibleDeviceMessage);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
alert(error.message);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
Webauth.Registration = Registration;
|
|
40
|
+
class Authentication {
|
|
41
|
+
constructor(form, challengeData) {
|
|
42
|
+
this.form = form;
|
|
43
|
+
this.challengeData = challengeData;
|
|
44
|
+
this.call();
|
|
45
|
+
}
|
|
46
|
+
call() {
|
|
47
|
+
console.debug('WebAuthn Authentication Ceremony - Initiation Phase Part 2 - START');
|
|
48
|
+
webauthnJSON.get({ publicKey: this.challengeData }).then((responseData) => {
|
|
49
|
+
console.debug(`WebAuthn Authentication Ceremony - Initiation Phase Part 2 - SUCCESS - ${JSON.stringify(responseData)}`);
|
|
50
|
+
new Verification(this.form, responseData);
|
|
51
|
+
}).catch((error) => {
|
|
52
|
+
console.error(`WebAuthn Authentication Ceremony - Verification Phase - ERROR - ${error.name} - ${error.message}`);
|
|
53
|
+
if (error.name == 'NotAllowedError') {
|
|
54
|
+
// Every webauth authentication form needs this localized error message.
|
|
55
|
+
alert(this.form.dataset.boothUnknownDeviceMessage);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
alert(error.message);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
Webauth.Authentication = Authentication;
|
|
64
|
+
class Verification {
|
|
65
|
+
constructor(form, webauth) {
|
|
66
|
+
this.form = form;
|
|
67
|
+
this.webauth = webauth;
|
|
68
|
+
this.call();
|
|
69
|
+
}
|
|
70
|
+
call() {
|
|
71
|
+
console.debug(`WebAuthn Ceremony - Verification Phase - START - ${this.formMethod} ${this.formUrl}`);
|
|
72
|
+
const options = {
|
|
73
|
+
body: JSON.stringify(this.webauth),
|
|
74
|
+
method: this.formMethod,
|
|
75
|
+
credentials: 'same-origin',
|
|
76
|
+
// Rails should not redirect us anywhere when making API calls.
|
|
77
|
+
// But in order to avoid mistakes let's be sure and never follow anywhere.
|
|
78
|
+
redirect: 'manual',
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-type': 'application/json',
|
|
81
|
+
'Accept': 'application/json',
|
|
82
|
+
'X-CSRF-Token': this.csrfToken
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
fetch(this.formUrl, options)
|
|
86
|
+
.then((response) => {
|
|
87
|
+
// Conventionally the server returns 201 on successful WebAuth verification.
|
|
88
|
+
// That makes it easier to detect a mistake (most other server responses will usually return 200).
|
|
89
|
+
if (response.status == 201) {
|
|
90
|
+
console.debug(`WebAuthn Ceremony - Verification Phase - SUCCESS - ${response.statusText}`);
|
|
91
|
+
console.info(`Reloading page with a GET request: ${window.location.href}`);
|
|
92
|
+
window.location.href = window.location.href;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
96
|
+
const message = `WebAuthn Ceremony - Verification Phase - ERROR - ${response.statusText} ${response.body}`;
|
|
97
|
+
console.debug(message);
|
|
98
|
+
alert(message);
|
|
99
|
+
}
|
|
100
|
+
}).catch((error) => {
|
|
101
|
+
const message = 'Please check your Internet connection and try again later.';
|
|
102
|
+
console.error(message);
|
|
103
|
+
alert(message);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
get formUrl() {
|
|
107
|
+
return this.form.action;
|
|
108
|
+
}
|
|
109
|
+
get formMethod() {
|
|
110
|
+
return this.form.querySelector('input[name="_method"]')?.value?.toUpperCase() || 'POST';
|
|
111
|
+
}
|
|
112
|
+
get csrfToken() {
|
|
113
|
+
return document.querySelector('meta[name="csrf-token"]')?.content;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
Webauth.Verification = Verification;
|
|
117
|
+
})(Webauth = Booth.Webauth || (Booth.Webauth = {}));
|
|
118
|
+
})(Booth || (Booth = {}));
|
|
119
|
+
// --------------
|
|
120
|
+
// Initialization
|
|
121
|
+
// --------------
|
|
122
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
123
|
+
document.querySelectorAll('.js-booth-webauth-registration').forEach((form) => {
|
|
124
|
+
form.addEventListener('ajax:before', () => {
|
|
125
|
+
console.debug('WebAuthn Registration Ceremony - Initiation Phase Part 1 - START');
|
|
126
|
+
});
|
|
127
|
+
form.addEventListener('ajax:success', (event) => {
|
|
128
|
+
const [data, status, xhr] = event.detail;
|
|
129
|
+
console.debug(`WebAuthn Registration Ceremony - Initiation Phase Part 1 - SUCCESS - ${JSON.stringify(data)}`);
|
|
130
|
+
new Booth.Webauth.Registration(form, data);
|
|
131
|
+
});
|
|
132
|
+
form.addEventListener('ajax:error', (event) => {
|
|
133
|
+
const [data, status, xhr] = event.detail;
|
|
134
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
135
|
+
// I don't think we need to reload the page on the registration page.
|
|
136
|
+
const message = `WebAuthn Registration Ceremony - Initiation Phase Part 1 - ERROR - ${xhr.responseText}`;
|
|
137
|
+
console.error(message);
|
|
138
|
+
alert(message);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
document.querySelectorAll('.js-booth-webauth-authentication').forEach((form) => {
|
|
142
|
+
form.addEventListener('ajax:before', () => {
|
|
143
|
+
console.debug('WebAuthn Authentication Ceremony - Initiation Phase Part 1 - START');
|
|
144
|
+
});
|
|
145
|
+
form.addEventListener('ajax:success', (event) => {
|
|
146
|
+
const [data, status, xhr] = event.detail;
|
|
147
|
+
console.debug(`WebAuthn Authentication Ceremony - Initiation Phase Part 1 - SUCCESS - ${JSON.stringify(data)}`);
|
|
148
|
+
new Booth.Webauth.Authentication(form, data);
|
|
149
|
+
});
|
|
150
|
+
form.addEventListener('ajax:error', (event) => {
|
|
151
|
+
const [data, status, xhr] = event.detail;
|
|
152
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
153
|
+
// On the authentication page there are timeout restrictions, it would be a good idea to reload page.
|
|
154
|
+
const message = `WebAuthn Authentication Ceremony - Initiation Phase Part 1 - ERROR - ${xhr.responseText}`;
|
|
155
|
+
console.error(message);
|
|
156
|
+
alert(message);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
if (document.querySelector('.js-booth-polling'))
|
|
160
|
+
Booth.reloadSoon();
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=all.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.js","sourceRoot":"","sources":["webauthn-json.ts","booth.ts"],"names":[],"mappings":"ACAA,yCAAyC;AAEzC,qBAAqB;AACrB,kFAAkF;AAElF,IAAU,KAAK,CAqId;AArID,WAAU,KAAK;IACb,SAAgB,UAAU;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA,CAAC,qCAAqC;QAEvF,UAAU,CAAC,GAAG,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;YAClC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAA;QAC1B,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAA;IACpB,CAAC;IAPe,gBAAU,aAOzB,CAAA;IAED,IAAiB,OAAO,CA0HvB;IA1HD,WAAiB,OAAO;QACtB,MAAa,YAAY;YAIvB,YAAY,IAAqB,EAAE,aAAkB;gBACnD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;gBAChB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;gBAClC,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;YAEO,IAAI;gBACV,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAA;gBACjF,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;oBAC3E,OAAO,CAAC,KAAK,CAAC,wEAAwE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;oBACrH,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;gBAE3C,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAmB,EAAE,EAAE;oBAC/B,OAAO,CAAC,KAAK,CAAC,iEAAiE,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;oBAE/G,IAAI,KAAK,CAAC,IAAI,IAAI,iBAAiB,EAAE;wBACnC,sEAAsE;wBACtE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAA;qBACxD;yBAAM;wBACL,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;qBACrB;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC;SACF;QA3BY,oBAAY,eA2BxB,CAAA;QAED,MAAa,cAAc;YAIzB,YAAY,IAAqB,EAAE,aAAkB;gBACnD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;gBAChB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;gBAClC,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;YAEO,IAAI;gBACV,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAA;gBACnF,YAAY,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;oBACxE,OAAO,CAAC,KAAK,CAAC,0EAA0E,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;oBACvH,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;gBAE3C,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACjB,OAAO,CAAC,KAAK,CAAC,mEAAmE,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;oBAEjH,IAAI,KAAK,CAAC,IAAI,IAAI,iBAAiB,EAAE;wBACnC,wEAAwE;wBACxE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;qBACnD;yBAAM;wBACL,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;qBACrB;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC;SACF;QA3BY,sBAAc,iBA2B1B,CAAA;QAED,MAAa,YAAY;YAIvB,YAAmB,IAAqB,EAAE,OAAY;gBACpD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;gBAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;gBACtB,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;YAEO,IAAI;gBACV,OAAO,CAAC,KAAK,CAAC,oDAAoD,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;gBAEpG,MAAM,OAAO,GAAgB;oBAC3B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;oBAClC,MAAM,EAAE,IAAI,CAAC,UAAU;oBACvB,WAAW,EAAE,aAAa;oBAC1B,+DAA+D;oBAC/D,0EAA0E;oBAC1E,QAAQ,EAAE,QAAQ;oBAClB,OAAO,EAAE;wBACT,cAAc,EAAE,kBAAkB;wBAClC,QAAQ,EAAE,kBAAkB;wBAC5B,cAAc,EAAE,IAAI,CAAC,SAAS;qBAC7B;iBACF,CAAA;gBAED,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;qBAC3B,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;oBAEjB,4EAA4E;oBAC5E,kGAAkG;oBAClG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE;wBAC1B,OAAO,CAAC,KAAK,CAAC,sDAAsD,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;wBAC1F,OAAO,CAAC,IAAI,CAAC,sCAAsC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;wBAC1E,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;qBAE5C;yBAAM;wBACL,8DAA8D;wBAC9D,MAAM,OAAO,GAAG,oDAAoD,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAA;wBAC1G,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;wBACtB,KAAK,CAAC,OAAO,CAAC,CAAA;qBACf;gBAEH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACjB,MAAM,OAAO,GAAG,4DAA4D,CAAA;oBAC5E,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;oBACtB,KAAK,CAAC,OAAO,CAAC,CAAA;gBAChB,CAAC,CAAC,CAAA;YACJ,CAAC;YAED,IAAY,OAAO;gBACjB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;YACzB,CAAC;YAED,IAAY,UAAU;gBACpB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAmB,uBAAuB,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,MAAM,CAAA;YAC3G,CAAC;YAED,IAAY,SAAS;gBACnB,OAAO,QAAQ,CAAC,aAAa,CAAkB,yBAAyB,CAAC,EAAE,OAAO,CAAA;YACpF,CAAC;SACF;QA9DY,oBAAY,eA8DxB,CAAA;IACH,CAAC,EA1HgB,OAAO,GAAP,aAAO,KAAP,aAAO,QA0HvB;AACH,CAAC,EArIS,KAAK,KAAL,KAAK,QAqId;AAKD,iBAAiB;AACjB,iBAAiB;AACjB,iBAAiB;AAEjB,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE;IAE5C,QAAQ,CAAC,gBAAgB,CAAC,gCAAgC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAqB,EAAE,EAAE;QAC5F,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE;YACxC,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAA;QACnF,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,KAAkB,EAAE,EAAE;YAC3D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;YACxC,OAAO,CAAC,KAAK,CAAC,wEAAwE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC7G,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC,KAAkB,EAAE,EAAE;YACzD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;YACxC,8DAA8D;YAC9D,2EAA2E;YAC3E,MAAM,OAAO,GAAG,sEAAsE,GAAG,CAAC,YAAY,EAAE,CAAA;YACxG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACtB,KAAK,CAAC,OAAO,CAAC,CAAA;QAChB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAqB,EAAE,EAAE;QAC9F,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE;YACxC,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAA;QACrF,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,KAAkB,EAAE,EAAE;YAC3D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;YACxC,OAAO,CAAC,KAAK,CAAC,0EAA0E,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC/G,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC,KAAkB,EAAE,EAAE;YACzD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;YACxC,8DAA8D;YAC9D,2GAA2G;YAC3G,MAAM,OAAO,GAAG,wEAAwE,GAAG,CAAC,YAAY,EAAE,CAAA;YAC1G,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACtB,KAAK,CAAC,OAAO,CAAC,CAAA;QAChB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,QAAQ,CAAC,aAAa,CAAC,mBAAmB,CAAC;QAAE,KAAK,CAAC,UAAU,EAAE,CAAA;AAErE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/// <reference path='webauthn-json.ts' />
|
|
2
|
+
|
|
3
|
+
//= require rails-ujs
|
|
4
|
+
//= require @github/webauthn-json/dist/browser-global/webauthn-json.browser-global
|
|
5
|
+
|
|
6
|
+
namespace Booth {
|
|
7
|
+
export function reloadSoon() {
|
|
8
|
+
const seconds = Math.floor(Math.random() * 3) + 5 // 4,5,6 or 7 to avoid request bursts
|
|
9
|
+
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
console.debug('Reloading page...')
|
|
12
|
+
window.location.reload()
|
|
13
|
+
}, 1000 * seconds)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export namespace Webauth {
|
|
17
|
+
export class Registration {
|
|
18
|
+
private form: HTMLFormElement
|
|
19
|
+
private challengeData: any
|
|
20
|
+
|
|
21
|
+
constructor(form: HTMLFormElement, challengeData: any) {
|
|
22
|
+
this.form = form
|
|
23
|
+
this.challengeData = challengeData
|
|
24
|
+
this.call()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private call() {
|
|
28
|
+
console.debug('WebAuthn Registration Ceremony - Initiation Phase Part 2 - START')
|
|
29
|
+
webauthnJSON.create({ publicKey: this.challengeData }).then((responseData) => {
|
|
30
|
+
console.debug(`WebAuthn Registration Ceremony - Initiation Phase Part 2 - SUCCESS - ${JSON.stringify(responseData)}`)
|
|
31
|
+
new Verification(this.form, responseData)
|
|
32
|
+
|
|
33
|
+
}).catch((error: DOMException) => {
|
|
34
|
+
console.error(`WebAuthn Registration Ceremony - Verification Phase - ERROR - ${error.name} - ${error.message}`)
|
|
35
|
+
|
|
36
|
+
if (error.name == 'NotAllowedError') {
|
|
37
|
+
// Every webauth registration form needs this localized error message.
|
|
38
|
+
alert(this.form.dataset.boothIncompatibleDeviceMessage)
|
|
39
|
+
} else {
|
|
40
|
+
alert(error.message)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class Authentication {
|
|
47
|
+
private form: HTMLFormElement
|
|
48
|
+
private challengeData: any
|
|
49
|
+
|
|
50
|
+
constructor(form: HTMLFormElement, challengeData: any) {
|
|
51
|
+
this.form = form
|
|
52
|
+
this.challengeData = challengeData
|
|
53
|
+
this.call()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private call() {
|
|
57
|
+
console.debug('WebAuthn Authentication Ceremony - Initiation Phase Part 2 - START')
|
|
58
|
+
webauthnJSON.get({ publicKey: this.challengeData }).then((responseData) => {
|
|
59
|
+
console.debug(`WebAuthn Authentication Ceremony - Initiation Phase Part 2 - SUCCESS - ${JSON.stringify(responseData)}`)
|
|
60
|
+
new Verification(this.form, responseData)
|
|
61
|
+
|
|
62
|
+
}).catch((error) => {
|
|
63
|
+
console.error(`WebAuthn Authentication Ceremony - Verification Phase - ERROR - ${error.name} - ${error.message}`)
|
|
64
|
+
|
|
65
|
+
if (error.name == 'NotAllowedError') {
|
|
66
|
+
// Every webauth authentication form needs this localized error message.
|
|
67
|
+
alert(this.form.dataset.boothUnknownDeviceMessage)
|
|
68
|
+
} else {
|
|
69
|
+
alert(error.message)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class Verification {
|
|
76
|
+
private form: HTMLFormElement
|
|
77
|
+
private webauth: any
|
|
78
|
+
|
|
79
|
+
public constructor(form: HTMLFormElement, webauth: any) {
|
|
80
|
+
this.form = form
|
|
81
|
+
this.webauth = webauth
|
|
82
|
+
this.call()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private call() {
|
|
86
|
+
console.debug(`WebAuthn Ceremony - Verification Phase - START - ${this.formMethod} ${this.formUrl}`)
|
|
87
|
+
|
|
88
|
+
const options: RequestInit = {
|
|
89
|
+
body: JSON.stringify(this.webauth),
|
|
90
|
+
method: this.formMethod,
|
|
91
|
+
credentials: 'same-origin',
|
|
92
|
+
// Rails should not redirect us anywhere when making API calls.
|
|
93
|
+
// But in order to avoid mistakes let's be sure and never follow anywhere.
|
|
94
|
+
redirect: 'manual',
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-type': 'application/json',
|
|
97
|
+
'Accept': 'application/json',
|
|
98
|
+
'X-CSRF-Token': this.csrfToken
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fetch(this.formUrl, options)
|
|
103
|
+
.then((response) => {
|
|
104
|
+
|
|
105
|
+
// Conventionally the server returns 201 on successful WebAuth verification.
|
|
106
|
+
// That makes it easier to detect a mistake (most other server responses will usually return 200).
|
|
107
|
+
if (response.status == 201) {
|
|
108
|
+
console.debug(`WebAuthn Ceremony - Verification Phase - SUCCESS - ${response.statusText}`)
|
|
109
|
+
console.info(`Reloading page with a GET request: ${window.location.href}`)
|
|
110
|
+
window.location.href = window.location.href
|
|
111
|
+
|
|
112
|
+
} else {
|
|
113
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
114
|
+
const message = `WebAuthn Ceremony - Verification Phase - ERROR - ${response.statusText} ${response.body}`
|
|
115
|
+
console.debug(message)
|
|
116
|
+
alert(message)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}).catch((error) => {
|
|
120
|
+
const message = 'Please check your Internet connection and try again later.'
|
|
121
|
+
console.error(message)
|
|
122
|
+
alert(message)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private get formUrl() {
|
|
127
|
+
return this.form.action
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private get formMethod() {
|
|
131
|
+
return this.form.querySelector<HTMLInputElement>('input[name="_method"]')?.value?.toUpperCase() || 'POST'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private get csrfToken() {
|
|
135
|
+
return document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// --------------
|
|
145
|
+
// Initialization
|
|
146
|
+
// --------------
|
|
147
|
+
|
|
148
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
149
|
+
|
|
150
|
+
document.querySelectorAll('.js-booth-webauth-registration').forEach((form: HTMLFormElement) => {
|
|
151
|
+
form.addEventListener('ajax:before', () => {
|
|
152
|
+
console.debug('WebAuthn Registration Ceremony - Initiation Phase Part 1 - START')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
form.addEventListener('ajax:success', (event: CustomEvent) => {
|
|
156
|
+
const [data, status, xhr] = event.detail
|
|
157
|
+
console.debug(`WebAuthn Registration Ceremony - Initiation Phase Part 1 - SUCCESS - ${JSON.stringify(data)}`)
|
|
158
|
+
new Booth.Webauth.Registration(form, data)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
form.addEventListener('ajax:error', (event: CustomEvent) => {
|
|
162
|
+
const [data, status, xhr] = event.detail
|
|
163
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
164
|
+
// I don't think we need to reload the page on the registration page.
|
|
165
|
+
const message = `WebAuthn Registration Ceremony - Initiation Phase Part 1 - ERROR - ${xhr.responseText}`
|
|
166
|
+
console.error(message)
|
|
167
|
+
alert(message)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
document.querySelectorAll('.js-booth-webauth-authentication').forEach((form: HTMLFormElement) => {
|
|
172
|
+
form.addEventListener('ajax:before', () => {
|
|
173
|
+
console.debug('WebAuthn Authentication Ceremony - Initiation Phase Part 1 - START')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
form.addEventListener('ajax:success', (event: CustomEvent) => {
|
|
177
|
+
const [data, status, xhr] = event.detail
|
|
178
|
+
console.debug(`WebAuthn Authentication Ceremony - Initiation Phase Part 1 - SUCCESS - ${JSON.stringify(data)}`)
|
|
179
|
+
new Booth.Webauth.Authentication(form, data)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
form.addEventListener('ajax:error', (event: CustomEvent) => {
|
|
183
|
+
const [data, status, xhr] = event.detail
|
|
184
|
+
// TODO: Extract error message from JSON that the server sent.
|
|
185
|
+
// On the authentication page there are timeout restrictions, it would be a good idea to reload page.
|
|
186
|
+
const message = `WebAuthn Authentication Ceremony - Initiation Phase Part 1 - ERROR - ${xhr.responseText}`
|
|
187
|
+
console.error(message)
|
|
188
|
+
alert(message)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
if (document.querySelector('.js-booth-polling')) Booth.reloadSoon()
|
|
193
|
+
|
|
194
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
type Base64urlString = string;
|
|
2
|
+
type SchemaLeaf = "copy" | "convert";
|
|
3
|
+
interface SchemaObject {
|
|
4
|
+
[property: string]: {
|
|
5
|
+
required: boolean;
|
|
6
|
+
schema: Schema;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
type SchemaArray = [SchemaObject] | [SchemaLeaf];
|
|
10
|
+
type Schema = SchemaLeaf | SchemaArray | SchemaObject;
|
|
11
|
+
interface CredPropsAuthenticationExtensionsClientOutputsJSON {
|
|
12
|
+
rk: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface PublicKeyCredentialDescriptorJSON {
|
|
15
|
+
type: PublicKeyCredentialType;
|
|
16
|
+
id: Base64urlString;
|
|
17
|
+
transports?: AuthenticatorTransport[];
|
|
18
|
+
}
|
|
19
|
+
interface SimpleWebAuthnExtensionsJSON {
|
|
20
|
+
appid?: string;
|
|
21
|
+
appidExclude?: string;
|
|
22
|
+
credProps?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface SimpleClientExtensionResultsJSON {
|
|
25
|
+
appid?: boolean;
|
|
26
|
+
appidExclude?: boolean;
|
|
27
|
+
credProps?: CredPropsAuthenticationExtensionsClientOutputsJSON;
|
|
28
|
+
}
|
|
29
|
+
interface PublicKeyCredentialUserEntityJSON extends PublicKeyCredentialEntity {
|
|
30
|
+
displayName: string;
|
|
31
|
+
id: Base64urlString;
|
|
32
|
+
}
|
|
33
|
+
declare interface AuthenticatorSelectionCriteriaJSON extends AuthenticatorSelectionCriteria {
|
|
34
|
+
residentKey?: ResidentKeyRequirement;
|
|
35
|
+
}
|
|
36
|
+
interface PublicKeyCredentialCreationOptionsJSON {
|
|
37
|
+
rp: PublicKeyCredentialRpEntity;
|
|
38
|
+
user: PublicKeyCredentialUserEntityJSON;
|
|
39
|
+
challenge: Base64urlString;
|
|
40
|
+
pubKeyCredParams: PublicKeyCredentialParameters[];
|
|
41
|
+
timeout?: number;
|
|
42
|
+
excludeCredentials?: PublicKeyCredentialDescriptorJSON[];
|
|
43
|
+
authenticatorSelection?: AuthenticatorSelectionCriteriaJSON;
|
|
44
|
+
attestation?: AttestationConveyancePreference;
|
|
45
|
+
extensions?: SimpleWebAuthnExtensionsJSON;
|
|
46
|
+
}
|
|
47
|
+
declare interface CredentialCreationOptionsJSON {
|
|
48
|
+
publicKey: PublicKeyCredentialCreationOptionsJSON;
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
}
|
|
51
|
+
interface AuthenticatorAttestationResponseJSON {
|
|
52
|
+
clientDataJSON: Base64urlString;
|
|
53
|
+
attestationObject: Base64urlString;
|
|
54
|
+
}
|
|
55
|
+
declare interface PublicKeyCredentialWithAttestationJSON {
|
|
56
|
+
id: string;
|
|
57
|
+
type: PublicKeyCredentialType;
|
|
58
|
+
rawId: Base64urlString;
|
|
59
|
+
response: AuthenticatorAttestationResponseJSON;
|
|
60
|
+
clientExtensionResults: SimpleClientExtensionResultsJSON;
|
|
61
|
+
}
|
|
62
|
+
interface PublicKeyCredentialRequestOptionsJSON {
|
|
63
|
+
challenge: Base64urlString;
|
|
64
|
+
timeout?: number;
|
|
65
|
+
rpId?: string;
|
|
66
|
+
allowCredentials?: PublicKeyCredentialDescriptorJSON[];
|
|
67
|
+
userVerification?: UserVerificationRequirement;
|
|
68
|
+
extensions?: SimpleWebAuthnExtensionsJSON;
|
|
69
|
+
}
|
|
70
|
+
declare interface CredentialRequestOptionsJSON {
|
|
71
|
+
mediation?: CredentialMediationRequirement;
|
|
72
|
+
publicKey?: PublicKeyCredentialRequestOptionsJSON;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}
|
|
75
|
+
interface AuthenticatorAssertionResponseJSON {
|
|
76
|
+
clientDataJSON: Base64urlString;
|
|
77
|
+
authenticatorData: Base64urlString;
|
|
78
|
+
signature: Base64urlString;
|
|
79
|
+
userHandle: Base64urlString | null;
|
|
80
|
+
}
|
|
81
|
+
declare interface PublicKeyCredentialWithAssertionJSON {
|
|
82
|
+
type: PublicKeyCredentialType;
|
|
83
|
+
id: string;
|
|
84
|
+
rawId: Base64urlString;
|
|
85
|
+
response: AuthenticatorAssertionResponseJSON;
|
|
86
|
+
clientExtensionResults: SimpleClientExtensionResultsJSON;
|
|
87
|
+
}
|
|
88
|
+
declare const schema: {
|
|
89
|
+
[s: string]: Schema;
|
|
90
|
+
};
|
|
91
|
+
// export function create(requestJSON: CredentialCreationOptionsJSON): Promise<PublicKeyCredentialWithAttestationJSON>;
|
|
92
|
+
// export function get(requestJSON: CredentialRequestOptionsJSON): Promise<PublicKeyCredentialWithAssertionJSON>;
|
|
93
|
+
// export function supported(): boolean;
|
|
94
|
+
|
|
95
|
+
declare class webauthnJSON {
|
|
96
|
+
static create(requestJSON: CredentialCreationOptionsJSON): Promise<PublicKeyCredentialWithAttestationJSON>;
|
|
97
|
+
static get(requestJSON: CredentialRequestOptionsJSON): Promise<PublicKeyCredentialWithAssertionJSON>;
|
|
98
|
+
static supported(): boolean;
|
|
99
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
de:
|
|
2
|
+
|
|
3
|
+
activerecord:
|
|
4
|
+
attributes:
|
|
5
|
+
booth/models/onboarding:
|
|
6
|
+
password: Passwort
|
|
7
|
+
password_confirmation: Passwort
|
|
8
|
+
authenticator_nickname: Gerätename
|
|
9
|
+
errors:
|
|
10
|
+
messages:
|
|
11
|
+
not_pwned: ist unsicher, weil es in %{count} Datenlecks veröffentlicht wurde.
|
|
12
|
+
|
|
13
|
+
booth:
|
|
14
|
+
all_other_sessions_revoked: All other devices except this one have been logged out.
|
|
15
|
+
already_responded_to_contest: You have already responded to this contest.
|
|
16
|
+
attempt_was_ignored: Dieser Versuch wurde ignoriert.
|
|
17
|
+
attempts_left: You may try %{attempts_left} more times.
|
|
18
|
+
authenticator_not_found: Could not find that hardware key.
|
|
19
|
+
authenticator_removal_failed: Could not delete that hardware key.
|
|
20
|
+
blank_contest_code: You did not enter a code.
|
|
21
|
+
blank_email: Please provide an email address.
|
|
22
|
+
blank_nickname: Please provide a name for your device.
|
|
23
|
+
blank_otp: Bitte gib den Einmalcode ein, den dir deine Authenticator App anzeigt.
|
|
24
|
+
blank_password: Bitte gib dein Passwort ein.
|
|
25
|
+
blank_secret_key: The provided secret key is empty. Is the URL correct?
|
|
26
|
+
blank_username: Trage bitte deinen Benutzernamen ein.
|
|
27
|
+
contest_response_accepted: You entered the correct code. You have now been logged in on the other device.
|
|
28
|
+
contest_timed_out: You had %{lifespan_minutes} minutes to enter the response code, but it took too long. Please start again.
|
|
29
|
+
email_too_long: The provided email is too long. It must be less at most %{maximum} characters long.
|
|
30
|
+
email_too_short: The provided email is too short. It should be at least %{minimum} characters long.
|
|
31
|
+
incompatible_webauth_device: Sorry, this device cannot be used. Please try another webauth device.
|
|
32
|
+
invalid_contest_code_format: Der Code darf nur aus Zahlen bestehen.
|
|
33
|
+
invalid_email_characters: The provided email address contains invalid characters.
|
|
34
|
+
invalid_email_format: The provided email address does not look valid.
|
|
35
|
+
invalid_otp_format: Der Einmalcode darf nur aus Zahlen bestehen.
|
|
36
|
+
invalid_secret_key_format: The provided secret key contains invalid characters. It should be from the Base58 alphabet.
|
|
37
|
+
invalid_username_format: Der eingegebene Benutzername enthält ungültige Zeichen.
|
|
38
|
+
last_attempt: This is your last attempt and you will have to contact customer service if you fail.
|
|
39
|
+
logged_in_user_cannot_reset_password: You are currently logged in. If you forgot your password, try another browser or logout first.
|
|
40
|
+
login_timeout: Du hattest %{lifespan_minutes} Minuten um den Login abzuschließen, es hat aber länger gedauert. Bitte beginne erneut.
|
|
41
|
+
missing_secret_key: Missing the secret key parameter. Is the URL correct?
|
|
42
|
+
mode_username_and_password_description: Benutzername und Passwort. Kann leicht gestohlen werden.
|
|
43
|
+
mode_username_and_password_title: Passwort (unsicher)
|
|
44
|
+
mode_username_and_webauth_description: Auch bekannt als WebAuthentication (WebAuthn), FIDO2, CTAP2, Apple Passkey (Touch ID, Face ID).
|
|
45
|
+
mode_username_and_webauth_title: Hardwareschlüssel (empfohlen)
|
|
46
|
+
mode_username_password_and_otp_description: Wenn kein kompatibler Hardwareschlüssel zur Hand ist.
|
|
47
|
+
mode_username_password_and_otp_title: Passwort und Einmalpasswort
|
|
48
|
+
mode_username_password_and_webauth_description: Zusätzlich zum Hardwareschlüssel muss jedesmal auch das Passwort eingegeben werden.
|
|
49
|
+
mode_username_password_and_webauth_title: Passwort und Hardwareschlüssel (am sichersten)
|
|
50
|
+
otp_removed: You successfully removed your OTP configuration.
|
|
51
|
+
otp_sudo_timeout: You had %{lifespan_minutes} minutes to perform this action, but it took too long. Please start again by providing your current OTP again.
|
|
52
|
+
otp_unavailable: You can't use OTP at this time.
|
|
53
|
+
password_changed: You successfully changed your password.
|
|
54
|
+
password_removed: You successfully removed your password.
|
|
55
|
+
password_reset_needs_username: To reset your password, please provide a username first.
|
|
56
|
+
password_reset_not_available: This username cannot reset the password. Please contact support.
|
|
57
|
+
password_successfully_reset: Your new password has been saved.
|
|
58
|
+
password_sudo_timeout: You had %{lifespan_minutes} minutes to perform this action, but it took too long. Please start again by providing your current password again.
|
|
59
|
+
password_unavailable: You can't use a password at this time.
|
|
60
|
+
passwordless_cannot_change_password: You cannot change your password because you don't have one.
|
|
61
|
+
permanently_blocked: You failed too many times for this account. Please contact customer service.
|
|
62
|
+
session_revoked: Das Gerät mit der IP %{ip} wurde erfolgreich ausgeloggt.
|
|
63
|
+
short_password: Das kann nicht dein Passwort sein, weil es zu kurz ist. Es muss aus mindestens %{minlength} Zeichen bestehen.
|
|
64
|
+
some_authenticator_removed: Hardware key successfully deleted.
|
|
65
|
+
successfully_logged_out: Du hast dich erfolgreich ausgeloggt.
|
|
66
|
+
try_again_cooldown: Du kannst es in %{distance_of_time_until_cooldown} erneut probieren.
|
|
67
|
+
uninitialized_credential: This account has not been initialized yet. Please contact support.
|
|
68
|
+
unknown_email: We don't know that email.
|
|
69
|
+
unknown_secret_key: The provided secret key is unknown.
|
|
70
|
+
unknown_username: Dieser Benutzername existiert nicht.
|
|
71
|
+
unknown_webauth_device: Sorry, We don't recognize that device. Have you registered it before?
|
|
72
|
+
username_already_exists: This username already exists. Try to login instead?
|
|
73
|
+
username_too_long: The provided username is too long. It must be less at most %{maximum} characters long.
|
|
74
|
+
username_too_short: The provided username is too short. It should be at least %{minimum} characters long.
|
|
75
|
+
webauth_irremovable: You cannot remove any of your hardware keys.
|
|
76
|
+
webauth_missing_authenticators: You cannot login using webauth, because we don't know any of your hardware keys.
|
|
77
|
+
webauth_removed: Hardware keys successfully deleted. Now you log in only with your password.
|
|
78
|
+
webauth_unavailable: You can't use WebAuthn at this time.
|
|
79
|
+
wrong_contest_code_length: The code you entered was not %{digits} digits long.
|
|
80
|
+
wrong_otp_length: Der Einmalcode muss aus %{digits} Zahlen bestehen.
|
|
81
|
+
wrong_otp: Dieser Einmalcode ist falsch, vielleicht ist er schon abgelaufen.
|
|
82
|
+
wrong_password: Das war nicht das richtige Passwort.
|
|
83
|
+
wrong_response_code: This was not the code that is currently shown on the login page on the other device.
|
|
84
|
+
wrong_secret_key_length: The provided secret key does not have the correct length. It should be 30 characters long.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
en:
|
|
2
|
+
|
|
3
|
+
activerecord:
|
|
4
|
+
errors:
|
|
5
|
+
messages:
|
|
6
|
+
not_pwned: is isecure, because it has been published in %{count} data leaks.
|
|
7
|
+
|
|
8
|
+
booth:
|
|
9
|
+
all_other_sessions_revoked: All other devices except this one have been logged out.
|
|
10
|
+
already_responded_to_contest: You have already responded to this contest.
|
|
11
|
+
attempt_was_ignored: This attempt has been ignored.
|
|
12
|
+
attempts_left: You may try %{attempts_left} more times.
|
|
13
|
+
authenticator_not_found: Could not find that hardware key.
|
|
14
|
+
authenticator_removal_failed: Could not delete that hardware key.
|
|
15
|
+
blank_contest_code: You did not enter a code.
|
|
16
|
+
blank_email: Please provide an email address.
|
|
17
|
+
blank_nickname: Please provide a name for your device.
|
|
18
|
+
blank_otp: Please provide the one-time code your authenticator app is showing you.
|
|
19
|
+
blank_password: Please provide a password.
|
|
20
|
+
blank_secret_key: The provided secret key is empty. Is the URL correct?
|
|
21
|
+
blank_username: Please provide a username.
|
|
22
|
+
contest_response_accepted: You entered the correct code. You have now been logged in on the other device.
|
|
23
|
+
contest_timed_out: You had %{lifespan_minutes} minutes to enter the response code, but it took too long. Please start again.
|
|
24
|
+
email_too_long: The provided email is too long. It must be less at most %{maximum} characters long.
|
|
25
|
+
email_too_short: The provided email is too short. It should be at least %{minimum} characters long.
|
|
26
|
+
incompatible_webauth_device: Sorry, this device cannot be used. Please try another webauth device.
|
|
27
|
+
invalid_contest_code_format: The code may only consist of digits.
|
|
28
|
+
invalid_email_characters: The provided email address contains invalid characters.
|
|
29
|
+
invalid_email_format: The provided email address does not look valid.
|
|
30
|
+
invalid_otp_format: The one-time may only consist of digits.
|
|
31
|
+
invalid_secret_key_format: The provided secret key contains invalid characters. It should be from the Base58 alphabet.
|
|
32
|
+
invalid_username_format: The provided username contains invalid characters.
|
|
33
|
+
last_attempt: This is your last attempt and you will have to contact customer service if you fail.
|
|
34
|
+
logged_in_user_cannot_reset_password: You are currently logged in. If you forgot your password, try another browser or logout first.
|
|
35
|
+
login_timeout: You had %{lifespan_minutes} minutes to complete the login, but it took too long. Please start again.
|
|
36
|
+
missing_secret_key: Missing the secret key parameter. Is the URL correct?
|
|
37
|
+
mode_username_and_password_description: Username and password. Can easily be stolen.
|
|
38
|
+
mode_username_and_password_title: Password (insecure)
|
|
39
|
+
mode_username_and_webauth_description: Also known as WebAuthentication (WebAuthn), FIDO2, CTAP2, Apple Passkey (Touch ID, Face ID).
|
|
40
|
+
mode_username_and_webauth_title: Hardware key (recommended)
|
|
41
|
+
mode_username_password_and_otp_description: Use this if you don't have a hardware key.
|
|
42
|
+
mode_username_password_and_otp_title: Password and one-time-password.
|
|
43
|
+
mode_username_password_and_webauth_description: Additionally to your hardware key, you will have to enter your password. So nobody with your hardware key can just log in.
|
|
44
|
+
mode_username_password_and_webauth_title: Password and hardware key (most secure)
|
|
45
|
+
otp_removed: You successfully removed your OTP configuration.
|
|
46
|
+
otp_sudo_timeout: You had %{lifespan_minutes} minutes to perform this action, but it took too long. Please start again by providing your current OTP again.
|
|
47
|
+
otp_unavailable: You can't use OTP at this time.
|
|
48
|
+
password_changed: You successfully changed your password.
|
|
49
|
+
password_removed: You successfully removed your password.
|
|
50
|
+
password_reset_needs_username: To reset your password, please provide a username first.
|
|
51
|
+
password_reset_not_available: This username cannot reset the password. Please contact support.
|
|
52
|
+
password_successfully_reset: Your new password has been saved.
|
|
53
|
+
password_sudo_timeout: You had %{lifespan_minutes} minutes to perform this action, but it took too long. Please start again by providing your current password again.
|
|
54
|
+
password_unavailable: You can't use a password at this time.
|
|
55
|
+
passwordless_cannot_change_password: You cannot change your password because you don't have one.
|
|
56
|
+
permanently_blocked: You failed too many times for this account. Please contact customer service.
|
|
57
|
+
session_revoked: The logged in device with the IP %{ip} has successfully been logged out.
|
|
58
|
+
short_password: That cannot be the password, because it was too short. It must be at least %{minlength} characters long.
|
|
59
|
+
some_authenticator_removed: Hardware key successfully deleted.
|
|
60
|
+
successfully_logged_out: Logout successful.
|
|
61
|
+
try_again_cooldown: You may try again in %{distance_of_time_until_cooldown}.
|
|
62
|
+
uninitialized_credential: This account has not been initialized yet. Please contact support.
|
|
63
|
+
unknown_email: We don't know that email.
|
|
64
|
+
unknown_secret_key: The provided secret key is unknown.
|
|
65
|
+
unknown_username: We don't know that username.
|
|
66
|
+
unknown_webauth_device: Sorry, We don't recognize that device. Have you registered it before?
|
|
67
|
+
username_already_exists: This username already exists. Try to login instead?
|
|
68
|
+
username_too_long: The provided username is too long. It must be less at most %{maximum} characters long.
|
|
69
|
+
username_too_short: The provided username is too short. It should be at least %{minimum} characters long.
|
|
70
|
+
webauth_irremovable: You cannot remove any of your hardware keys.
|
|
71
|
+
webauth_missing_authenticators: You cannot login using webauth, because we don't know any of your hardware keys.
|
|
72
|
+
webauth_removed: Hardware keys successfully deleted. Now you log in only with your password.
|
|
73
|
+
webauth_unavailable: You can't use WebAuthn at this time.
|
|
74
|
+
wrong_contest_code_length: The code you entered was not %{digits} digits long.
|
|
75
|
+
wrong_otp_length: The one-time code must be %{digits} digits long.
|
|
76
|
+
wrong_otp: The one-time code is wrong, maybe it already expired.
|
|
77
|
+
wrong_password: Wrong password.
|
|
78
|
+
wrong_response_code: This was not the code that is currently shown on the login page on the other device.
|
|
79
|
+
wrong_secret_key_length: The provided secret key does not have the correct length. It should be 30 characters long.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Booth
|
|
2
|
+
module Adminland
|
|
3
|
+
module Credentials
|
|
4
|
+
class Create
|
|
5
|
+
include ::Booth::MethodObject
|
|
6
|
+
include ::Booth::Logging
|
|
7
|
+
|
|
8
|
+
option :username
|
|
9
|
+
option :allowed_modes
|
|
10
|
+
option :scope, default: -> { :default }
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
creation = ::Booth::Credentials::Create.call(
|
|
14
|
+
username:,
|
|
15
|
+
allowed_modes:,
|
|
16
|
+
scope:
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
return creation if creation.failure?
|
|
20
|
+
|
|
21
|
+
# Not exposing the ActiveRecord model outside of Booth.
|
|
22
|
+
Tron.success :credential_created,
|
|
23
|
+
id: creation.credential.id,
|
|
24
|
+
scope: creation.credential.scope,
|
|
25
|
+
allowed_modes: creation.credential.allowed_modes.map(&:to_sym)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|