Add confirm to prime user for PRF output retrieval

This commit is contained in:
Karl Ludwig Weise
2026-01-19 20:51:18 +01:00
parent 9e4094702d
commit e570ecef51
4 changed files with 23 additions and 10 deletions
+5 -1
View File
@@ -109,9 +109,13 @@
"action": "Passkey setzen",
"success": "Passkey eingerichtet. Logge Dich damit jetzt ein.",
"successKeyPairSaved": "Schlüsselpaar erfolgreich gespeichert.",
"confirmPrfRetrival": "Jetzt verwende den neu erstellten Passkey, um damit die Verschlüsselung zu aktivieren.",
"error": "Passkey konnte nicht gesetzt werden",
"errorKeyPairDataMissing": "Konnte Schlüsselpaar nicht generieren. Fehlende Daten.",
"errorKeyPairNotSaved": "Schlüsselpaar konnte nicht gespeichert werden."
"errorKeyPairNotSaved": "Schlüsselpaar konnte nicht gespeichert werden.",
"errorPrfOutputNotTriggered": "Sie müssen der Verwendung des Passkeys zustimmen.",
"errorAuthenticatorNotSupported": "Dieser Authentifikator unterstützt nicht die erforderliche Sicherheitserweiterung (PRF). Bitte verwenden Sie einen modernen Authentifikator wie YubiKey 5.2.3+, Apple Touch ID oder Android.",
"errorGettingPrfOutput": "Fehler beim Abrufen der Sicherheitsdaten vom Passkey. Bitte verwenden Sie einen modernen Authentifikator, der die PRF-Erweiterung unterstützt."
},
"logout": {
"title": "Erfolgreich abgemeldet",
+5 -1
View File
@@ -42,9 +42,13 @@
"action": "Set Passkey",
"success": "Passkey set. Login with it now.",
"successKeyPairSaved": "Key pair saved successfully.",
"confirmPrfRetrival": "Now use your newly created passkey to enable encryption with it.",
"error": "Passkey could not be set",
"errorKeyPairDataMissing": "Could not generate key pair. Missing data.",
"errorKeyPairNotSaved": "Key pair could not be saved."
"errorKeyPairNotSaved": "Key pair could not be saved.",
"errorPrfOutputNotTriggered": "You have to confirm using the passkey.",
"errorAuthenticatorNotSupported": "This authenticator does not support the required security extension (PRF). Please use a modern authenticator like YubiKey 5.2.3+, Apple Touch ID or Android.",
"errorGettingPrfOutput": "Failed to retrieve security data from passkey. Please use a modern authenticator that supports PRF extension."
},
"slogan": "End-to-End encrypted appointment booking platform.",
"logo": "OpenReception Logo: A 3D pixel art with calendar that's also a lock",
@@ -35,7 +35,7 @@
let passkeyId: string | undefined = $state();
let prfOutput: ArrayBuffer | undefined = $state();
let kyberKeyPair: { publicKey: Uint8Array; privateKey: Uint8Array } | undefined = $state();
let registrationChallenge: string | undefined = $state(); // Store first challenge for form submission
let registrationChallenge: string | undefined = $state();
const form = superForm(data.form, {
validators: zodClient(formSchema),
@@ -111,9 +111,7 @@
email: $formData.email,
extensions: extensionResults,
});
toast.error(
"This authenticator does not support the required security extension (PRF). Please use a modern authenticator like YubiKey 5.2.3+, Titan Gen2, Windows Hello, Touch ID, or Android.",
);
toast.warning(m["setupPasskey.errorAuthenticatorNotSupported"]());
return;
}
@@ -121,6 +119,15 @@
const attestationObjectResp = passkeyResp.response.attestationObject;
const clientDataJSONResp = passkeyResp.response.clientDataJSON;
// UX Primer for second passkey UI
const isConfirmed = confirm(m["setupPasskey.confirmPrfRetrival"]());
if (!isConfirmed) {
$passkeyLoading = "error";
toast.error(m["setupPasskey.errorPrfOutputNotTriggered"]());
logger.warn("PRF challenge primer not confirmed");
return;
}
// CRITICAL: Get PRF output immediately after passkey creation
// This is the only time we can retrieve the PRF output
// Uses email as salt for multi-passkey support
@@ -149,9 +156,7 @@
passkeyId: passkeyResp.id,
error,
});
toast.error(
"Failed to retrieve security data from passkey. Please use a modern authenticator that supports PRF extension.",
);
toast.error(m["setupPasskey.errorGettingPrfOutput"]());
return;
}
+1 -1
View File
@@ -11,7 +11,7 @@ const log = logger.setContext(import.meta.filename);
export const load: PageServerLoad = async () => {
return {
form: await superValidate(zod(formSchema)),
form: await superValidate({ type: "passkey" }, zod(formSchema)),
};
};