// CCN — passkeys: signing in or joining, and adding one from the account screen. Without it, or without WebAuthn, the demo sign-in remains. const form = document.querySelector("[data-passkey-form]"); const supported = window.PublicKeyCredential && PublicKeyCredential.parseCreationOptionsFromJSON && PublicKeyCredential.parseRequestOptionsFromJSON; if (form && supported) { const button = form.querySelector("[data-passkey]"); const status = form.querySelector("[data-passkey-status]"); const post = async (path, body) => { const response = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const answer = await response.json(); if (!response.ok) { throw new Error(answer.error ?? "Something went wrong. Try again."); } return answer; }; button.hidden = false; button.addEventListener("click", async () => { button.disabled = true; status.textContent = ""; try { // On the account screen the button adds a passkey to the account signed in; on the sign-in screen it signs in or joins. const adding = button.dataset.passkey === "add"; const start = adding ? { add: true } : { email: form.querySelector("[name=email]").value }; const { mode, options } = await post("/sign-in/passkey/options", start); let credential; if (mode === "create") { // A discoverable passkey, so the next sign-in needs no email. options.authenticatorSelection = { residentKey: "required", userVerification: "preferred" }; credential = await navigator.credentials.create({ publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(options), }); } else { credential = await navigator.credentials.get({ publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options), }); } const { location } = await post("/sign-in/passkey/finish", { credential: credential.toJSON(), return: form.querySelector("[name=return]")?.value, }); window.location.assign(location); } catch (error) { // Dismissing the browser's prompt is not an error worth a message. status.textContent = error.name === "NotAllowedError" ? "" : error.message; button.disabled = false; } }); }