Auto-suggest name for new Passkeys

- Use plain client data json everywhere
- Expose AAGUID and backed_up on Credentials
- Make attestation verifiers configurable
- Auto-suggest names for passkeys and show icons
This commit is contained in:
Stanko K.R.
2026-02-19 10:56:22 +01:00
parent b23660d897
commit fbe9252db1
53 changed files with 666 additions and 172 deletions
@@ -1,4 +1,5 @@
import { Controller } from "@hotwired/stimulus"
import { base64urlToBuffer, bufferToBase64url } from "helpers/base64url_helpers"
export default class extends Controller {
static values = { publicKey: Object, url: String, csrfToken: String }
@@ -41,10 +42,10 @@ export default class extends Controller {
const fields = {
authenticity_token: this.csrfTokenValue,
credential_id: credential.id,
"response[client_data_json]": new TextDecoder().decode(credential.response.clientDataJSON),
"response[authenticator_data]": this.#bufferToBase64url(credential.response.authenticatorData),
"response[signature]": this.#bufferToBase64url(credential.response.signature)
"passkey[id]": credential.id,
"passkey[client_data_json]": new TextDecoder().decode(credential.response.clientDataJSON),
"passkey[authenticator_data]": bufferToBase64url(credential.response.authenticatorData),
"passkey[signature]": bufferToBase64url(credential.response.signature)
}
for (const [name, value] of Object.entries(fields)) {
@@ -62,13 +63,13 @@ export default class extends Controller {
#prepareOptions(options) {
const prepared = {
...options,
challenge: this.#base64urlToBuffer(options.challenge)
challenge: base64urlToBuffer(options.challenge)
}
if (options.allowCredentials?.length) {
prepared.allowCredentials = options.allowCredentials.map(cred => ({
...cred,
id: this.#base64urlToBuffer(cred.id)
id: base64urlToBuffer(cred.id)
}))
} else {
delete prepared.allowCredentials
@@ -76,17 +77,4 @@ export default class extends Controller {
return prepared
}
#base64urlToBuffer(base64url) {
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/")
const padding = "=".repeat((4 - base64.length % 4) % 4)
const binary = atob(base64 + padding)
return Uint8Array.from(binary, c => c.charCodeAt(0)).buffer
}
#bufferToBase64url(buffer) {
const bytes = new Uint8Array(buffer)
const binary = String.fromCharCode(...bytes)
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
}
}