Merge pull request #2756 from basecamp/friendly-error-message-on-duplicate-passkey
Show separate error message when adding a Passkey twice
This commit is contained in:
@@ -77,9 +77,9 @@ class PasskeyButton extends HTMLElement {
|
|||||||
|
|
||||||
#handleError(error) {
|
#handleError(error) {
|
||||||
console.error("Passkey ceremony failed", error)
|
console.error("Passkey ceremony failed", error)
|
||||||
const cancelled = error.name === "AbortError" || error.name === "NotAllowedError"
|
const type = errorType(error)
|
||||||
this.#showError(cancelled ? "cancelled" : "error")
|
this.#showError(type)
|
||||||
this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, cancelled } }))
|
this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, type } }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#showError(type) {
|
#showError(type) {
|
||||||
@@ -139,8 +139,8 @@ class PasskeySignInButton extends PasskeyButton {
|
|||||||
this.form.submit()
|
this.form.submit()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Passkey conditional mediation failed", error)
|
console.error("Passkey conditional mediation failed", error)
|
||||||
const cancelled = error.name === "AbortError" || error.name === "NotAllowedError"
|
const type = errorType(error)
|
||||||
this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, cancelled } }))
|
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 ----------------------------------------------------------
|
// -- Shared helpers ----------------------------------------------------------
|
||||||
|
|
||||||
|
function errorType(error) {
|
||||||
|
switch (error.name) {
|
||||||
|
case "AbortError":
|
||||||
|
case "NotAllowedError": return "cancelled"
|
||||||
|
case "InvalidStateError": return "duplicate"
|
||||||
|
default: return "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function passkeysAvailable() {
|
function passkeysAvailable() {
|
||||||
return !!window.PublicKeyCredential
|
return !!window.PublicKeyCredential
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<%= passkey_registration_button my_passkeys_path, options: @registration_options, error: { class: "txt-negative margin-block-half" }, cancellation: { class: "txt-subtle margin-block-half" }, class: "btn btn--link center txt-medium" do %>
|
<%= passkey_registration_button my_passkeys_path, options: @registration_options, error: { class: "txt-negative margin-block-half" }, cancellation: { class: "txt-subtle margin-block-half" }, duplicate: { class: "txt-negative margin-block-half" }, class: "btn btn--link center txt-medium" do %>
|
||||||
<%= icon_tag "add" %>
|
<%= icon_tag "add" %>
|
||||||
<span>Register a passkey</span>
|
<span>Register a passkey</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
module ActionPack::Passkey::FormHelper
|
module ActionPack::Passkey::FormHelper
|
||||||
REGISTRATION_ERROR_MESSAGE = "Something went wrong while registering your passkey."
|
REGISTRATION_ERROR_MESSAGE = "Something went wrong while registering your passkey."
|
||||||
REGISTRATION_CANCELLED_MESSAGE = "Passkey registration was cancelled. Try again when you are ready."
|
REGISTRATION_CANCELLED_MESSAGE = "Passkey registration was cancelled. Try again when you are ready."
|
||||||
|
REGISTRATION_DUPLICATE_MESSAGE = "You already have a passkey registered on this device. Remove the existing one first to re-register."
|
||||||
SIGN_IN_ERROR_MESSAGE = "Something went wrong while signing in with your passkey."
|
SIGN_IN_ERROR_MESSAGE = "Something went wrong while signing in with your passkey."
|
||||||
SIGN_IN_CANCELLED_MESSAGE = "Passkey sign in was cancelled. Try again when you are ready."
|
SIGN_IN_CANCELLED_MESSAGE = "Passkey sign in was cancelled. Try again when you are ready."
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ module ActionPack::Passkey::FormHelper
|
|||||||
component_options, form_options, button_options, error_options = partition_passkey_options(url, options)
|
component_options, form_options, button_options, error_options = partition_passkey_options(url, options)
|
||||||
error_options[:error][:message] ||= REGISTRATION_ERROR_MESSAGE
|
error_options[:error][:message] ||= REGISTRATION_ERROR_MESSAGE
|
||||||
error_options[:cancellation][:message] ||= REGISTRATION_CANCELLED_MESSAGE
|
error_options[:cancellation][:message] ||= REGISTRATION_CANCELLED_MESSAGE
|
||||||
|
error_options[:duplicate][:message] ||= REGISTRATION_DUPLICATE_MESSAGE
|
||||||
param = form_options.delete(:param)
|
param = form_options.delete(:param)
|
||||||
|
|
||||||
content_tag("rails-passkey-registration-button", **component_options.transform_keys { |key| key.to_s.dasherize }) do
|
content_tag("rails-passkey-registration-button", **component_options.transform_keys { |key| key.to_s.dasherize }) do
|
||||||
@@ -91,7 +93,7 @@ module ActionPack::Passkey::FormHelper
|
|||||||
form_options = options
|
form_options = options
|
||||||
.fetch(:form, {})
|
.fetch(:form, {})
|
||||||
.reverse_merge(method: :post, action: url, class: "button_to", param: :passkey)
|
.reverse_merge(method: :post, action: url, class: "button_to", param: :passkey)
|
||||||
error_options = options.slice(:error, :cancellation).reverse_merge(error: {}, cancellation: {})
|
error_options = options.slice(:error, :cancellation, :duplicate).reverse_merge(error: {}, cancellation: {}, duplicate: {})
|
||||||
|
|
||||||
button_options = options.except(:options, :form, *component_options.keys, *error_options.keys)
|
button_options = options.except(:options, :form, *component_options.keys, *error_options.keys)
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ module ActionPack::Passkey::FormHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def passkey_error_messages(error: {}, cancellation: {})
|
def passkey_error_messages(error: {}, cancellation: {}, duplicate: {})
|
||||||
error_message = error[:message]
|
error_message = error[:message]
|
||||||
error_attributes = error.except(:message)
|
error_attributes = error.except(:message)
|
||||||
error_attributes[:data] ||= {}
|
error_attributes[:data] ||= {}
|
||||||
@@ -117,6 +119,15 @@ module ActionPack::Passkey::FormHelper
|
|||||||
cancellation_attributes[:data] ||= {}
|
cancellation_attributes[:data] ||= {}
|
||||||
cancellation_attributes[:data][:passkey_error] = "cancelled"
|
cancellation_attributes[:data][:passkey_error] = "cancelled"
|
||||||
|
|
||||||
tag.div(error_message, hidden: true, **error_attributes) + tag.div(cancellation_message, hidden: true, **cancellation_attributes)
|
messages = tag.div(error_message, hidden: true, **error_attributes) +
|
||||||
|
tag.div(cancellation_message, hidden: true, **cancellation_attributes)
|
||||||
|
|
||||||
|
if duplicate[:message]
|
||||||
|
duplicate_attributes = duplicate.except(:message)
|
||||||
|
duplicate_attributes[:data] = { passkey_error: "duplicate" }
|
||||||
|
messages += tag.div(duplicate[:message], hidden: true, **duplicate_attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
messages
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user