> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chipipay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Use Passkeys as a PIN

> Secure wallet authentication with biometric passkeys instead of PINs

## Overview

WebAuthn passkeys replace PINs with biometric authentication (Face ID, Touch ID, Windows Hello). No PINs to remember or store.

## Prerequisites

* Next.js 13+ with App Router
* Modern browser with WebAuthn support
* Biometric hardware (fingerprint, face recognition)
* Chipi Next.js SDK installed

## Installation

```bash theme={null}
npm install @chipi-stack/chipi-passkey
```

## Basic Implementation

<Steps>
  <Step title="Create wallet with passkey">
    <CodeGroup>
      ```typescript Import theme={null}
      "use client";
      import { useCreateWallet, Chain } from "@chipi-stack/nextjs";
      import { usePasskeySetup } from "@chipi-stack/chipi-passkey/hooks";
      ```

      ```typescript Usage theme={null}
      <Snippet file="sdk/use-passkeys-create.mdx" />
      ```
    </CodeGroup>
  </Step>

  <Step title="Authenticate with passkey">
    <CodeGroup>
      ```typescript Import theme={null}
      "use client";
      import { useTransfer, ChainToken } from "@chipi-stack/nextjs";
      import { usePasskeyAuth } from "@chipi-stack/chipi-passkey/hooks";
      ```

      ```typescript Usage theme={null}
      <Snippet file="sdk/use-passkeys-transfer.mdx" />
      ```
    </CodeGroup>
  </Step>

  <Step title="Migrate existing wallet (optional)">
    If users already have a PIN-based wallet, they can migrate to passkey:

    <CodeGroup>
      ```typescript Import theme={null}
      "use client";
      import { useMigrateWalletToPasskey } from "@chipi-stack/nextjs";
      ```

      ```typescript Usage theme={null}
      <Snippet file="sdk/use-passkeys-migrate.mdx" />
      ```
    </CodeGroup>
  </Step>
</Steps>

## Example

