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
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
npm install @chipi-stack/chipi-passkey
Basic Implementation
Create wallet with passkey
"use client";
import { useCreateWallet, Chain } from "@chipi-stack/nextjs";
import { usePasskeySetup } from "@chipi-stack/chipi-passkey/hooks";
Authenticate with passkey
"use client";
import { useTransfer, ChainToken } from "@chipi-stack/nextjs";
import { usePasskeyAuth } from "@chipi-stack/chipi-passkey/hooks";
Migrate existing wallet (optional)
If users already have a PIN-based wallet, they can migrate to passkey:"use client";
import { useMigrateWalletToPasskey } from "@chipi-stack/nextjs";
Example
Create Wallet
Transfer with Passkey
Migrate to Passkey
"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>
);
}
"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>
);
}
"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>
);
}
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)
Passkeys are stored in the browser and synced via platform (iCloud Keychain, Google Password Manager).
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