# [NEW] On-chain Passkeys

> How to use onchain passkeys to authenticate users and send transactions

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

<Warning>**This feature is in early access.**</Warning>

The WebAuthn Modular Account enables password-less authentication onchain using **passkeys** (via WebAuthn), and is compatible with [Wallet APIs](/docs/wallets). This guide demonstrates how to register credentials, authenticate users, and send transactions using the `@account-kit/smart-contracts` package.

Instead of on-device verification, this uses onchain verification. You are responsible for generating the credentials attached to those biometric WebAuthn-compatible passkeys and then using the authentication provider.

## Prerequisites

* A frontend environment (React, Vite, Next.js, etc.)
* Browser with WebAuthn and Credential Management API support
* Install the Wallet APIs SDK smart contracts package (use the latest version - **at least 4.52.1**) and Viem

<CodeBlocks>
  ```bash yarn
    yarn add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
  ```

  ```bash npm
    npm add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
  ```

  ```bash pnpm
    pnpm add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
  ```
</CodeBlocks>

## Example Workflow

1. User registers a WebAuthn credential
2. Credential ID and public key are stored locally
3. On login, `navigator.credentials.get()` fetches the credential
4. The app creates a client using the credential
5. A transaction is signed and sent

### Register WebAuthn credential

<Warning>
  **You are responsible for retaining your users’ public keys**. Public keys
  generated by the WebAuthn specification are only retrievable **once** on the
  initial creation of the credential. As a precaution, strongly consider
  adding a secondary offchain owner to use for account recovery
</Warning>

```tsx twoslash
import { createWebAuthnCredential } from "viem/account-abstraction";

const credential = await createWebAuthnCredential({
  name: "Credential Name",
});

// store credential id to public key mapping
// NOTE: use of localStorage is for TESTING PURPOSES ONLY, NOT FOR PRODUCTION USE
localStorage.setItem(credential.id, credential.publicKey); //credentialIdAsBase64Encoded -> publicKeyHex
```

### Login with credential

```tsx twoslash
import { createModularAccountV2Client } from "@account-kit/smart-contracts";
import { alchemy, arbitrumSepolia } from "@account-kit/infra";

const publicKeyRequest: PublicKeyCredentialRequestOptions = {
  challenge: Uint8Array.fromHex("0x"), // Generate a random challenge
  rpId: "localhost", // should match your dApp domain
};

// retrieve available passkeys for the provided domain
const publicKeyCredential = await navigator.credentials.get({
  publicKeyRequest,
});

if (publicKeyCredential) {
  // verify that passkey with corresponding id exists on dapp
  const publicKeyHex = localStorage.getItem(publicKeyCredential.id);
  if (!publicKeyHex) throw new Error("Account does not exist");

  // create client to send transactions on behalf of verified user
  const accountClient = await createModularAccountV2Client({
    policyId: "YOUR_POLICY_ID",
    mode: "webauthn",
    credential: {
      id: publicKeyCredential.id,
      publicKey: publicKeyHex,
    },
    rpId: "localhost",
    chain: arbitrumSepolia,
    transport: alchemy({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
  });
}
```

<Accordion title="`createModularAccountV2Client` Parameters">
  | Parameter    | Type         | Description                                         |
  | ------------ | ------------ | --------------------------------------------------- |
  | `policyId`   | `string`     | Your account policy UUID from the Alchemy dashboard |
  | `mode`       | `"webauthn"` | Specifies credential mode                           |
  | `credential` | `object`     | `{ id: string, publicKey: Address }`                |
  | `rpId`       | `string`     | Relying Party ID (e.g., `localhost` or your domain) |
  | `chain`      | `Chain`      | Network config (e.g., `arbitrumSepolia`)            |
  | `transport`  | `Transport`  | Alchemy or custom RPC transport                     |
</Accordion>

### Send a transaction

```tsx
const operation = await accountClient.sendUserOperation({
  uo: {
    target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // Example: Vitalik's address
    data: "0x", // No calldata
    value: parseEther("0"),
  },
});
```

## React Native integration

Get your React Native environment set up by following these [docs](/docs/wallets/react-native/overview). Once you’ve completed this setup, you can use the WebAuthn authentication as detailed above!

<Warning>
  `localStorage` is not available in React Native. Please use an alternative
  storage method.{" "}
</Warning>

## What's next

This is the initial SDK release for onchain passkeys. The developer experience is being actively improved, so your feedback is greatly appreciated. If you have any questions or are interested in learning more, please reach out!