@txnlab/use-wallet-passkey 5.2.0-next.4 → 5.2.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -18
- package/dist/index.d.ts +84 -5
- package/dist/index.js +135 -28
- package/dist/index.js.map +1 -1
- package/dist/{source.generated-CISa6YOa.js → source.generated-BdYdygOX.js} +2 -2
- package/dist/{source.generated-CISa6YOa.js.map → source.generated-BdYdygOX.js.map} +1 -1
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["bytesEqual"],"sources":["../src/derivation.ts","../src/errors.ts","../src/icon.ts","../src/key-store.ts","../src/record.ts","../src/webauthn.ts","../src/adapter.ts","../src/index.ts"],"sourcesContent":["/**\n * The derivation from a passkey's PRF output to a Falcon-1024 account.\n *\n * ```\n * entropy = PRF(eval.first = PRF_LABEL_PREFIX + context) // 32 bytes\n * keygenSeed = SHA-512/256(\"PQK\" || \"f1\" || entropy)\n * {pk, sk} = falcon-1024 generateKey(keygenSeed) // falcon_det1024\n * address = algosdk.addressFromPQKey(FALCON_1024_SCHEME, pk)\n * phrase = algosdk.mnemonicFromSeed(entropy) // 25 words\n * ```\n *\n * The keygen seed matches go-algorand's `algokey pq` (`derivePQKeySeed`),\n * algosdk's `pq25WordMnemonicToSeed`, Lute's Falcon25 accounts, Pera and\n * ARC-76, so the recovery phrase restores the same account in any of them.\n *\n * NEVER CHANGE `PRF_LABEL_PREFIX`: the label is the PRF input, so changing it\n * changes every account derived from it.\n */\nimport { sha512_256 } from '@noble/hashes/sha2'\n\n/** The PRF input prefix. Frozen: changing it moves every account. */\nexport const PRF_LABEL_PREFIX = '@txnlab/use-wallet-passkey:falcon-1024:v1:'\n\n/**\n * A `context` may not contain `:`, so a future account slot can be encoded as\n * a `:<n>` suffix without colliding with any slot-0 label.\n */\nconst CONTEXT_PATTERN = /^[^:]{0,128}$/\n\nexport function assertValidContext(context: string): void {\n if (!CONTEXT_PATTERN.test(context)) {\n throw new Error('passkey: `context` must be at most 128 characters and must not contain \":\"')\n }\n}\n\n/** The PRF input (`eval.first`) for an account context */\nexport function prfInput(context: string): Uint8Array {\n assertValidContext(context)\n return new TextEncoder().encode(PRF_LABEL_PREFIX + context)\n}\n\n// \"PQK\" || \"f1\"\nconst PQK_PREFIX = new Uint8Array([0x50, 0x51, 0x4b, 0x66, 0x31])\n\n/** keygenSeed = SHA-512/256(\"PQK\" || \"f1\" || entropy) */\nexport function keygenSeedFromEntropy(entropy: Uint8Array): Uint8Array {\n if (entropy.length !== 32) {\n throw new Error('entropy must be 32 bytes')\n }\n const input = new Uint8Array(PQK_PREFIX.length + entropy.length)\n input.set(PQK_PREFIX)\n input.set(entropy, PQK_PREFIX.length)\n try {\n return sha512_256(input)\n } finally {\n input.fill(0)\n }\n}\n","export type PasskeyErrorCode =\n /** The user cancelled or the request timed out (or, on iOS, an in-app browser blocked it) */\n | 'cancelled'\n /** This browser can't run passkey ceremonies (no WebAuthn, or an Android WebView) */\n | 'unsupported'\n /** The passkey's provider doesn't support the PRF extension */\n | 'prf-unsupported'\n /** The relying party ID isn't valid for this page's origin */\n | 'invalid-rp-id'\n /** The passkey derived a different key than the connected account's */\n | 'key-mismatch'\n /** No account is connected */\n | 'not-connected'\n /** The key store failed, e.g. the session worker was blocked by a CSP */\n | 'key-store-failed'\n | 'failed'\n\nexport class PasskeyError extends Error {\n readonly code: PasskeyErrorCode\n\n constructor(code: PasskeyErrorCode, message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = 'PasskeyError'\n this.code = code\n }\n}\n\n/**\n * Classifies a WebAuthn failure after the fact. Never gate on user-agent\n * sniffing up front: these errors are the only reliable signal.\n */\nexport function toPasskeyError(error: unknown): PasskeyError {\n if (error instanceof PasskeyError) return error\n\n const name = error instanceof Error ? error.name : undefined\n const message = error instanceof Error ? error.message : String(error)\n\n switch (name) {\n case 'NotAllowedError':\n case 'AbortError':\n return new PasskeyError(\n 'cancelled',\n 'The passkey request was cancelled or timed out. In an in-app browser, open the page in Safari or Chrome.',\n { cause: error }\n )\n case 'NotSupportedError':\n // Android WebView: \"...connecting to Web Authentication service\"\n return new PasskeyError(\n 'unsupported',\n 'This browser does not support passkeys here. Open the page in a full browser.',\n { cause: error }\n )\n case 'SecurityError':\n return new PasskeyError(\n 'invalid-rp-id',\n `The passkey relying party ID is not valid for this origin: ${message}`,\n { cause: error }\n )\n default:\n return new PasskeyError('failed', message, { cause: error })\n }\n}\n","export const icon = `\n<svg viewBox=\"0 0 40 40\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect fill=\"#1F2A44\" width=\"40\" height=\"40\" rx=\"8\"/>\n <circle cx=\"16\" cy=\"13.5\" r=\"5\" fill=\"none\" stroke=\"#FFFFFF\" stroke-width=\"2.5\"/>\n <path d=\"M7.5 30c0-4.7 3.8-8.5 8.5-8.5 1.6 0 3.1.4 4.3 1.2\" fill=\"none\" stroke=\"#FFFFFF\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <circle cx=\"27\" cy=\"23\" r=\"3.6\" fill=\"none\" stroke=\"#7DD3FC\" stroke-width=\"2.4\"/>\n <path d=\"M27 26.6V33m0-3h2.6M27 32.4h2\" fill=\"none\" stroke=\"#7DD3FC\" stroke-width=\"2.4\" stroke-linecap=\"round\"/>\n</svg>\n`\n","import { PasskeyError } from './errors'\nimport type { WorkerRequest, WorkerResponse } from './worker/protocol'\n\n/**\n * Holds the Falcon-1024 private key between a passkey's PRF ceremony and the\n * signatures it authorizes. The adapter's built-in key store runs the key in a\n * dedicated worker; supply your own (the `keyStore` option) to hold it\n * elsewhere, e.g. a SharedWorker that outlives a page reload.\n */\nexport interface PasskeyKeyStore {\n /** True when `sign` can run without a new PRF ceremony */\n readonly unlocked: boolean\n /**\n * Derive the key from the passkey's 32-byte PRF output and return its\n * Falcon-1024 public key. Implementations must zero `entropy` and derive the\n * keygen seed with `keygenSeedFromEntropy`.\n */\n unlock(entropy: Uint8Array): Promise<Uint8Array>\n /** Sign exactly these bytes with the private key */\n sign(bytes: Uint8Array): Promise<Uint8Array>\n /** Drop the private key */\n lock(): void\n}\n\nexport interface WorkerKeyStoreOptions {\n /** Clear the key after this long without a signature. 0 keeps it until `lock`. */\n idleTimeoutMs?: number\n /** Called when the idle timeout clears the key */\n onIdleLock?: () => void\n /** How long to wait for the worker to load. Default: 10000 */\n startTimeoutMs?: number\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n/**\n * The built-in key store: a dedicated module worker started from a Blob URL.\n * The worker module is inlined into this package, so a consumer's bundler has\n * no worker entry to resolve; the page's CSP must allow `worker-src blob:` and\n * `'wasm-unsafe-eval'`. `lock` terminates the worker, which drops its WASM\n * heap.\n */\nexport class WorkerKeyStore implements PasskeyKeyStore {\n private worker: Worker | null = null\n private isUnlocked = false\n private nextId = 1\n private pending = new Map<\n number,\n { resolve: (value: any) => void; reject: (error: Error) => void }\n >()\n\n constructor(private readonly options: WorkerKeyStoreOptions = {}) {}\n\n get unlocked(): boolean {\n return this.isUnlocked\n }\n\n async unlock(entropy: Uint8Array): Promise<Uint8Array> {\n // Copy into a buffer the worker takes ownership of, then zero the caller's\n const buffer = new Uint8Array(entropy).buffer\n entropy.fill(0)\n\n this.lock()\n try {\n this.worker = await this.start()\n const publicKey = await this.call<Uint8Array>(\n { op: 'unlock', entropy: buffer, idleTimeoutMs: this.options.idleTimeoutMs },\n [buffer]\n )\n this.isUnlocked = true\n return publicKey\n } catch (error) {\n new Uint8Array(buffer).fill(0)\n this.lock()\n throw error\n }\n }\n\n sign(bytes: Uint8Array): Promise<Uint8Array> {\n if (!this.isUnlocked) {\n return Promise.reject(\n new PasskeyError('key-store-failed', 'The passkey key store is locked.')\n )\n }\n return this.call<Uint8Array>({ op: 'sign', bytes })\n }\n\n lock(): void {\n this.isUnlocked = false\n if (!this.worker) return\n this.worker.terminate()\n this.worker = null\n const error = new PasskeyError('key-store-failed', 'The passkey key store was locked.')\n for (const { reject } of this.pending.values()) reject(error)\n this.pending.clear()\n }\n\n private async start(): Promise<Worker> {\n // Loaded lazily, after the passkey ceremony, so it never sits between a\n // user's tap and navigator.credentials\n const { WORKER_SOURCE } = await import('./worker/source.generated')\n const { startTimeoutMs = 10000 } = this.options\n const url = URL.createObjectURL(new Blob([WORKER_SOURCE], { type: 'text/javascript' }))\n\n return new Promise<Worker>((resolve, reject) => {\n let worker: Worker\n try {\n worker = new Worker(url, { type: 'module', name: 'use-wallet-passkey' })\n } catch (error) {\n URL.revokeObjectURL(url)\n reject(this.startError(error))\n return\n }\n\n const done = () => {\n clearTimeout(timer)\n URL.revokeObjectURL(url)\n }\n const timer = setTimeout(() => {\n done()\n worker.terminate()\n reject(\n new PasskeyError(\n 'key-store-failed',\n `The passkey key worker did not start within ${startTimeoutMs} ms.`\n )\n )\n }, startTimeoutMs)\n\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\n if (event.data.type !== 'ready') return\n done()\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => this.handle(event.data)\n worker.onerror = (event) => {\n event.preventDefault()\n this.lock()\n }\n resolve(worker)\n }\n worker.onerror = (event) => {\n // Keep the load failure from also surfacing as an uncaught page error\n event.preventDefault()\n done()\n worker.terminate()\n reject(this.startError(event.message))\n }\n })\n }\n\n private startError(cause: unknown): PasskeyError {\n const detail = cause instanceof Error ? cause.message : cause ? String(cause) : ''\n return new PasskeyError(\n 'key-store-failed',\n \"The passkey key worker failed to start. If the page has a Content Security Policy, it must allow worker-src blob: and script-src 'wasm-unsafe-eval'.\" +\n (detail ? ` (${detail})` : ''),\n { cause }\n )\n }\n\n private call<T>(\n request: DistributiveOmit<WorkerRequest, 'id'>,\n transfer: Transferable[] = []\n ): Promise<T> {\n const worker = this.worker\n if (!worker) {\n return Promise.reject(\n new PasskeyError('key-store-failed', 'The passkey key store is locked.')\n )\n }\n const id = this.nextId++\n return new Promise<T>((resolve, reject) => {\n this.pending.set(id, { resolve, reject })\n worker.postMessage({ ...request, id }, transfer)\n })\n }\n\n private handle(message: WorkerResponse): void {\n if (message.type === 'locked') {\n this.lock()\n this.options.onIdleLock?.()\n return\n }\n if (message.type !== 'result') return\n const entry = this.pending.get(message.id)\n if (!entry) return\n this.pending.delete(message.id)\n if (message.ok) entry.resolve(message.value)\n else entry.reject(new PasskeyError('key-store-failed', message.error))\n }\n}\n","import { base64ToByteArray, byteArrayToBase64 } from '@txnlab/use-wallet/adapter'\n\nexport const LOCAL_STORAGE_PASSKEY_KEY = '@txnlab/use-wallet:v5:passkey'\n\n/**\n * What the adapter persists about a connected passkey account. Public data\n * only: never the PRF output, the entropy or the private key.\n */\nexport interface PasskeyRecord {\n /** Bumped if the derivation ever changes */\n version: 1\n rpId: string\n context: string\n credentialId: Uint8Array\n publicKey: Uint8Array\n /** The PQ address salt from `addressFromPQKey` */\n salt: number\n address: string\n}\n\ninterface StoredRecord {\n version: 1\n rpId: string\n context: string\n credentialId: string\n publicKey: string\n salt: number\n address: string\n}\n\nexport function loadRecord(): PasskeyRecord | null {\n if (typeof localStorage === 'undefined') return null\n try {\n const raw = localStorage.getItem(LOCAL_STORAGE_PASSKEY_KEY)\n if (!raw) return null\n const stored = JSON.parse(raw) as Partial<StoredRecord>\n if (\n stored.version !== 1 ||\n typeof stored.rpId !== 'string' ||\n typeof stored.context !== 'string' ||\n typeof stored.credentialId !== 'string' ||\n typeof stored.publicKey !== 'string' ||\n typeof stored.salt !== 'number' ||\n typeof stored.address !== 'string'\n ) {\n return null\n }\n return {\n version: 1,\n rpId: stored.rpId,\n context: stored.context,\n credentialId: base64ToByteArray(stored.credentialId),\n publicKey: base64ToByteArray(stored.publicKey),\n salt: stored.salt,\n address: stored.address\n }\n } catch {\n return null\n }\n}\n\nexport function saveRecord(record: PasskeyRecord): void {\n if (typeof localStorage === 'undefined') return\n const stored: StoredRecord = {\n ...record,\n credentialId: byteArrayToBase64(record.credentialId),\n publicKey: byteArrayToBase64(record.publicKey)\n }\n localStorage.setItem(LOCAL_STORAGE_PASSKEY_KEY, JSON.stringify(stored))\n}\n\nexport function removeRecord(): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(LOCAL_STORAGE_PASSKEY_KEY)\n}\n","/**\n * WebAuthn ceremonies.\n *\n * Each function calls `navigator.credentials.*` synchronously, before its\n * first `await`. WebKit (every iOS browser) drops the user gesture across an\n * await, so callers must also start the ceremony before awaiting anything.\n *\n * PRF output depends on whether the user was verified (CTAP2 `hmac-secret`\n * keeps separate secrets for verified and presence-only ceremonies), so every\n * ceremony that evaluates the PRF uses `userVerification: 'required'`. Only\n * `assertUser`, which evaluates nothing, may use `'preferred'`.\n */\nimport { PasskeyError, toPasskeyError } from './errors'\n\nfunction randomBytes(length: number): Uint8Array<ArrayBuffer> {\n return crypto.getRandomValues(new Uint8Array(length))\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, i) => byte === b[i])\n}\n\nfunction getCredentials(): CredentialsContainer {\n if (\n typeof navigator === 'undefined' ||\n !navigator.credentials ||\n !globalThis.PublicKeyCredential\n ) {\n throw new PasskeyError('unsupported', 'This browser does not support passkeys.')\n }\n return navigator.credentials\n}\n\n/** Copy a WebAuthn buffer so the result doesn't alias the credential object */\nfunction copy(buffer: ArrayBuffer | ArrayBufferView): Uint8Array<ArrayBuffer> {\n return ArrayBuffer.isView(buffer)\n ? new Uint8Array(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength))\n : new Uint8Array(new Uint8Array(buffer))\n}\n\nexport interface CreatePasskeyParams {\n rpId: string\n rpName: string\n userName: string\n}\n\n/**\n * Create a discoverable passkey with the PRF extension enabled. Returns its\n * credential ID. The create-time `prf.enabled` flag is unreliable (1Password\n * on iOS returns `{}`, Windows Hello reports `false` but works), so callers\n * confirm PRF support with a scoped `evaluatePrf` right after.\n */\nexport function createPasskey({\n rpId,\n rpName,\n userName\n}: CreatePasskeyParams): Promise<Uint8Array> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().create({\n publicKey: {\n rp: { id: rpId, name: rpName },\n user: { id: randomBytes(16), name: userName, displayName: userName },\n challenge: randomBytes(32),\n pubKeyCredParams: [\n { type: 'public-key', alg: -8 }, // EdDSA\n { type: 'public-key', alg: -7 }, // ES256\n { type: 'public-key', alg: -257 } // RS256\n ],\n authenticatorSelection: {\n residentKey: 'required',\n requireResidentKey: true,\n userVerification: 'required'\n },\n extensions: { prf: {} }\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'No passkey was created.')\n }\n return copy(credential.rawId)\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n\nexport interface EvaluatePrfParams {\n rpId: string\n prfInput: Uint8Array\n /** Scope the ceremony to this credential. Omit to let the user pick a passkey. */\n credentialId?: Uint8Array\n}\n\nexport interface PrfResult {\n credentialId: Uint8Array\n /** The 32-byte PRF output. Zero it once it's used. */\n prf: Uint8Array\n}\n\n/** Run a PRF assertion with `userVerification: 'required'` */\nexport function evaluatePrf({\n rpId,\n prfInput,\n credentialId\n}: EvaluatePrfParams): Promise<PrfResult> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().get({\n publicKey: {\n challenge: randomBytes(32),\n rpId,\n userVerification: 'required',\n allowCredentials: credentialId\n ? [{ type: 'public-key', id: copy(credentialId) }]\n : undefined,\n extensions: { prf: { eval: { first: copy(prfInput) } } }\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'No passkey was selected.')\n }\n const rawId = copy(credential.rawId)\n if (credentialId && !bytesEqual(rawId, credentialId)) {\n throw new PasskeyError('key-mismatch', 'A different passkey answered the request.')\n }\n const first = credential.getClientExtensionResults().prf?.results?.first\n if (!first) {\n throw new PasskeyError(\n 'prf-unsupported',\n \"This passkey's provider doesn't support the PRF extension. Use a passkey from a provider that does, such as iCloud Keychain, Google Password Manager, 1Password, Windows Hello or a FIDO2 security key.\"\n )\n }\n const prf = copy(first)\n if (prf.length !== 32) {\n prf.fill(0)\n throw new PasskeyError('prf-unsupported', `Unexpected PRF output length: ${prf.length}`)\n }\n return { credentialId: rawId, prf }\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n\nexport interface AssertUserParams {\n rpId: string\n credentialId: Uint8Array\n userVerification: 'preferred' | 'required'\n}\n\n/**\n * Run a bare assertion as a consent prompt: no PRF, so it derives nothing and\n * may use `'preferred'`. There is no server to check its signature; it only\n * proves the user approved the prompt on this page.\n */\nexport function assertUser({\n rpId,\n credentialId,\n userVerification\n}: AssertUserParams): Promise<void> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().get({\n publicKey: {\n challenge: randomBytes(32),\n rpId,\n userVerification,\n allowCredentials: [{ type: 'public-key', id: copy(credentialId) }]\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'The passkey prompt was dismissed.')\n }\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n","/**\n * Passkey Wallet Adapter for Algorand\n *\n * Derives a Falcon-1024 (post-quantum) account from a passkey's WebAuthn PRF\n * output. There is no extension, app or seed phrase to set up; the passkey\n * provider (iCloud Keychain, Google Password Manager, 1Password, Windows\n * Hello, a security key) syncs and protects the credential.\n *\n * SECURITY CONSIDERATIONS:\n * - The account is bound to the relying party ID (by default the page's\n * hostname). Whoever controls that domain can run a PRF ceremony and recover\n * the key with one tap, and the account is stranded if the domain is lost,\n * so offer `exportRecoveryPhrase`.\n * - Only public data is persisted: the credential ID, public key, address\n * salt and address. Never the PRF output, the entropy or the private key.\n * - The private key lives in a dedicated worker, never on the main thread.\n * Without `session`, the worker is terminated after every request.\n */\n\nimport algosdk from 'algosdk'\nimport { canonify } from 'canonify'\nimport {\n BaseWallet,\n ScopeType,\n SignDataError,\n encodeEmptySignature,\n flattenTxnGroup,\n isSignedTxn,\n isTransactionArray,\n type AdapterConstructorParams,\n type Siwa,\n type StdSignDataResponse,\n type StdSignMetadata,\n type WalletAccount,\n type WalletMetadata,\n type WalletState\n} from '@txnlab/use-wallet/adapter'\nimport { assertValidContext, prfInput } from './derivation'\nimport { PasskeyError } from './errors'\nimport { icon } from './icon'\nimport { WorkerKeyStore, type PasskeyKeyStore } from './key-store'\nimport { loadRecord, removeRecord, saveRecord, type PasskeyRecord } from './record'\nimport { assertUser, createPasskey, evaluatePrf, type PrfResult } from './webauthn'\n\nconst ICON = `data:image/svg+xml;base64,${btoa(icon)}`\n\nconst DEFAULT_IDLE_TIMEOUT_MS = 15 * 60 * 1000\n\n/**\n * How to confirm a signing request while a session is unlocked:\n * - `silent`: sign without a prompt\n * - `presence`: a passkey prompt with `userVerification: 'preferred'`, which\n * some providers satisfy with a tap and no biometric or system password\n * - `verify`: a passkey prompt with `userVerification: 'required'`\n *\n * These prompts evaluate no PRF, so they never change the account. When the\n * session is locked, the PRF ceremony that unlocks it is the confirmation.\n */\nexport type PasskeyConfirmation = 'silent' | 'presence' | 'verify'\n\nexport type PasskeySigningRequest =\n { type: 'transactions'; transactions: algosdk.Transaction[] } | { type: 'data'; siwa: Siwa }\n\nexport interface PasskeySessionOptions {\n /** Lock the session after this long without a signature; 0 never locks it. Default: 15 minutes */\n idleTimeoutMs?: number\n}\n\nexport interface PasskeyOptions {\n /**\n * The WebAuthn relying party ID: the domain the passkey (and so the account)\n * is bound to. Default: `location.hostname`. Set a parent domain (e.g.\n * `example.com` for `app.example.com`) to share one account across its\n * subdomains.\n */\n rpId?: string\n /** The relying party name shown in passkey prompts. Default: the RP ID */\n rpName?: string\n /**\n * Separates this app's account from others on the same RP ID. Up to 128\n * characters, no `:`. Default: `''`. Changing it changes the account.\n */\n context?: string\n /** The user name new passkeys are saved under. Default: `<rpName> account` */\n userName?: string\n /**\n * Keep the key unlocked between signatures, so only the first signature\n * (or `connect`) runs a PRF ceremony with full user verification; later ones\n * are confirmed per `confirm`. The key is held in a dedicated worker until\n * the idle timeout, `disconnect` or a page reload. Default: `false`, a PRF\n * ceremony for every request.\n */\n session?: boolean | PasskeySessionOptions\n /**\n * Chooses how to confirm each signing request while a session is unlocked.\n * Called synchronously, before the prompt: WebKit drops the user gesture\n * across an await, so this must not be async. Default: `() => 'presence'`.\n */\n confirm?: (request: PasskeySigningRequest) => PasskeyConfirmation\n /**\n * Replaces the built-in worker key store, e.g. to keep a session across page\n * reloads. The adapter treats a custom key store as a session: it never\n * locks it after signing, only on `disconnect`. May be called more than once.\n */\n keyStore?: () => PasskeyKeyStore\n}\n\nexport interface PasskeyConnectArgs {\n /** Create a new passkey (and account) instead of signing in with an existing one */\n create?: boolean\n /** The user name to save a new passkey under */\n userName?: string\n}\n\nexport interface PasskeyRecoveryPhrase {\n /** Import the phrase as a Falcon-1024 account, not a regular 25-word account */\n scheme: 'falcon-1024'\n /** The 25-word phrase */\n phrase: string\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, i) => byte === b[i])\n}\n\nfunction emptySignatureFor(record: PasskeyRecord): string {\n return encodeEmptySignature({\n pqsig: {\n sch: algosdk.FALCON_1024_SCHEME,\n slt: record.salt,\n pk: record.publicKey,\n sig: new Uint8Array()\n }\n })\n}\n\nasync function sha256(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await crypto.subtle.digest('SHA-256', new Uint8Array(data)))\n}\n\nexport class PasskeyAdapter extends BaseWallet<PasskeyOptions> {\n private keyStore: PasskeyKeyStore | null = null\n\n constructor(params: AdapterConstructorParams<PasskeyOptions>) {\n super(params)\n assertValidContext(this.context)\n }\n\n static defaultMetadata: WalletMetadata = {\n name: 'Passkey',\n icon: ICON\n }\n\n // ---------- Options ----------------------------------------------- //\n\n private get rpId(): string {\n return this.options.rpId ?? location.hostname\n }\n\n private get rpName(): string {\n return this.options.rpName ?? this.rpId\n }\n\n private get context(): string {\n return this.options.context ?? ''\n }\n\n /** True when the key stays unlocked between requests */\n private get isSession(): boolean {\n return !!this.options.session || !!this.options.keyStore\n }\n\n private createKeyStore(): PasskeyKeyStore {\n if (this.options.keyStore) return this.options.keyStore()\n\n const { session } = this.options\n const idleTimeoutMs = session\n ? ((typeof session === 'object' ? session.idleTimeoutMs : undefined) ??\n DEFAULT_IDLE_TIMEOUT_MS)\n : 0\n return new WorkerKeyStore({\n idleTimeoutMs,\n onIdleLock: () => this.logger.info('Session locked after inactivity')\n })\n }\n\n private getKeyStore(): PasskeyKeyStore {\n this.keyStore ??= this.createKeyStore()\n return this.keyStore\n }\n\n /** Drop the key after a request, unless a session keeps it */\n private release(): void {\n if (!this.isSession) this.keyStore?.lock()\n }\n\n // ---------- Connection -------------------------------------------- //\n\n /**\n * Sign in with an existing passkey, or create one with `{ create: true }`.\n * Show both choices: a missing passkey and a cancelled prompt raise the same\n * error, so the adapter can't tell which one happened.\n */\n public connect = async (args?: PasskeyConnectArgs): Promise<WalletAccount[]> => {\n const rpId = this.rpId\n const context = this.context\n const input = prfInput(context)\n this.logger.info(args?.create ? 'Creating passkey...' : 'Connecting...')\n\n let result: PrfResult | undefined\n try {\n // The first ceremony starts before any await (WebKit drops the user gesture)\n if (args?.create) {\n const credentialId = await createPasskey({\n rpId,\n rpName: this.rpName,\n userName: args.userName ?? this.options.userName ?? `${this.rpName} account`\n })\n // Scope to the new credential so the picker can't bind a sibling passkey\n result = await evaluatePrf({ rpId, prfInput: input, credentialId })\n } else {\n result = await evaluatePrf({ rpId, prfInput: input })\n }\n\n this.keyStore?.lock()\n const keyStore = this.getKeyStore()\n const publicKey = await keyStore.unlock(result.prf)\n this.release()\n\n const { address, salt } = algosdk.addressFromPQKey(algosdk.FALCON_1024_SCHEME, publicKey)\n const record: PasskeyRecord = {\n version: 1,\n rpId,\n context,\n credentialId: result.credentialId,\n publicKey,\n salt,\n address: address.toString()\n }\n saveRecord(record)\n\n const walletAccount: WalletAccount = {\n name: `${this.metadata.name} Account`,\n address: record.address,\n emptySignature: emptySignatureFor(record)\n }\n const walletState: WalletState = {\n accounts: [walletAccount],\n activeAccount: walletAccount\n }\n this.store.addWallet(walletState)\n\n this.logger.info('Connected successfully', walletState)\n return [walletAccount]\n } catch (error: any) {\n result?.prf.fill(0)\n this.keyStore?.lock()\n this.logger.error('Error connecting:', error.message)\n throw error\n }\n }\n\n public disconnect = async (): Promise<void> => {\n this.logger.info('Disconnecting...')\n this.keyStore?.lock()\n this.keyStore = null\n removeRecord()\n this.onDisconnect()\n this.logger.info('Disconnected')\n }\n\n /** Restores the account without a ceremony; the key unlocks on the first signature */\n public resumeSession = async (): Promise<void> => {\n const walletState = this.store.getWalletState()\n if (!walletState) {\n this.logger.info('No session to resume')\n return\n }\n\n const record = loadRecord()\n if (!record || !this.matchesRecord(record, walletState)) {\n this.logger.info('No passkey record for this session, disconnecting...')\n await this.disconnect()\n return\n }\n this.logger.info('Session resumed')\n }\n\n private matchesRecord(record: PasskeyRecord, walletState: WalletState): boolean {\n return (\n record.rpId === this.rpId &&\n record.context === this.context &&\n walletState.accounts.length === 1 &&\n walletState.accounts[0].address === record.address\n )\n }\n\n private requireRecord(): PasskeyRecord {\n const walletState = this.store.getWalletState()\n const record = loadRecord()\n if (!walletState || !record || !this.matchesRecord(record, walletState)) {\n throw new PasskeyError('not-connected', 'No passkey account is connected.')\n }\n return record\n }\n\n // ---------- Authorization ----------------------------------------- //\n\n /**\n * Make sure the key is unlocked and the user approved this request. When the\n * key is locked, the PRF ceremony that unlocks it is the approval. Starts its\n * prompt before any await.\n */\n private async authorize(request: PasskeySigningRequest, record: PasskeyRecord): Promise<void> {\n const keyStore = this.getKeyStore()\n\n if (!keyStore.unlocked) {\n const { prf } = await evaluatePrf({\n rpId: record.rpId,\n prfInput: prfInput(record.context),\n credentialId: record.credentialId\n })\n const publicKey = await keyStore.unlock(prf)\n if (!bytesEqual(publicKey, record.publicKey)) {\n keyStore.lock()\n throw new PasskeyError(\n 'key-mismatch',\n 'This passkey derived a different key than the connected account. Disconnect and connect again.'\n )\n }\n return\n }\n\n const confirmation = this.options.confirm?.(request) ?? 'presence'\n if (confirmation === 'silent') return\n await assertUser({\n rpId: record.rpId,\n credentialId: record.credentialId,\n // Anything but an explicit 'presence' gets full verification\n userVerification: confirmation === 'presence' ? 'preferred' : 'required'\n })\n }\n\n // ---------- Signing ----------------------------------------------- //\n\n private processTxns(\n txnGroup: algosdk.Transaction[],\n indexesToSign: number[] | undefined,\n address: string\n ): algosdk.Transaction[] {\n return txnGroup.filter(\n (txn, index) =>\n (!indexesToSign || indexesToSign.includes(index)) && txn.sender.toString() === address\n )\n }\n\n private processEncodedTxns(\n txnGroup: Uint8Array[],\n indexesToSign: number[] | undefined,\n address: string\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txnBuffer, index) => {\n const isSigned = isSignedTxn(algosdk.msgpackRawDecode(txnBuffer))\n const txn: algosdk.Transaction = isSigned\n ? algosdk.decodeSignedTransaction(txnBuffer).txn\n : algosdk.decodeUnsignedTransaction(txnBuffer)\n\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n if (isIndexMatch && !isSigned && txn.sender.toString() === address) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n /**\n * Signs the transactions sent from the connected account. Each one carries a\n * Falcon-1024 `pqsig` (about 3.2 KB) and pays an extra 2× the minimum fee;\n * get fees by simulating with `emptyTransactionSigner`.\n */\n public signTransactions = async <T extends algosdk.Transaction[] | Uint8Array[]>(\n txnGroup: T | T[],\n indexesToSign?: number[]\n ): Promise<(Uint8Array | null)[]> => {\n const record = this.requireRecord()\n\n const txnsToSign = isTransactionArray(txnGroup)\n ? this.processTxns(flattenTxnGroup(txnGroup), indexesToSign, record.address)\n : this.processEncodedTxns(\n flattenTxnGroup(txnGroup as Uint8Array[]),\n indexesToSign,\n record.address\n )\n if (txnsToSign.length === 0) return []\n\n try {\n this.logger.debug('Signing transactions...', { txnsToSign })\n await this.authorize({ type: 'transactions', transactions: txnsToSign }, record)\n\n const keyStore = this.getKeyStore()\n const { txnSigner } = algosdk.addressWithSignersFromRawFalcon1024Signer({\n falcon1024PublicKey: record.publicKey,\n falcon1024Signer: (bytes) => keyStore.sign(bytes)\n })\n const signedTxns = await txnSigner(\n txnsToSign,\n txnsToSign.map((_, index) => index)\n )\n this.logger.debug('Transactions signed successfully')\n return signedTxns\n } catch (error: any) {\n this.logger.error('Error signing transactions:', error.message)\n throw error\n } finally {\n this.release()\n }\n }\n\n // ---------- signData (ARC-60) ------------------------------------- //\n\n public canSignData = true\n\n /**\n * Signs an ARC-60 authentication request (SIWA) with the account's Falcon\n * key, scheme `falcon-1024-det`. The signature covers\n * `SHA-256(data) || SHA-256(authenticatorData)`.\n */\n public signData = async (\n data: string,\n metadata: StdSignMetadata\n ): Promise<StdSignDataResponse> => {\n const record = this.requireRecord()\n\n // Validate before prompting, without awaiting\n if (metadata.encoding !== 'base64') {\n throw new SignDataError('Failed decoding', 4602)\n }\n if (metadata.scope !== ScopeType.AUTH) {\n throw new SignDataError('Invalid Scope', 4600)\n }\n // Sign exactly the bytes the dApp sent: strict UTF-8 with no BOM, so the\n // decoded JSON can't differ from them\n let bytes: Uint8Array\n let json: string\n let siwa: Siwa\n try {\n bytes = algosdk.base64ToBytes(data)\n json = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes)\n siwa = JSON.parse(json)\n } catch {\n throw new SignDataError('Bad JSON', 4609)\n }\n if (!siwa || typeof siwa !== 'object' || canonify(siwa) !== json) {\n throw new SignDataError('Bad JSON', 4609)\n }\n if (siwa.domain !== location.host) {\n throw new SignDataError('Failed Domain Auth', 4610)\n }\n if (siwa.type !== 'falcon-1024-det' || siwa.account_address !== record.address) {\n throw new SignDataError('Invalid Signer', 4603)\n }\n\n try {\n await this.authorize({ type: 'data', siwa }, record)\n\n const stdSignData = await this.createStdSignData(data)\n if (!bytesEqual(stdSignData.signer, record.publicKey)) {\n throw new SignDataError('Invalid Signer', 4603)\n }\n\n const toSign = new Uint8Array([\n ...(await sha256(bytes)),\n ...(await sha256(stdSignData.authenticatorData))\n ])\n const signature = await this.getKeyStore().sign(toSign)\n return { ...stdSignData, signature }\n } catch (error: any) {\n this.logger.error('Error signing data:', error.message)\n throw error\n } finally {\n this.release()\n }\n }\n\n // ---------- Recovery ---------------------------------------------- //\n\n /**\n * Returns the account's 25-word recovery phrase after a fresh passkey\n * ceremony with full user verification. Show it with a warning that it must\n * be imported as a Falcon / quantum account (Lute: \"Falcon25 account\";\n * Pera: quantum import; `algokey pq import --scheme falcon-1024`): the same\n * words in a regular 25-word import restore a different, empty account.\n */\n public exportRecoveryPhrase = async (): Promise<PasskeyRecoveryPhrase> => {\n const record = this.requireRecord()\n\n const { prf } = await evaluatePrf({\n rpId: record.rpId,\n prfInput: prfInput(record.context),\n credentialId: record.credentialId\n })\n\n const check = this.createKeyStore()\n try {\n const publicKey = await check.unlock(new Uint8Array(prf))\n if (!bytesEqual(publicKey, record.publicKey)) {\n throw new PasskeyError(\n 'key-mismatch',\n 'This passkey derived a different key than the connected account.'\n )\n }\n return { scheme: 'falcon-1024', phrase: algosdk.mnemonicFromSeed(prf) }\n } finally {\n check.lock()\n prf.fill(0)\n }\n }\n}\n","import { PasskeyAdapter } from './adapter'\nimport type {\n PasskeyConfirmation,\n PasskeyConnectArgs,\n PasskeyOptions,\n PasskeyRecoveryPhrase,\n PasskeySessionOptions,\n PasskeySigningRequest\n} from './adapter'\nimport { assertValidContext } from './derivation'\nimport type { WalletAdapterConfig, WalletFactoryOptions } from '@txnlab/use-wallet'\n\nexport const WALLET_ID = 'passkey' as const\n\nexport function passkey(options?: PasskeyOptions & WalletFactoryOptions): WalletAdapterConfig {\n const { metadata, ...adapterOptions } = options ?? {}\n assertValidContext(adapterOptions.context ?? '')\n return {\n id: WALLET_ID,\n metadata: { ...PasskeyAdapter.defaultMetadata, ...metadata },\n Adapter: PasskeyAdapter as unknown as WalletAdapterConfig['Adapter'],\n options:\n Object.keys(adapterOptions).length > 0\n ? (adapterOptions as unknown as Record<string, unknown>)\n : undefined\n }\n}\n\nexport { PasskeyAdapter }\nexport { PasskeyError, type PasskeyErrorCode } from './errors'\nexport { WorkerKeyStore, type PasskeyKeyStore, type WorkerKeyStoreOptions } from './key-store'\nexport { PRF_LABEL_PREFIX, keygenSeedFromEntropy, prfInput } from './derivation'\nexport { LOCAL_STORAGE_PASSKEY_KEY } from './record'\nexport type {\n PasskeyConfirmation,\n PasskeyConnectArgs,\n PasskeyOptions,\n PasskeyRecoveryPhrase,\n PasskeySessionOptions,\n PasskeySigningRequest\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["bytesEqual"],"sources":["../src/derivation.ts","../src/errors.ts","../src/icon.ts","../src/key-store.ts","../src/record.ts","../src/webauthn.ts","../src/adapter.ts","../src/index.ts"],"sourcesContent":["/**\n * The derivation from a passkey's PRF output to a Falcon-1024 account.\n *\n * ```\n * entropy = PRF(eval.first = PRF_LABEL_PREFIX + context) // 32 bytes\n * keygenSeed = SHA-512/256(\"PQK\" || \"f1\" || entropy)\n * {pk, sk} = falcon-1024 generateKey(keygenSeed) // falcon_det1024\n * address = algosdk.addressFromPQKey(FALCON_1024_SCHEME, pk)\n * phrase = algosdk.mnemonicFromSeed(entropy) // 25 words\n * ```\n *\n * The keygen seed matches go-algorand's `algokey pq` (`derivePQKeySeed`),\n * algosdk's `pq25WordMnemonicToSeed`, Lute's Falcon25 accounts, Pera and\n * ARC-76, so the recovery phrase restores the same account in any of them.\n *\n * NEVER CHANGE `PRF_LABEL_PREFIX`: the label is the PRF input, so changing it\n * changes every account derived from it.\n */\nimport { sha512_256 } from '@noble/hashes/sha2'\n\n/** The PRF input prefix. Frozen: changing it moves every account. */\nexport const PRF_LABEL_PREFIX = '@txnlab/use-wallet-passkey:falcon-1024:v1:'\n\n/**\n * A `context` may not contain `:`, so a future account slot can be encoded as\n * a `:<n>` suffix without colliding with any slot-0 label.\n */\nconst CONTEXT_PATTERN = /^[^:]{0,128}$/\n\nexport function assertValidContext(context: string): void {\n if (!CONTEXT_PATTERN.test(context)) {\n throw new Error('passkey: `context` must be at most 128 characters and must not contain \":\"')\n }\n}\n\n/** The PRF input (`eval.first`) for an account context */\nexport function prfInput(context: string): Uint8Array {\n assertValidContext(context)\n return new TextEncoder().encode(PRF_LABEL_PREFIX + context)\n}\n\n// \"PQK\" || \"f1\"\nconst PQK_PREFIX = new Uint8Array([0x50, 0x51, 0x4b, 0x66, 0x31])\n\n/** keygenSeed = SHA-512/256(\"PQK\" || \"f1\" || entropy) */\nexport function keygenSeedFromEntropy(entropy: Uint8Array): Uint8Array {\n if (entropy.length !== 32) {\n throw new Error('entropy must be 32 bytes')\n }\n const input = new Uint8Array(PQK_PREFIX.length + entropy.length)\n input.set(PQK_PREFIX)\n input.set(entropy, PQK_PREFIX.length)\n try {\n return sha512_256(input)\n } finally {\n input.fill(0)\n }\n}\n","export type PasskeyErrorCode =\n /** The user cancelled or the request timed out (or, on iOS, an in-app browser blocked it) */\n | 'cancelled'\n /** This browser can't run passkey ceremonies (no WebAuthn, or an Android WebView) */\n | 'unsupported'\n /** The passkey's provider doesn't support the PRF extension */\n | 'prf-unsupported'\n /** The relying party ID isn't valid for this page's origin */\n | 'invalid-rp-id'\n /** The passkey derived a different key than the connected account's */\n | 'key-mismatch'\n /** No account is connected */\n | 'not-connected'\n /** The key store failed, e.g. the session worker was blocked by a CSP */\n | 'key-store-failed'\n | 'failed'\n\nexport class PasskeyError extends Error {\n readonly code: PasskeyErrorCode\n\n constructor(code: PasskeyErrorCode, message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = 'PasskeyError'\n this.code = code\n }\n}\n\n/**\n * Classifies a WebAuthn failure after the fact. Never gate on user-agent\n * sniffing up front: these errors are the only reliable signal.\n */\nexport function toPasskeyError(error: unknown): PasskeyError {\n if (error instanceof PasskeyError) return error\n\n const name = error instanceof Error ? error.name : undefined\n const message = error instanceof Error ? error.message : String(error)\n\n switch (name) {\n case 'NotAllowedError':\n case 'AbortError':\n return new PasskeyError(\n 'cancelled',\n 'The passkey request was cancelled or timed out. In an in-app browser, open the page in Safari or Chrome.',\n { cause: error }\n )\n case 'NotSupportedError':\n // Android WebView: \"...connecting to Web Authentication service\"\n return new PasskeyError(\n 'unsupported',\n 'This browser does not support passkeys here. Open the page in a full browser.',\n { cause: error }\n )\n case 'SecurityError':\n return new PasskeyError(\n 'invalid-rp-id',\n `The passkey relying party ID is not valid for this origin: ${message}`,\n { cause: error }\n )\n default:\n return new PasskeyError('failed', message, { cause: error })\n }\n}\n","export const icon = `\n<svg viewBox=\"0 0 40 40\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect fill=\"#1F2A44\" width=\"40\" height=\"40\" rx=\"8\"/>\n <circle cx=\"16\" cy=\"13.5\" r=\"5\" fill=\"none\" stroke=\"#FFFFFF\" stroke-width=\"2.5\"/>\n <path d=\"M7.5 30c0-4.7 3.8-8.5 8.5-8.5 1.6 0 3.1.4 4.3 1.2\" fill=\"none\" stroke=\"#FFFFFF\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <circle cx=\"27\" cy=\"23\" r=\"3.6\" fill=\"none\" stroke=\"#7DD3FC\" stroke-width=\"2.4\"/>\n <path d=\"M27 26.6V33m0-3h2.6M27 32.4h2\" fill=\"none\" stroke=\"#7DD3FC\" stroke-width=\"2.4\" stroke-linecap=\"round\"/>\n</svg>\n`\n","import { PasskeyError } from './errors'\nimport type { PasskeyTimeoutReason, WorkerRequest, WorkerResponse } from './worker/protocol'\n\n/**\n * Why the built-in key store locked itself: a timeout, or `'key-store'` when\n * its worker failed\n */\nexport type WorkerKeyStoreLockReason = PasskeyTimeoutReason | 'key-store'\n\n/**\n * Holds the Falcon-1024 private key between a passkey's PRF ceremony and the\n * signatures it authorizes. The adapter's built-in key store runs the key in a\n * dedicated worker; supply your own (the `keyStore` option) to hold it\n * elsewhere, e.g. a SharedWorker that outlives a page reload.\n */\nexport interface PasskeyKeyStore {\n /** True when `sign` can run without a new PRF ceremony */\n readonly unlocked: boolean\n /**\n * Derive the key from the passkey's 32-byte PRF output and return its\n * Falcon-1024 public key. Implementations must zero `entropy` and derive the\n * keygen seed with `keygenSeedFromEntropy`.\n */\n unlock(entropy: Uint8Array): Promise<Uint8Array>\n /** Sign exactly these bytes with the private key */\n sign(bytes: Uint8Array): Promise<Uint8Array>\n /** Drop the private key */\n lock(): void\n}\n\nexport interface WorkerKeyStoreOptions {\n /** Clear the key after this long without a signature. 0 keeps it until `lock`. */\n idleTimeoutMs?: number\n /** Clear the key this long after `unlock`, however often it signs. 0 sets no limit. */\n maxAgeMs?: number\n /** Called when the key store locks itself, not on `lock` */\n onLock?: (reason: WorkerKeyStoreLockReason) => void\n /** How long to wait for the worker to load. Default: 10000 */\n startTimeoutMs?: number\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n/**\n * The built-in key store: a dedicated module worker started from a Blob URL.\n * The worker module is inlined into this package, so a consumer's bundler has\n * no worker entry to resolve; the page's CSP must allow `worker-src blob:` and\n * `'wasm-unsafe-eval'`. `lock` terminates the worker, which drops its WASM\n * heap.\n */\nexport class WorkerKeyStore implements PasskeyKeyStore {\n private worker: Worker | null = null\n private isUnlocked = false\n private nextId = 1\n private pending = new Map<\n number,\n { resolve: (value: any) => void; reject: (error: Error) => void }\n >()\n\n constructor(private readonly options: WorkerKeyStoreOptions = {}) {}\n\n get unlocked(): boolean {\n return this.isUnlocked\n }\n\n async unlock(entropy: Uint8Array): Promise<Uint8Array> {\n // Copy into a buffer the worker takes ownership of, then zero the caller's\n const buffer = new Uint8Array(entropy).buffer\n entropy.fill(0)\n\n this.lock()\n try {\n this.worker = await this.start()\n const publicKey = await this.call<Uint8Array>(\n {\n op: 'unlock',\n entropy: buffer,\n idleTimeoutMs: this.options.idleTimeoutMs,\n maxAgeMs: this.options.maxAgeMs\n },\n [buffer]\n )\n this.isUnlocked = true\n return publicKey\n } catch (error) {\n new Uint8Array(buffer).fill(0)\n this.lock()\n throw error\n }\n }\n\n sign(bytes: Uint8Array): Promise<Uint8Array> {\n if (!this.isUnlocked) {\n return Promise.reject(\n new PasskeyError('key-store-failed', 'The passkey key store is locked.')\n )\n }\n return this.call<Uint8Array>({ op: 'sign', bytes })\n }\n\n lock(): void {\n this.isUnlocked = false\n if (!this.worker) return\n this.worker.terminate()\n this.worker = null\n const error = new PasskeyError('key-store-failed', 'The passkey key store was locked.')\n for (const { reject } of this.pending.values()) reject(error)\n this.pending.clear()\n }\n\n private async start(): Promise<Worker> {\n // Loaded lazily, after the passkey ceremony, so it never sits between a\n // user's tap and navigator.credentials\n const { WORKER_SOURCE } = await import('./worker/source.generated')\n const { startTimeoutMs = 10000 } = this.options\n const url = URL.createObjectURL(new Blob([WORKER_SOURCE], { type: 'text/javascript' }))\n\n return new Promise<Worker>((resolve, reject) => {\n let worker: Worker\n try {\n worker = new Worker(url, { type: 'module', name: 'use-wallet-passkey' })\n } catch (error) {\n URL.revokeObjectURL(url)\n reject(this.startError(error))\n return\n }\n\n const done = () => {\n clearTimeout(timer)\n URL.revokeObjectURL(url)\n }\n const timer = setTimeout(() => {\n done()\n worker.terminate()\n reject(\n new PasskeyError(\n 'key-store-failed',\n `The passkey key worker did not start within ${startTimeoutMs} ms.`\n )\n )\n }, startTimeoutMs)\n\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\n if (event.data.type !== 'ready') return\n done()\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => this.handle(event.data)\n worker.onerror = (event) => {\n event.preventDefault()\n const wasUnlocked = this.isUnlocked\n this.lock()\n if (wasUnlocked) this.options.onLock?.('key-store')\n }\n resolve(worker)\n }\n worker.onerror = (event) => {\n // Keep the load failure from also surfacing as an uncaught page error\n event.preventDefault()\n done()\n worker.terminate()\n reject(this.startError(event.message))\n }\n })\n }\n\n private startError(cause: unknown): PasskeyError {\n const detail = cause instanceof Error ? cause.message : cause ? String(cause) : ''\n return new PasskeyError(\n 'key-store-failed',\n \"The passkey key worker failed to start. If the page has a Content Security Policy, it must allow worker-src blob: and script-src 'wasm-unsafe-eval'.\" +\n (detail ? ` (${detail})` : ''),\n { cause }\n )\n }\n\n private call<T>(\n request: DistributiveOmit<WorkerRequest, 'id'>,\n transfer: Transferable[] = []\n ): Promise<T> {\n const worker = this.worker\n if (!worker) {\n return Promise.reject(\n new PasskeyError('key-store-failed', 'The passkey key store is locked.')\n )\n }\n const id = this.nextId++\n return new Promise<T>((resolve, reject) => {\n this.pending.set(id, { resolve, reject })\n worker.postMessage({ ...request, id }, transfer)\n })\n }\n\n private handle(message: WorkerResponse): void {\n if (message.type === 'locked') {\n this.lock()\n this.options.onLock?.(message.reason)\n return\n }\n if (message.type !== 'result') return\n const entry = this.pending.get(message.id)\n if (!entry) return\n this.pending.delete(message.id)\n if (message.ok) entry.resolve(message.value)\n else entry.reject(new PasskeyError('key-store-failed', message.error))\n }\n}\n","import { base64ToByteArray, byteArrayToBase64 } from '@txnlab/use-wallet/adapter'\n\nexport const LOCAL_STORAGE_PASSKEY_KEY = '@txnlab/use-wallet:v5:passkey'\n\n/**\n * What the adapter persists about a connected passkey account. Public data\n * only: never the PRF output, the entropy or the private key.\n */\nexport interface PasskeyRecord {\n /** Bumped if the derivation ever changes */\n version: 1\n rpId: string\n context: string\n credentialId: Uint8Array\n publicKey: Uint8Array\n /** The PQ address salt from `addressFromPQKey` */\n salt: number\n address: string\n}\n\ninterface StoredRecord {\n version: 1\n rpId: string\n context: string\n credentialId: string\n publicKey: string\n salt: number\n address: string\n}\n\nexport function loadRecord(): PasskeyRecord | null {\n if (typeof localStorage === 'undefined') return null\n try {\n const raw = localStorage.getItem(LOCAL_STORAGE_PASSKEY_KEY)\n if (!raw) return null\n const stored = JSON.parse(raw) as Partial<StoredRecord>\n if (\n stored.version !== 1 ||\n typeof stored.rpId !== 'string' ||\n typeof stored.context !== 'string' ||\n typeof stored.credentialId !== 'string' ||\n typeof stored.publicKey !== 'string' ||\n typeof stored.salt !== 'number' ||\n typeof stored.address !== 'string'\n ) {\n return null\n }\n return {\n version: 1,\n rpId: stored.rpId,\n context: stored.context,\n credentialId: base64ToByteArray(stored.credentialId),\n publicKey: base64ToByteArray(stored.publicKey),\n salt: stored.salt,\n address: stored.address\n }\n } catch {\n return null\n }\n}\n\nexport function saveRecord(record: PasskeyRecord): void {\n if (typeof localStorage === 'undefined') return\n const stored: StoredRecord = {\n ...record,\n credentialId: byteArrayToBase64(record.credentialId),\n publicKey: byteArrayToBase64(record.publicKey)\n }\n localStorage.setItem(LOCAL_STORAGE_PASSKEY_KEY, JSON.stringify(stored))\n}\n\nexport function removeRecord(): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(LOCAL_STORAGE_PASSKEY_KEY)\n}\n","/**\n * WebAuthn ceremonies.\n *\n * Each function calls `navigator.credentials.*` synchronously, before its\n * first `await`. WebKit (every iOS browser) drops the user gesture across an\n * await, so callers must also start the ceremony before awaiting anything.\n *\n * PRF output depends on whether the user was verified (CTAP2 `hmac-secret`\n * keeps separate secrets for verified and presence-only ceremonies), so every\n * ceremony that evaluates the PRF uses `userVerification: 'required'`. Only\n * `assertUser`, which evaluates nothing, may use `'preferred'`.\n */\nimport { PasskeyError, toPasskeyError } from './errors'\n\nfunction randomBytes(length: number): Uint8Array<ArrayBuffer> {\n return crypto.getRandomValues(new Uint8Array(length))\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, i) => byte === b[i])\n}\n\nfunction getCredentials(): CredentialsContainer {\n if (\n typeof navigator === 'undefined' ||\n !navigator.credentials ||\n !globalThis.PublicKeyCredential\n ) {\n throw new PasskeyError('unsupported', 'This browser does not support passkeys.')\n }\n return navigator.credentials\n}\n\n/** Copy a WebAuthn buffer so the result doesn't alias the credential object */\nfunction copy(buffer: ArrayBuffer | ArrayBufferView): Uint8Array<ArrayBuffer> {\n return ArrayBuffer.isView(buffer)\n ? new Uint8Array(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength))\n : new Uint8Array(new Uint8Array(buffer))\n}\n\nexport interface CreatePasskeyParams {\n rpId: string\n rpName: string\n userName: string\n}\n\n/**\n * Create a discoverable passkey with the PRF extension enabled. Returns its\n * credential ID. The create-time `prf.enabled` flag is unreliable (1Password\n * on iOS returns `{}`, Windows Hello reports `false` but works), so callers\n * confirm PRF support with a scoped `evaluatePrf` right after.\n */\nexport function createPasskey({\n rpId,\n rpName,\n userName\n}: CreatePasskeyParams): Promise<Uint8Array> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().create({\n publicKey: {\n rp: { id: rpId, name: rpName },\n user: { id: randomBytes(16), name: userName, displayName: userName },\n challenge: randomBytes(32),\n pubKeyCredParams: [\n { type: 'public-key', alg: -8 }, // EdDSA\n { type: 'public-key', alg: -7 }, // ES256\n { type: 'public-key', alg: -257 } // RS256\n ],\n authenticatorSelection: {\n residentKey: 'required',\n requireResidentKey: true,\n userVerification: 'required'\n },\n extensions: { prf: {} }\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'No passkey was created.')\n }\n return copy(credential.rawId)\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n\nexport interface EvaluatePrfParams {\n rpId: string\n prfInput: Uint8Array\n /** Scope the ceremony to this credential. Omit to let the user pick a passkey. */\n credentialId?: Uint8Array\n}\n\nexport interface PrfResult {\n credentialId: Uint8Array\n /** The 32-byte PRF output. Zero it once it's used. */\n prf: Uint8Array\n}\n\n/** Run a PRF assertion with `userVerification: 'required'` */\nexport function evaluatePrf({\n rpId,\n prfInput,\n credentialId\n}: EvaluatePrfParams): Promise<PrfResult> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().get({\n publicKey: {\n challenge: randomBytes(32),\n rpId,\n userVerification: 'required',\n allowCredentials: credentialId\n ? [{ type: 'public-key', id: copy(credentialId) }]\n : undefined,\n extensions: { prf: { eval: { first: copy(prfInput) } } }\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'No passkey was selected.')\n }\n const rawId = copy(credential.rawId)\n if (credentialId && !bytesEqual(rawId, credentialId)) {\n throw new PasskeyError('key-mismatch', 'A different passkey answered the request.')\n }\n const first = credential.getClientExtensionResults().prf?.results?.first\n if (!first) {\n throw new PasskeyError(\n 'prf-unsupported',\n \"This passkey's provider doesn't support the PRF extension. Use a passkey from a provider that does, such as iCloud Keychain, Google Password Manager, 1Password, Windows Hello or a FIDO2 security key.\"\n )\n }\n const prf = copy(first)\n if (prf.length !== 32) {\n prf.fill(0)\n throw new PasskeyError('prf-unsupported', `Unexpected PRF output length: ${prf.length}`)\n }\n return { credentialId: rawId, prf }\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n\nexport interface AssertUserParams {\n rpId: string\n credentialId: Uint8Array\n userVerification: 'preferred' | 'required'\n}\n\n/**\n * Run a bare assertion as a consent prompt: no PRF, so it derives nothing and\n * may use `'preferred'`. There is no server to check its signature; it only\n * proves the user approved the prompt on this page.\n */\nexport function assertUser({\n rpId,\n credentialId,\n userVerification\n}: AssertUserParams): Promise<void> {\n let pending: Promise<Credential | null>\n try {\n pending = getCredentials().get({\n publicKey: {\n challenge: randomBytes(32),\n rpId,\n userVerification,\n allowCredentials: [{ type: 'public-key', id: copy(credentialId) }]\n }\n })\n } catch (error) {\n return Promise.reject(toPasskeyError(error))\n }\n\n return pending.then(\n (credential) => {\n if (!(credential instanceof PublicKeyCredential)) {\n throw new PasskeyError('cancelled', 'The passkey prompt was dismissed.')\n }\n },\n (error) => {\n throw toPasskeyError(error)\n }\n )\n}\n","/**\n * Passkey Wallet Adapter for Algorand\n *\n * Derives a Falcon-1024 (post-quantum) account from a passkey's WebAuthn PRF\n * output. There is no extension, app or seed phrase to set up; the passkey\n * provider (iCloud Keychain, Google Password Manager, 1Password, Windows\n * Hello, a security key) syncs and protects the credential.\n *\n * SECURITY MODEL: the account is app-scoped.\n * - It is bound to the relying party ID (by default the page's hostname). Any\n * script running there can derive the key with one verified passkey prompt\n * that looks like a sign-in: the dApp itself, an XSS bug, a compromised\n * dependency, or whoever takes over the domain. Other sites can't derive it.\n * - The account is stranded if the domain is lost, so offer\n * `exportRecoveryPhrase` and a way to move to a wallet. See the README.\n * - Only public data is persisted: the credential ID, public key, address\n * salt and address. Never the PRF output, the entropy or the private key.\n * - The private key lives in a dedicated worker, never on the main thread.\n * Without `session`, the worker is terminated after every request.\n */\n\nimport algosdk from 'algosdk'\nimport { canonify } from 'canonify'\nimport {\n BaseWallet,\n ScopeType,\n SignDataError,\n encodeEmptySignature,\n flattenTxnGroup,\n isSignedTxn,\n isTransactionArray,\n type AdapterConstructorParams,\n type Siwa,\n type StdSignDataResponse,\n type StdSignMetadata,\n type WalletAccount,\n type WalletMetadata,\n type WalletState\n} from '@txnlab/use-wallet/adapter'\nimport { assertValidContext, prfInput } from './derivation'\nimport { PasskeyError } from './errors'\nimport { icon } from './icon'\nimport { WorkerKeyStore, type PasskeyKeyStore, type WorkerKeyStoreLockReason } from './key-store'\nimport { loadRecord, removeRecord, saveRecord, type PasskeyRecord } from './record'\nimport { assertUser, createPasskey, evaluatePrf, type PrfResult } from './webauthn'\n\nconst ICON = `data:image/svg+xml;base64,${btoa(icon)}`\n\nconst DEFAULT_IDLE_TIMEOUT_MS = 15 * 60 * 1000\nconst DEFAULT_MAX_AGE_MS = 60 * 60 * 1000\n\n/**\n * How to confirm a signing request while a session is unlocked:\n * - `silent`: sign without a prompt\n * - `presence`: a passkey prompt with `userVerification: 'preferred'`, which\n * some providers satisfy with a tap and no biometric or system password\n * - `verify`: a passkey prompt with `userVerification: 'required'`\n *\n * These prompts evaluate no PRF, so they never change the account. When the\n * session is locked, the PRF ceremony that unlocks it is the confirmation.\n */\nexport type PasskeyConfirmation = 'silent' | 'presence' | 'verify'\n\nexport type PasskeySigningRequest =\n { type: 'transactions'; transactions: algosdk.Transaction[] } | { type: 'data'; siwa: Siwa }\n\n/**\n * Why an unlocked session locked:\n * - `idle`, `max-age`: the built-in key store's timeouts\n * - `hidden`: the page was hidden, with `lockWhenHidden`\n * - `lock`: the app called `lock()`\n * - `disconnect`: the wallet disconnected\n * - `connect`: a new `connect` replaced or dropped the session\n * - `key-store`: the key store dropped the key itself, e.g. its worker failed\n */\nexport type PasskeyLockReason =\n WorkerKeyStoreLockReason | 'hidden' | 'lock' | 'disconnect' | 'connect'\n\nexport interface PasskeySessionOptions {\n /** Lock the session after this long without a signature; 0 never locks it. Default: 15 minutes */\n idleTimeoutMs?: number\n /** Lock the session this long after it unlocks, however active it is; 0 sets no limit. Default: 1 hour */\n maxAgeMs?: number\n /**\n * Lock the session when the page is hidden, e.g. the user switches tabs or\n * apps. A signing request in progress finishes first. Also applies to a\n * custom `keyStore`. Pass a function to follow a setting the user can\n * change; it's read each time the page is hidden. Default: `false`\n */\n lockWhenHidden?: boolean | (() => boolean)\n}\n\nexport interface PasskeyOptions {\n /**\n * The WebAuthn relying party ID: the domain the passkey (and so the account)\n * is bound to. Default: `location.hostname`. Set a parent domain (e.g.\n * `example.com` for `app.example.com`) to share one account across its\n * subdomains.\n */\n rpId?: string\n /** The relying party name shown in passkey prompts. Default: the RP ID */\n rpName?: string\n /**\n * Separates this app's account from others on the same RP ID. Up to 128\n * characters, no `:`. Default: `''`. Changing it changes the account.\n */\n context?: string\n /** The user name new passkeys are saved under. Default: `<rpName> account` */\n userName?: string\n /**\n * Keep the key unlocked between signatures, so only the first signature\n * (or `connect`) runs a PRF ceremony with full user verification; later ones\n * are confirmed per `confirm`. The key is held in a dedicated worker until\n * the idle timeout, the maximum age, `disconnect` or a page reload. Default:\n * `false`, a PRF ceremony for every request.\n */\n session?: boolean | PasskeySessionOptions\n /**\n * Chooses how to confirm each signing request while a session is unlocked.\n * Called synchronously, before the prompt: WebKit drops the user gesture\n * across an await, so this must not be async. Default: `() => 'presence'`.\n */\n confirm?: (request: PasskeySigningRequest) => PasskeyConfirmation\n /**\n * Called when an unlocked session locks, so the app can show it (e.g. an\n * \"Unlock\" button) without polling `isUnlocked`. Not called without a\n * session, where the key is only held during a request.\n */\n onLock?: (reason: PasskeyLockReason) => void\n /**\n * Called when a locked session unlocks: on `connect`, `unlock`, or a\n * signature that ran the PRF ceremony. Not called without a session.\n */\n onUnlock?: () => void\n /**\n * Replaces the built-in worker key store, e.g. to keep a session across page\n * reloads. The adapter treats a custom key store as a session: it never\n * locks it after signing, only on `disconnect`. May be called more than once.\n */\n keyStore?: () => PasskeyKeyStore\n}\n\nexport interface PasskeyConnectArgs {\n /** Create a new passkey (and account) instead of signing in with an existing one */\n create?: boolean\n /** The user name to save a new passkey under */\n userName?: string\n}\n\nexport interface PasskeyRecoveryPhrase {\n /** Import the phrase as a Falcon-1024 account, not a regular 25-word account */\n scheme: 'falcon-1024'\n /** The 25-word phrase */\n phrase: string\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, i) => byte === b[i])\n}\n\nfunction emptySignatureFor(record: PasskeyRecord): string {\n return encodeEmptySignature({\n pqsig: {\n sch: algosdk.FALCON_1024_SCHEME,\n slt: record.salt,\n pk: record.publicKey,\n sig: new Uint8Array()\n }\n })\n}\n\nasync function sha256(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await crypto.subtle.digest('SHA-256', new Uint8Array(data)))\n}\n\nexport class PasskeyAdapter extends BaseWallet<PasskeyOptions> {\n private keyStore: PasskeyKeyStore | null = null\n /** Signing requests in progress; hiding the page doesn't lock in the middle of one */\n private activeRequests = 0\n /** Whether the session was unlocked when `onLock`/`onUnlock` last checked */\n private reportedUnlocked = false\n\n constructor(params: AdapterConstructorParams<PasskeyOptions>) {\n super(params)\n assertValidContext(this.context)\n if (this.isSession && this.sessionOptions.lockWhenHidden && typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', this.onVisibilityChange)\n }\n }\n\n static defaultMetadata: WalletMetadata = {\n name: 'Passkey',\n icon: ICON\n }\n\n // ---------- Options ----------------------------------------------- //\n\n private get rpId(): string {\n return this.options.rpId ?? location.hostname\n }\n\n private get rpName(): string {\n return this.options.rpName ?? this.rpId\n }\n\n private get context(): string {\n return this.options.context ?? ''\n }\n\n /** True when the key stays unlocked between requests */\n private get isSession(): boolean {\n return !!this.options.session || !!this.options.keyStore\n }\n\n private get sessionOptions(): PasskeySessionOptions {\n const { session } = this.options\n return typeof session === 'object' ? session : {}\n }\n\n private get lockWhenHidden(): boolean {\n const { lockWhenHidden } = this.sessionOptions\n if (!this.isSession) return false\n if (typeof lockWhenHidden !== 'function') return !!lockWhenHidden\n try {\n return lockWhenHidden()\n } catch (error: any) {\n // Fail closed\n this.logger.error('Error in lockWhenHidden:', error.message)\n return true\n }\n }\n\n private createKeyStore(): PasskeyKeyStore {\n if (this.options.keyStore) return this.options.keyStore()\n\n const session = !!this.options.session\n const { idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS, maxAgeMs = DEFAULT_MAX_AGE_MS } =\n this.sessionOptions\n return new WorkerKeyStore({\n idleTimeoutMs: session ? idleTimeoutMs : 0,\n maxAgeMs: session ? maxAgeMs : 0,\n onLock: (reason) => {\n this.logger.info(`Session locked (${reason})`)\n this.reportSession(reason)\n }\n })\n }\n\n private getKeyStore(): PasskeyKeyStore {\n this.keyStore ??= this.createKeyStore()\n return this.keyStore\n }\n\n /** Drop the key after a request, unless a session keeps it */\n private release(): void {\n if (!this.isSession || this.isHidden()) this.keyStore?.lock()\n }\n\n /** True when the page is hidden and the session should lock for it */\n private isHidden(): boolean {\n return (\n typeof document !== 'undefined' &&\n document.visibilityState === 'hidden' &&\n this.lockWhenHidden\n )\n }\n\n private onVisibilityChange = (): void => {\n // A passkey prompt can hide the page; a request in progress locks when it ends\n if (this.activeRequests === 0 && this.isHidden()) {\n this.keyStore?.lock()\n this.reportSession('hidden')\n }\n }\n\n /** Finish a request: drop the key unless the session keeps it, and report the session state */\n private finishRequest(): void {\n this.activeRequests--\n this.release()\n // Any other lock during the request was reported where it happened\n this.reportSession(this.isHidden() ? 'hidden' : 'key-store')\n }\n\n /**\n * Call `onLock` or `onUnlock` once when the session changes state, with\n * `reason` for a lock. Checks the key store itself, so a change is reported\n * however it happened.\n */\n private reportSession(reason: PasskeyLockReason): void {\n const unlocked = this.isSession && this.isUnlocked\n if (unlocked === this.reportedUnlocked) return\n this.reportedUnlocked = unlocked\n try {\n if (unlocked) this.options.onUnlock?.()\n else this.options.onLock?.(reason)\n } catch (error: any) {\n this.logger.error(`Error in ${unlocked ? 'onUnlock' : 'onLock'}:`, error.message)\n }\n }\n\n // ---------- Connection -------------------------------------------- //\n\n /**\n * Sign in with an existing passkey, or create one with `{ create: true }`.\n * Show both choices: a missing passkey and a cancelled prompt raise the same\n * error, so the adapter can't tell which one happened.\n */\n public connect = async (args?: PasskeyConnectArgs): Promise<WalletAccount[]> => {\n const rpId = this.rpId\n const context = this.context\n const input = prfInput(context)\n this.logger.info(args?.create ? 'Creating passkey...' : 'Connecting...')\n\n let result: PrfResult | undefined\n try {\n // The first ceremony starts before any await (WebKit drops the user gesture)\n if (args?.create) {\n const credentialId = await createPasskey({\n rpId,\n rpName: this.rpName,\n userName: args.userName ?? this.options.userName ?? `${this.rpName} account`\n })\n // Scope to the new credential so the picker can't bind a sibling passkey\n result = await evaluatePrf({ rpId, prfInput: input, credentialId })\n } else {\n result = await evaluatePrf({ rpId, prfInput: input })\n }\n\n this.keyStore?.lock()\n const keyStore = this.getKeyStore()\n const publicKey = await keyStore.unlock(result.prf)\n this.release()\n\n const { address, salt } = algosdk.addressFromPQKey(algosdk.FALCON_1024_SCHEME, publicKey)\n const record: PasskeyRecord = {\n version: 1,\n rpId,\n context,\n credentialId: result.credentialId,\n publicKey,\n salt,\n address: address.toString()\n }\n saveRecord(record)\n\n const walletAccount: WalletAccount = {\n // Name the domain the account belongs to\n name: `${this.metadata.name} · ${rpId}`,\n address: record.address,\n emptySignature: emptySignatureFor(record)\n }\n const walletState: WalletState = {\n accounts: [walletAccount],\n activeAccount: walletAccount\n }\n this.store.addWallet(walletState)\n this.reportSession(this.isHidden() ? 'hidden' : 'connect')\n\n this.logger.info('Connected successfully', walletState)\n return [walletAccount]\n } catch (error: any) {\n result?.prf.fill(0)\n this.keyStore?.lock()\n this.reportSession('connect')\n this.logger.error('Error connecting:', error.message)\n throw error\n }\n }\n\n public disconnect = async (): Promise<void> => {\n this.logger.info('Disconnecting...')\n this.keyStore?.lock()\n this.reportSession('disconnect')\n this.keyStore = null\n removeRecord()\n this.onDisconnect()\n this.logger.info('Disconnected')\n }\n\n /** Restores the account without a ceremony; the key unlocks on the first signature */\n public resumeSession = async (): Promise<void> => {\n const walletState = this.store.getWalletState()\n if (!walletState) {\n this.logger.info('No session to resume')\n return\n }\n\n const record = loadRecord()\n if (!record || !this.matchesRecord(record, walletState)) {\n this.logger.info('No passkey record for this session, disconnecting...')\n await this.disconnect()\n return\n }\n this.logger.info('Session resumed')\n }\n\n private matchesRecord(record: PasskeyRecord, walletState: WalletState): boolean {\n return (\n record.rpId === this.rpId &&\n record.context === this.context &&\n walletState.accounts.length === 1 &&\n walletState.accounts[0].address === record.address\n )\n }\n\n private requireRecord(): PasskeyRecord {\n const walletState = this.store.getWalletState()\n const record = loadRecord()\n if (!walletState || !record || !this.matchesRecord(record, walletState)) {\n throw new PasskeyError('not-connected', 'No passkey account is connected.')\n }\n return record\n }\n\n // ---------- Session ----------------------------------------------- //\n\n /** True when the key is unlocked, so the next signature needs no PRF ceremony */\n public get isUnlocked(): boolean {\n return this.keyStore?.unlocked ?? false\n }\n\n /**\n * Unlocks the session ahead of the next signature with one PRF ceremony\n * (full user verification), e.g. from an \"Unlock\" button after a page\n * reload, so signing needs no ceremony. Call it directly from the click\n * handler: iOS drops the tap across an await. Does nothing if the session is\n * already unlocked, or without `session`, where every request runs its own\n * ceremony.\n */\n public unlock = async (): Promise<void> => {\n const record = this.requireRecord()\n if (!this.isSession || this.isUnlocked) return\n\n this.logger.info('Unlocking...')\n this.activeRequests++\n try {\n await this.unlockKey(record)\n this.logger.info('Unlocked')\n } catch (error: any) {\n this.logger.error('Error unlocking:', error.message)\n throw error\n } finally {\n this.finishRequest()\n }\n }\n\n /**\n * Drops the key. The account stays connected; the next signature (or\n * `unlock`) runs a PRF ceremony.\n */\n public lock = (): void => {\n this.keyStore?.lock()\n this.reportSession('lock')\n }\n\n // ---------- Authorization ----------------------------------------- //\n\n /**\n * Unlock the key with a PRF ceremony for the connected credential, and check\n * it derives the connected account. Starts its prompt before any await.\n */\n private async unlockKey(record: PasskeyRecord): Promise<void> {\n const keyStore = this.getKeyStore()\n const { prf } = await evaluatePrf({\n rpId: record.rpId,\n prfInput: prfInput(record.context),\n credentialId: record.credentialId\n })\n const publicKey = await keyStore.unlock(prf)\n if (!bytesEqual(publicKey, record.publicKey)) {\n keyStore.lock()\n throw new PasskeyError(\n 'key-mismatch',\n 'This passkey derived a different key than the connected account. Disconnect and connect again.'\n )\n }\n }\n\n /**\n * Make sure the key is unlocked and the user approved this request. When the\n * key is locked, the PRF ceremony that unlocks it is the approval. Starts its\n * prompt before any await.\n */\n private async authorize(request: PasskeySigningRequest, record: PasskeyRecord): Promise<void> {\n const keyStore = this.getKeyStore()\n if (!keyStore.unlocked) return this.unlockKey(record)\n\n const confirmation = this.options.confirm?.(request) ?? 'presence'\n if (confirmation === 'silent') return\n await assertUser({\n rpId: record.rpId,\n credentialId: record.credentialId,\n // Anything but an explicit 'presence' gets full verification\n userVerification: confirmation === 'presence' ? 'preferred' : 'required'\n })\n }\n\n // ---------- Signing ----------------------------------------------- //\n\n private processTxns(\n txnGroup: algosdk.Transaction[],\n indexesToSign: number[] | undefined,\n address: string\n ): algosdk.Transaction[] {\n return txnGroup.filter(\n (txn, index) =>\n (!indexesToSign || indexesToSign.includes(index)) && txn.sender.toString() === address\n )\n }\n\n private processEncodedTxns(\n txnGroup: Uint8Array[],\n indexesToSign: number[] | undefined,\n address: string\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txnBuffer, index) => {\n const isSigned = isSignedTxn(algosdk.msgpackRawDecode(txnBuffer))\n const txn: algosdk.Transaction = isSigned\n ? algosdk.decodeSignedTransaction(txnBuffer).txn\n : algosdk.decodeUnsignedTransaction(txnBuffer)\n\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n if (isIndexMatch && !isSigned && txn.sender.toString() === address) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n /**\n * Signs the transactions sent from the connected account. Each one carries a\n * Falcon-1024 `pqsig` (about 3.2 KB) and pays an extra 2× the minimum fee;\n * get fees by simulating with `emptyTransactionSigner`.\n */\n public signTransactions = async <T extends algosdk.Transaction[] | Uint8Array[]>(\n txnGroup: T | T[],\n indexesToSign?: number[]\n ): Promise<(Uint8Array | null)[]> => {\n const record = this.requireRecord()\n\n const txnsToSign = isTransactionArray(txnGroup)\n ? this.processTxns(flattenTxnGroup(txnGroup), indexesToSign, record.address)\n : this.processEncodedTxns(\n flattenTxnGroup(txnGroup as Uint8Array[]),\n indexesToSign,\n record.address\n )\n if (txnsToSign.length === 0) return []\n\n this.activeRequests++\n try {\n this.logger.debug('Signing transactions...', { txnsToSign })\n await this.authorize({ type: 'transactions', transactions: txnsToSign }, record)\n\n const keyStore = this.getKeyStore()\n const { txnSigner } = algosdk.addressWithSignersFromRawFalcon1024Signer({\n falcon1024PublicKey: record.publicKey,\n falcon1024Signer: (bytes) => keyStore.sign(bytes)\n })\n const signedTxns = await txnSigner(\n txnsToSign,\n txnsToSign.map((_, index) => index)\n )\n this.logger.debug('Transactions signed successfully')\n return signedTxns\n } catch (error: any) {\n this.logger.error('Error signing transactions:', error.message)\n throw error\n } finally {\n this.finishRequest()\n }\n }\n\n // ---------- signData (ARC-60) ------------------------------------- //\n\n public canSignData = true\n\n /**\n * Signs an ARC-60 authentication request (SIWA) with the account's Falcon\n * key, scheme `falcon-1024-det`. The signature covers\n * `SHA-256(data) || SHA-256(authenticatorData)`.\n */\n public signData = async (\n data: string,\n metadata: StdSignMetadata\n ): Promise<StdSignDataResponse> => {\n const record = this.requireRecord()\n\n // Validate before prompting, without awaiting\n if (metadata.encoding !== 'base64') {\n throw new SignDataError('Failed decoding', 4602)\n }\n if (metadata.scope !== ScopeType.AUTH) {\n throw new SignDataError('Invalid Scope', 4600)\n }\n // Sign exactly the bytes the dApp sent: strict UTF-8 with no BOM, so the\n // decoded JSON can't differ from them\n let bytes: Uint8Array\n let json: string\n let siwa: Siwa\n try {\n bytes = algosdk.base64ToBytes(data)\n json = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes)\n siwa = JSON.parse(json)\n } catch {\n throw new SignDataError('Bad JSON', 4609)\n }\n if (!siwa || typeof siwa !== 'object' || canonify(siwa) !== json) {\n throw new SignDataError('Bad JSON', 4609)\n }\n if (siwa.domain !== location.host) {\n throw new SignDataError('Failed Domain Auth', 4610)\n }\n if (siwa.type !== 'falcon-1024-det' || siwa.account_address !== record.address) {\n throw new SignDataError('Invalid Signer', 4603)\n }\n\n this.activeRequests++\n try {\n await this.authorize({ type: 'data', siwa }, record)\n\n const stdSignData = await this.createStdSignData(data)\n if (!bytesEqual(stdSignData.signer, record.publicKey)) {\n throw new SignDataError('Invalid Signer', 4603)\n }\n\n const toSign = new Uint8Array([\n ...(await sha256(bytes)),\n ...(await sha256(stdSignData.authenticatorData))\n ])\n const signature = await this.getKeyStore().sign(toSign)\n return { ...stdSignData, signature }\n } catch (error: any) {\n this.logger.error('Error signing data:', error.message)\n throw error\n } finally {\n this.finishRequest()\n }\n }\n\n // ---------- Recovery ---------------------------------------------- //\n\n /**\n * Returns the account's 25-word recovery phrase after a fresh passkey\n * ceremony with full user verification. Show it with a warning that it must\n * be imported as a Falcon / quantum account (Lute: \"Falcon25 account\";\n * Pera: quantum import; `algokey pq import --scheme falcon-1024`): the same\n * words in a regular 25-word import restore a different, empty account.\n */\n public exportRecoveryPhrase = async (): Promise<PasskeyRecoveryPhrase> => {\n const record = this.requireRecord()\n\n const { prf } = await evaluatePrf({\n rpId: record.rpId,\n prfInput: prfInput(record.context),\n credentialId: record.credentialId\n })\n\n const check = this.createKeyStore()\n try {\n const publicKey = await check.unlock(new Uint8Array(prf))\n if (!bytesEqual(publicKey, record.publicKey)) {\n throw new PasskeyError(\n 'key-mismatch',\n 'This passkey derived a different key than the connected account.'\n )\n }\n return { scheme: 'falcon-1024', phrase: algosdk.mnemonicFromSeed(prf) }\n } finally {\n check.lock()\n prf.fill(0)\n }\n }\n}\n","import { PasskeyAdapter } from './adapter'\nimport type {\n PasskeyConfirmation,\n PasskeyConnectArgs,\n PasskeyLockReason,\n PasskeyOptions,\n PasskeyRecoveryPhrase,\n PasskeySessionOptions,\n PasskeySigningRequest\n} from './adapter'\nimport { assertValidContext } from './derivation'\nimport type { WalletAdapterConfig, WalletFactoryOptions } from '@txnlab/use-wallet'\n\nexport const WALLET_ID = 'passkey' as const\n\nexport function passkey(options?: PasskeyOptions & WalletFactoryOptions): WalletAdapterConfig {\n const { metadata, ...adapterOptions } = options ?? {}\n assertValidContext(adapterOptions.context ?? '')\n return {\n id: WALLET_ID,\n metadata: { ...PasskeyAdapter.defaultMetadata, ...metadata },\n Adapter: PasskeyAdapter as unknown as WalletAdapterConfig['Adapter'],\n options:\n Object.keys(adapterOptions).length > 0\n ? (adapterOptions as unknown as Record<string, unknown>)\n : undefined\n }\n}\n\nexport { PasskeyAdapter }\nexport { PasskeyError, type PasskeyErrorCode } from './errors'\nexport {\n WorkerKeyStore,\n type PasskeyKeyStore,\n type WorkerKeyStoreLockReason,\n type WorkerKeyStoreOptions\n} from './key-store'\nexport type { PasskeyTimeoutReason } from './worker/protocol'\nexport { PRF_LABEL_PREFIX, keygenSeedFromEntropy, prfInput } from './derivation'\nexport { LOCAL_STORAGE_PASSKEY_KEY } from './record'\nexport type {\n PasskeyConfirmation,\n PasskeyConnectArgs,\n PasskeyLockReason,\n PasskeyOptions,\n PasskeyRecoveryPhrase,\n PasskeySessionOptions,\n PasskeySigningRequest\n}\n"],"mappings":"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"}
|