Skip to main content

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.

Overview

The Passkey SDK lets you create self-custodial wallets protected by biometrics (Face ID, Touch ID, Windows Hello). No passwords, no seed phrases — just a biometric prompt. Dual-key architecture: Every passkey wallet also has a mandatory PIN backup. If biometrics fail (browser change, device reset), the PIN recovers the wallet.
Passkey wallet:
  1. Biometric → passkey-derived key → encrypts private key (primary)
  2. PIN → encrypts same private key (backup)
  3. Both stored in backend. Either can decrypt.

Installation

npm install @chipi-stack/chipi-passkey@latest

Core Functions

createWalletPasskey

Creates a new passkey credential via the browser’s WebAuthn API and derives an encryption key.
import { createWalletPasskey } from "@chipi-stack/chipi-passkey";

const result = await createWalletPasskey("user-123", "Alice");
// result: {
//   encryptKey: "a3f8c2...",     // hex string — use to encrypt private key
//   credentialId: "abc123...",    // stored locally for future auth
//   prfSupported: true            // whether PRF extension worked
// }
What happens:
  1. Browser shows biometric prompt (Face ID / Touch ID / Windows Hello)
  2. WebAuthn PRF extension derives a deterministic encryption key
  3. Credential metadata stored in localStorage for future auth
  4. Returns the encryption key + credential ID
createWalletPasskey triggers a browser modal. Call it only in response to a user action (button click), not on page load.

getWalletEncryptKey

Authenticates with an existing passkey to retrieve the encryption key (for signing transactions).
import { getWalletEncryptKey } from "@chipi-stack/chipi-passkey";

const encryptKey = await getWalletEncryptKey();
// encryptKey: "a3f8c2..." (same key as creation)
// Returns null if user cancels the biometric prompt
Pass a specific credential ID if the user has multiple passkeys:
const encryptKey = await getWalletEncryptKey("specific-credential-id");

Status Checks

import {
  isWebAuthnSupported,
  hasWalletPasskey,
  verifyWalletPasskey,
  verifyWalletPasskeyDetailed,
} from "@chipi-stack/chipi-passkey";

// Check browser support
isWebAuthnSupported(); // true/false

// Check if a passkey is stored locally
hasWalletPasskey(); // true/false

// Verify the stored passkey still works
const valid = await verifyWalletPasskey(); // true/false

// Detailed verification (for error handling)
const result = await verifyWalletPasskeyDetailed();
// { valid: true }
// { valid: false, reason: "no_passkey", message: "No passkey found" }
// { valid: false, reason: "prf_unavailable", message: "Passkey PRF unavailable..." }
// { valid: false, reason: "error", message: "..." }

Credential Management

import {
  getStoredCredential,
  removeStoredCredential,
} from "@chipi-stack/chipi-passkey";

// Get stored credential metadata
const cred = getStoredCredential();
// { credentialId, createdAt, userId, transports, prfSupported }

// Remove stored credential (logout / reset)
removeStoredCredential();

React Hooks

For React/Next.js apps, use the hooks for built-in loading and error states:
import {
  usePasskeySetup,
  usePasskeyAuth,
  usePasskeyStatus,
} from "@chipi-stack/chipi-passkey/hooks";

usePasskeySetup

function CreateWalletButton({ userId }: { userId: string }) {
  const { setupPasskey, isLoading, error, data } = usePasskeySetup();

  const handleCreate = async () => {
    const result = await setupPasskey(userId, "User Name");
    // result.encryptKey, result.credentialId, result.prfSupported
  };

  return (
    <button onClick={handleCreate} disabled={isLoading}>
      {isLoading ? "Creating..." : "Create Wallet"}
    </button>
  );
}

usePasskeyAuth

function SignTransaction() {
  const { authenticate, isLoading, encryptKey } = usePasskeyAuth();

  const handleSign = async () => {
    // Basic: uses stored credential
    const key = await authenticate();

    // Or specify credential + PRF hint:
    // const key = await authenticate("credentialId", { prfSupported: true });

    // Use key to decrypt private key and sign transaction
  };

  return (
    <button onClick={handleSign} disabled={isLoading}>
      {isLoading ? "Authenticating..." : "Sign with Passkey"}
    </button>
  );
}

usePasskeyStatus

function PasskeyInfo() {
  const { status, checkValidity, isChecking } = usePasskeyStatus();

  return (
    <div>
      <p>WebAuthn supported: {status.isSupported ? "Yes" : "No"}</p>
      <p>Has passkey: {status.hasPasskey ? "Yes" : "No"}</p>
      <button onClick={checkValidity}>
        {isChecking ? "Checking..." : "Verify Passkey"}
      </button>
    </div>
  );
}

Full Wallet Creation Flow

Combining the passkey SDK with the Chipi wallet API:
import { createWalletPasskey } from "@chipi-stack/chipi-passkey";
import { useCreateWallet, Chain } from "@chipi-stack/nextjs";

function CreateWallet({ userId }: { userId: string }) {
  const { createWalletAsync, isLoading } = useCreateWallet();
  const [pin, setPin] = useState("");

  const handleCreate = async () => {
    const bearerToken = await getToken(); // from your auth provider

    // usePasskey: true triggers biometric + derives encryption key
    // encryptKey (PIN) becomes the backup
    const wallet = await createWalletAsync({
      params: {
        encryptKey: pin,          // PIN backup (mandatory)
        externalUserId: userId,
        chain: Chain.STARKNET,
        usePasskey: true,         // triggers biometric prompt
      },
      bearerToken,
    });

    // wallet.authMethod === "passkey+pin"
    // wallet.publicKey — the Starknet address
    // wallet.encryptedPrivateKey — encrypted with passkey key
    // wallet.encryptedPrivateKeyBackup — encrypted with PIN
  };
}

Platform Support

PlatformTechnologyPackage
Web (Next.js, React)WebAuthn PRF@chipi-stack/chipi-passkey
Mobile (Expo)Face ID / Touch ID + SecureStore@chipi-stack/chipi-expo
PRF (Pseudo-Random Function) is a WebAuthn extension that deterministically derives an encryption key from the biometric. Not all browsers support it — the SDK detects support and falls back to PBKDF2 when PRF is unavailable.