Add a button to sign in with a passkey
This commit is contained in:
@@ -53,6 +53,7 @@ class PasskeyButton extends HTMLElement {
|
|||||||
|
|
||||||
// Arrow function to preserve `this` binding for addEventListener/removeEventListener.
|
// Arrow function to preserve `this` binding for addEventListener/removeEventListener.
|
||||||
#perform = async () => {
|
#perform = async () => {
|
||||||
|
await this.abortConditionalMediation?.()
|
||||||
this.button.disabled = true
|
this.button.disabled = true
|
||||||
this.#hideErrors()
|
this.#hideErrors()
|
||||||
this.button.dispatchEvent(new CustomEvent("passkey:start", { bubbles: true }))
|
this.button.dispatchEvent(new CustomEvent("passkey:start", { bubbles: true }))
|
||||||
@@ -105,6 +106,9 @@ class PasskeyRegistrationButton extends PasskeyButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PasskeySignInButton extends PasskeyButton {
|
class PasskeySignInButton extends PasskeyButton {
|
||||||
|
#conditionalMediationController = null
|
||||||
|
#conditionalMediationPromise = null
|
||||||
|
|
||||||
get purpose() { return "authentication" }
|
get purpose() { return "authentication" }
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@@ -116,32 +120,49 @@ class PasskeySignInButton extends PasskeyButton {
|
|||||||
return this.getAttribute("mediation")
|
return this.getAttribute("mediation")
|
||||||
}
|
}
|
||||||
|
|
||||||
async perform(options, { mediation } = {}) {
|
async perform(options, { signal, mediation } = {}) {
|
||||||
return await authenticate(options, { mediation })
|
return await authenticate(options, { signal, mediation })
|
||||||
}
|
}
|
||||||
|
|
||||||
fillForm(passkey) {
|
fillForm(passkey) {
|
||||||
fillSignInForm(this.form, passkey)
|
fillSignInForm(this.form, passkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async abortConditionalMediation() {
|
||||||
|
if (this.#conditionalMediationController) {
|
||||||
|
this.#conditionalMediationController.abort()
|
||||||
|
await this.#conditionalMediationPromise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async #attemptConditionalMediation() {
|
async #attemptConditionalMediation() {
|
||||||
if (await this.#conditionalMediationAvailable()) {
|
if (await this.#conditionalMediationAvailable()) {
|
||||||
const options = this.options
|
const options = this.options
|
||||||
|
|
||||||
this.form.dispatchEvent(new CustomEvent("passkey:start", { bubbles: true }))
|
this.form.dispatchEvent(new CustomEvent("passkey:start", { bubbles: true }))
|
||||||
|
|
||||||
try {
|
this.#conditionalMediationController = new AbortController()
|
||||||
await refreshChallenge(options, this.challengeUrl, this.purpose)
|
this.#conditionalMediationPromise = this.#runConditionalMediation(options)
|
||||||
const passkey = await this.perform(options, { mediation: this.mediation })
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.form.dispatchEvent(new CustomEvent("passkey:success", { bubbles: true }))
|
async #runConditionalMediation(options) {
|
||||||
this.fillForm(passkey)
|
try {
|
||||||
this.form.submit()
|
await refreshChallenge(options, this.challengeUrl, this.purpose)
|
||||||
} catch (error) {
|
const passkey = await this.perform(options, { signal: this.#conditionalMediationController.signal, mediation: this.mediation })
|
||||||
console.error("Passkey conditional mediation failed", error)
|
|
||||||
const type = errorType(error)
|
this.form.dispatchEvent(new CustomEvent("passkey:success", { bubbles: true }))
|
||||||
this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, type } }))
|
this.fillForm(passkey)
|
||||||
}
|
this.form.submit()
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === "AbortError") return
|
||||||
|
|
||||||
|
console.error("Passkey conditional mediation failed", error)
|
||||||
|
const type = errorType(error)
|
||||||
|
this.button.dispatchEvent(new CustomEvent("passkey:error", { bubbles: true, detail: { error, type } }))
|
||||||
|
} finally {
|
||||||
|
this.#conditionalMediationController = null
|
||||||
|
this.#conditionalMediationPromise = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= passkey_sign_in_button "Sign in with a passkey", session_passkey_path, options: @authentication_options, mediation: "conditional", class: "btn btn--link center txt-medium", hidden: true %>
|
<p>
|
||||||
|
<%= passkey_sign_in_button "Sign in with a passkey", session_passkey_path, options: @authentication_options, mediation: "conditional", class: "btn btn--plain txt-link center txt-small" %>
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user