booth 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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":"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"}
|
@@ -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
|