<Tabs>
  <Tab title="Create Wallet">
    ```typescript theme={null}
    "use client";

    import { useCreateWallet } from '@chipi-stack/nextjs';
    import { usePasskeySetup } from '@chipi-stack/chipi-passkey/hooks';
    import { useState } from 'react';

    export function CreateWalletWithPasskey({ userId }: { userId: string }) {
      const { createWalletAsync, isLoading, error } = useCreateWallet();
      const { setupPasskey } = usePasskeySetup();
      const [wallet, setWallet] = useState(null);

      const handleCreate = async () => {
        try {
          // Triggers biometric prompt — requires userId and display name
          const { encryptKey, credentialId } = await setupPasskey(userId, userId);

          // Create wallet with passkey-derived key
          // Do NOT pass usePasskey: true — setupPasskey already handled it
          const result = await createWalletAsync({
            params: {
              encryptKey,
              externalUserId: userId,
              chain: Chain.STARKNET,
            },
            bearerToken: 'your-jwt-token',
          });

          // Result is flat — publicKey is at top level, not nested under .wallet
          localStorage.setItem('wallet', JSON.stringify(result));
          localStorage.setItem('credentialId', credentialId);
          setWallet(result);
        } catch (err) {
          console.error(err);
        }
      };

      return (
        <div className="p-6 bg-white rounded-lg shadow">
          <button
            onClick={handleCreate}
            disabled={isLoading}
            className="px-4 py-2 bg-green-600 text-white rounded disabled:bg-gray-400"
          >
            {isLoading ? 'Creating...' : 'Create Wallet with Passkey'}
          </button>
          {error && <p className="text-red-600 mt-2">Error: {error.message}</p>}
          {wallet && (
            <p className="text-sm mt-4 font-mono">
              Wallet created: {wallet.publicKey}
            </p>
          )}
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="Transfer with Passkey">
    ```typescript theme={null}
    "use client";

    import { useTransfer } from '@chipi-stack/nextjs';
    import { usePasskeyAuth } from '@chipi-stack/chipi-passkey/hooks';
    import { useState } from 'react';

    export function TransferWithPasskey() {
      const { transferAsync, isLoading } = useTransfer();
      const { authenticate } = usePasskeyAuth();
      const [recipient, setRecipient] = useState('');
      const [amount, setAmount] = useState('');

      const handleTransfer = async () => {
        try {
          // Triggers biometric prompt — returns encryptKey or null if cancelled
          const encryptKey = await authenticate();
          if (!encryptKey) return;

          const wallet = JSON.parse(localStorage.getItem('wallet')!);

          // Transfer with passkey-derived key
          // Do NOT pass usePasskey: true — authenticate() already handled it
          const txHash = await transferAsync({
            params: {
              encryptKey,
              wallet,
              token: ChainToken.USDC,
              recipient,
              amount: Number(amount),
            },
            bearerToken: 'your-jwt-token',
          });

          alert(`Transfer complete: ${txHash}`);
        } catch (err) {
          console.error(err);
        }
      };

      return (
        <div className="space-y-4 p-6 bg-white rounded-lg shadow">
          <input 
            placeholder="Recipient address" 
            value={recipient}
            onChange={(e) => setRecipient(e.target.value)}
            className="w-full p-2 border rounded"
          />
          <input 
            placeholder="Amount" 
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
            type="number"
            className="w-full p-2 border rounded"
          />
          <button 
            onClick={handleTransfer} 
            disabled={isLoading}
            className="w-full px-4 py-2 bg-green-600 text-white rounded disabled:bg-gray-400"
          >
            {isLoading ? 'Sending...' : 'Send with Passkey'}
          </button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="Migrate to Passkey">
    ```typescript theme={null}
    "use client";

    import { useMigrateWalletToPasskey } from '@chipi-stack/nextjs';
    import { useState } from 'react';

    export function MigrateToPasskey({ userId }: { userId: string }) {
      const { migrateWalletToPasskeyAsync, isLoading } = useMigrateWalletToPasskey();
      const [currentPin, setCurrentPin] = useState('');

      const handleMigrate = async () => {
        try {
          const wallet = JSON.parse(localStorage.getItem('wallet')!);

          // Validates PIN first, then triggers biometric prompt internally
          const result = await migrateWalletToPasskeyAsync({
            wallet,
            oldEncryptKey: currentPin,
            externalUserId: userId,
            bearerToken: 'your-jwt-token',
          });

          // Store updated wallet and credentialId
          localStorage.setItem('wallet', JSON.stringify(result.wallet));
          localStorage.setItem('credentialId', result.credentialId);
          alert('Successfully migrated to passkey!');
        } catch (err) {
          console.error(err);
        }
      };

      return (
        <div className="space-y-4 p-6 bg-white rounded-lg shadow">
          <input
            type="password"
            placeholder="Current PIN"
            value={currentPin}
            onChange={(e) => setCurrentPin(e.target.value)}
            className="w-full p-2 border rounded"
          />
          <button 
            onClick={handleMigrate} 
            disabled={isLoading}
            className="w-full px-4 py-2 bg-green-600 text-white rounded disabled:bg-gray-400"
          >
            {isLoading ? 'Migrating...' : 'Migrate to Passkey'}
          </button>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

## Hooks Reference

| Hook                        | Package                            | Purpose                            |
| --------------------------- | ---------------------------------- | ---------------------------------- |
| `usePasskeySetup`           | `@chipi-stack/chipi-passkey/hooks` | Create new passkey                 |
| `usePasskeyAuth`            | `@chipi-stack/chipi-passkey/hooks` | Authenticate with existing passkey |
| `usePasskeyStatus`          | `@chipi-stack/chipi-passkey/hooks` | Check passkey support & status     |
| `useMigrateWalletToPasskey` | `@chipi-stack/nextjs`              | Migrate from PIN to passkey        |

## Browser Support

* ✅ Chrome 67+ (Desktop & Android)
* ✅ Safari 14+ (iOS 14+, macOS)
* ✅ Firefox 60+ (Desktop)
* ✅ Edge 18+ (Desktop)

<Note>
  Passkeys are stored in the browser and synced via platform (iCloud Keychain, Google Password Manager).
</Note>

## Security Benefits

* **No PINs stored** - Keys derived on-demand from biometric
* **Platform-level security** - Hardware-backed authentication
* **Phishing resistant** - Domain-bound credentials
* **User convenience** - One tap authentication

## Next Steps

* Check out [useCreateWallet](/sdk/nextjs/hooks/use-create-wallet) for wallet creation options
* Learn about [useTransfer](/sdk/nextjs/hooks/use-transfer) for token transfers
* Explore [session keys](/sdk/nextjs/hooks/use-create-session-key) for additional security
