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:
- Browser shows biometric prompt (Face ID / Touch ID / Windows Hello)
- WebAuthn PRF extension derives a deterministic encryption key
- Credential metadata stored in
localStorage for future auth
- 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
| Platform | Technology | Package |
|---|
| 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.