diff --git a/packages/entrykit/src/passkey/createPasskey.ts b/packages/entrykit/src/passkey/createPasskey.ts index 4ce7179365..a167681f2a 100644 --- a/packages/entrykit/src/passkey/createPasskey.ts +++ b/packages/entrykit/src/passkey/createPasskey.ts @@ -1,23 +1,21 @@ import { cache } from "./cache"; -import { createBridge, PasskeyCredential } from "@latticexyz/id/internal"; -import { MUDChain } from "@latticexyz/common/chains"; +import { PasskeyCredential } from "@latticexyz/id/internal"; +import { createCredential } from "webauthn-p256"; -export async function createPasskey(chain: MUDChain): Promise { - const bridge = await createBridge({ url: chain.mudIdUrl, message: "Creating account…" }); - try { - const credential = await bridge.request("create"); - console.log("created passkey", credential); +export async function createPasskey(): Promise { + const credential = await createCredential({ name: "MUD Account" }); + console.log("created passkey", credential); - cache.setState((state) => ({ - activeCredential: credential.credentialId, - publicKeys: { - ...state.publicKeys, - [credential.credentialId]: credential.publicKey, - }, - })); + cache.setState((state) => ({ + activeCredential: credential.id, + publicKeys: { + ...state.publicKeys, + [credential.id]: credential.publicKey, + }, + })); - return credential; - } finally { - bridge.close(); - } + return { + credentialId: credential.id, + publicKey: credential.publicKey, + }; } diff --git a/packages/entrykit/src/passkey/getAccount.ts b/packages/entrykit/src/passkey/getAccount.ts index 7ee1758900..2976b96358 100644 --- a/packages/entrykit/src/passkey/getAccount.ts +++ b/packages/entrykit/src/passkey/getAccount.ts @@ -2,8 +2,7 @@ import { Chain, Client, Transport } from "viem"; import { cache } from "./cache"; import { P256Credential } from "webauthn-p256"; import { toCoinbaseSmartAccount, ToCoinbaseSmartAccountReturnType } from "@latticexyz/common/accounts"; -import { toWebAuthnAccount } from "./toWebAuthnAccount"; -import { MUDChain } from "@latticexyz/common/chains"; +import { toWebAuthnAccount } from "viem/account-abstraction"; export async function getAccount( client: Client, @@ -17,7 +16,7 @@ export async function getAccount( throw new Error("No public key found for passkey credential."); } - const passkey = toWebAuthnAccount({ credential: { id, publicKey }, bridgeUrl: (client.chain as MUDChain).mudIdUrl }); + const passkey = toWebAuthnAccount({ credential: { id, publicKey } }); const owners = [passkey]; return await toCoinbaseSmartAccount({ client, owners }); diff --git a/packages/entrykit/src/passkey/passkeyConnector.ts b/packages/entrykit/src/passkey/passkeyConnector.ts index f4515b1423..6e65b07af5 100644 --- a/packages/entrykit/src/passkey/passkeyConnector.ts +++ b/packages/entrykit/src/passkey/passkeyConnector.ts @@ -78,13 +78,13 @@ export function passkeyConnector({ chainId }: PasskeyConnectorOptions): CreatePa // supportsSimulation: true, async createPasskey() { - const { credentialId } = await createPasskey(chain); + const { credentialId } = await createPasskey(); const account = await getAccount(client, credentialId); this.onAccountsChanged([account.address]); this.onConnect?.({ chainId: numberToHex(chainId) }); }, async reusePasskey() { - const { credentialId } = await reusePasskey(chain); + const { credentialId } = await reusePasskey(); const account = await getAccount(client, credentialId); this.onAccountsChanged([account.address]); this.onConnect?.({ chainId: numberToHex(chainId) }); @@ -103,7 +103,7 @@ export function passkeyConnector({ chainId }: PasskeyConnectorOptions): CreatePa // attempt to reuse credential if this is called directly // TODO: move this into wallet so it's only triggered via rainbowkit? if (!cache.getState().activeCredential && !params?.isReconnecting) { - await reusePasskey(chain); + await reusePasskey(); } const accounts = await this.getAccounts(); diff --git a/packages/entrykit/src/passkey/reusePasskey.ts b/packages/entrykit/src/passkey/reusePasskey.ts index 9806dbe059..ef14a17291 100644 --- a/packages/entrykit/src/passkey/reusePasskey.ts +++ b/packages/entrykit/src/passkey/reusePasskey.ts @@ -2,54 +2,53 @@ import { bytesToHex, hashMessage } from "viem"; import { cache } from "./cache"; import { getMessageHash } from "./getMessageHash"; import { findPublicKey } from "./findPublicKey"; -import { PasskeyCredential, createBridge } from "@latticexyz/id/internal"; -import { MUDChain } from "@latticexyz/common/chains"; - -export async function reusePasskey(chain: MUDChain): Promise { - const bridge = await createBridge({ url: chain.mudIdUrl, message: "Signing in…" }); - try { - const challenge = hashMessage(bytesToHex(crypto.getRandomValues(new Uint8Array(256)))); - const { credentialId, signature, metadata } = await bridge.request("sign", { challenge }); - - const publicKey = await (async () => { - const cachedPublicKey = cache.getState().publicKeys[credentialId]; - if (cachedPublicKey) return cachedPublicKey; - - // TODO: look up account/public key by credential ID once we store it onchain - - const messageHash = await getMessageHash(metadata); - const challenge2 = hashMessage(signature); - const signature2 = await bridge.request("sign", { credentialId, challenge: challenge2 }); - if (signature2.credentialId !== credentialId) { - throw new Error("wrong credential"); - } - - const publicKey = findPublicKey([ - { messageHash, signatureHex: signature }, - { messageHash: await getMessageHash(signature2.metadata), signatureHex: signature2.signature }, - ]); - if (!publicKey) { - throw new Error("recovery failed"); - } - - cache.setState((state) => ({ - publicKeys: { - ...state.publicKeys, - [credentialId]: publicKey, - }, - })); - - return publicKey; - })(); - - console.log("recovered passkey", credentialId, publicKey); - - cache.setState(() => ({ - activeCredential: credentialId, +import { PasskeyCredential } from "@latticexyz/id/internal"; +import { sign } from "webauthn-p256"; + +export async function reusePasskey(): Promise { + const challenge = hashMessage(bytesToHex(crypto.getRandomValues(new Uint8Array(256)))); + const { + raw: { id: credentialId }, + signature, + webauthn: metadata, + } = await sign({ hash: challenge }); + + const publicKey = await (async () => { + const cachedPublicKey = cache.getState().publicKeys[credentialId]; + if (cachedPublicKey) return cachedPublicKey; + + // TODO: look up account/public key by credential ID once we store it onchain + + const messageHash = await getMessageHash(metadata); + const challenge2 = hashMessage(signature); + const signature2 = await sign({ credentialId, hash: challenge2 }); + if (signature2.raw.id !== credentialId) { + throw new Error("wrong credential"); + } + + const publicKey = findPublicKey([ + { messageHash, signatureHex: signature }, + { messageHash: await getMessageHash(signature2.webauthn), signatureHex: signature2.signature }, + ]); + if (!publicKey) { + throw new Error("recovery failed"); + } + + cache.setState((state) => ({ + publicKeys: { + ...state.publicKeys, + [credentialId]: publicKey, + }, })); - return { credentialId, publicKey }; - } finally { - bridge.close(); - } + return publicKey; + })(); + + console.log("recovered passkey", credentialId, publicKey); + + cache.setState(() => ({ + activeCredential: credentialId, + })); + + return { credentialId, publicKey }; } diff --git a/packages/entrykit/src/passkey/toWebAuthnAccount.ts b/packages/entrykit/src/passkey/toWebAuthnAccount.ts deleted file mode 100644 index 13f223ce1b..0000000000 --- a/packages/entrykit/src/passkey/toWebAuthnAccount.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ErrorType } from "@ark/util"; -import { hashMessage, hashTypedData } from "viem"; -import { WebAuthnAccount } from "viem/account-abstraction"; -import { type P256Credential } from "webauthn-p256"; -import { createBridge } from "@latticexyz/id/internal"; - -export type ToWebAuthnAccountParameters = { - /** - * The WebAuthn P256 credential to use. - */ - credential: { - id: P256Credential["id"]; - publicKey: P256Credential["publicKey"]; - }; - bridgeUrl?: string; -}; - -export type ToWebAuthnAccountReturnType = WebAuthnAccount; - -export type ToWebAuthnAccountErrorType = ErrorType; - -/** - * @description Creates an Account from a WebAuthn Credential. - * - * @returns A WebAuthn Account. - */ -export function toWebAuthnAccount(parameters: ToWebAuthnAccountParameters): WebAuthnAccount { - const { id, publicKey } = parameters.credential; - return { - publicKey, - async sign({ hash }) { - const bridge = await createBridge({ message: "Requesting signature…", url: parameters.bridgeUrl }); - try { - const { signature, metadata: webauthn } = await bridge.request("sign", { credentialId: id, challenge: hash }); - return { - signature, - webauthn, - raw: { id } as never, - }; - } finally { - bridge.close(); - } - }, - async signMessage({ message }) { - return this.sign({ hash: hashMessage(message) }); - }, - async signTypedData(parameters) { - return this.sign({ hash: hashTypedData(parameters) }); - }, - type: "webAuthn", - }; -} diff --git a/packages/id/src/rpc/onPostMessage.ts b/packages/id/src/rpc/onPostMessage.ts index 580fd520f0..f86269284c 100644 --- a/packages/id/src/rpc/onPostMessage.ts +++ b/packages/id/src/rpc/onPostMessage.ts @@ -41,8 +41,9 @@ async function onPortMessage(event: MessageEvent) { reply(port, { id: event.data.id, method: event.data.method, + // TODO: send the whole error object back, should be fine error: { - message: String(error), + message: error instanceof Error ? `${error.message}\n\n${error.stack}` : String(error), }, }); } @@ -60,8 +61,9 @@ async function onPortMessage(event: MessageEvent) { reply(port, { id: event.data.id, method: event.data.method, + // TODO: send the whole error object back, should be fine error: { - message: String(error), + message: error instanceof Error ? `${error.message}\n\n${error.stack}` : String(error), }, }); }