diff --git a/app/javascript/lib/action_pack/passkey.js b/app/javascript/lib/action_pack/passkey.js index 7ee20cb84a..103cbc72ec 100644 --- a/app/javascript/lib/action_pack/passkey.js +++ b/app/javascript/lib/action_pack/passkey.js @@ -77,9 +77,9 @@ class PasskeyButton extends HTMLElement { #handleError(error) { console.error("Passkey ceremony failed", error) - const cancelled = error.name === "AbortError" || error.name === "NotAllowedError" - this.#showError(cancelled ? "cancelled" : "error") - this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, cancelled } })) + const type = errorType(error) + this.#showError(type) + this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, type } })) } #showError(type) { @@ -139,8 +139,8 @@ class PasskeySignInButton extends PasskeyButton { this.form.submit() } catch (error) { console.error("Passkey conditional mediation failed", error) - const cancelled = error.name === "AbortError" || error.name === "NotAllowedError" - this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, cancelled } })) + const type = errorType(error) + this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, type } })) } } } @@ -157,6 +157,15 @@ customElements.define("rails-passkey-sign-in-button", PasskeySignInButton) // -- Shared helpers ---------------------------------------------------------- +function errorType(error) { + switch (error.name) { + case "AbortError": + case "NotAllowedError": return "cancelled" + case "InvalidStateError": return "duplicate" + default: return "error" + } +} + function passkeysAvailable() { return !!window.PublicKeyCredential } diff --git a/app/views/my/passkeys/index.html.erb b/app/views/my/passkeys/index.html.erb index de143eeb41..fea08c27bf 100644 --- a/app/views/my/passkeys/index.html.erb +++ b/app/views/my/passkeys/index.html.erb @@ -18,7 +18,7 @@ <% end %>