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.
The package ships two parallel surfaces. Pick the one that matches your wallet type. Mixing them is fine — both can live on the same device under different localStorage keys.
| Wallet type | Surface | Use when |
|---|
| SHHH (default since v14.5.0) | createShhhPasskey, signShhhMessage, …ShhhCredential | New integrations, any SHHH wallet |
| Legacy CHIPI v29 | createWalletPasskey, getWalletEncryptKey, …Credential | Existing CHIPI v29 wallets you haven’t migrated yet |
The React hooks (usePasskeySetup, usePasskeyAuth, usePasskeyStatus) wrap the legacy surface today. SHHH equivalents ship in a follow-on release; until then, call createShhhPasskey / signShhhMessage directly inside your own effects.
SHHH passkey (modern path)
createShhhPasskey
Registers a new P-256 platform passkey. No PRF extension, no PIN derivation. The private key stays inside the platform authenticator.
import { createShhhPasskey } from "@chipi-stack/chipi-passkey";
const result = await createShhhPasskey(
userId: string,
userName: string,
options?: { rpName?: string; rpId?: string; displayName?: string },
);
// result: {
// credentialId: string,
// publicKey: { x: Uint8Array(32), y: Uint8Array(32) },
// publicKeyHex: { x: string, y: string },
// rpId: string,
// }
Defaults: rpId = window.location.hostname, rpName = "Chipi Wallet". Persists { credentialId, publicKey, userId, rpId, transports } to localStorage under the key chipi_wallet_shhh_passkey_credential. Throws on NotAllowedError (user cancelled) or InvalidStateError (passkey already exists for this account on this device).
signShhhMessage
Runs navigator.credentials.get() with messageHash encoded as the 32-byte big-endian WebAuthn challenge. Returns the raw assertion bytes ready to feed into buildWebAuthnEnvelopeFromAssertion from @chipi-stack/backend.
import { signShhhMessage } from "@chipi-stack/chipi-passkey";
const result = await signShhhMessage({
messageHash: bigint, // SHHH SNIP-12 hash
credentialId?: string, // defaults to the stored credential
rpId?: string, // defaults to the registered rpId
});
// result: {
// authenticatorData: Uint8Array,
// clientDataJSON: Uint8Array,
// signatureDer: Uint8Array,
// credentialId: string,
// }
hasShhhPasskey
Synchronous check for a stored SHHH credential. Use it to branch between “Sign up” and “Sign in” UI without prompting.
import { hasShhhPasskey } from "@chipi-stack/chipi-passkey";
const ready = hasShhhPasskey(); // boolean
getStoredShhhCredential
Reads the persisted credential metadata.
import { getStoredShhhCredential } from "@chipi-stack/chipi-passkey";
const stored = getStoredShhhCredential();
// stored | null:
// {
// credentialId: string,
// publicKey: { x: string, y: string }, // hex
// createdAt: string, // ISO timestamp
// userId: string,
// rpId: string,
// transports?: AuthenticatorTransport[],
// }
removeStoredShhhCredential
Clears the stored credential id from localStorage. Use at logout, account reset, or when the user opts to re-register. The platform authenticator’s stored credential is NOT deleted — only your app’s reference to it.
import { removeStoredShhhCredential } from "@chipi-stack/chipi-passkey";
removeStoredShhhCredential();
Exported for tests and for advanced callers who want to verify a passkey’s pubkey against a separately-stored copy.
import {
bigintToBe32,
extractP256PubkeyFromRegistration,
extractP256PubkeyFromSpki,
extractP256PubkeyFromAttestationObject,
} from "@chipi-stack/chipi-passkey";
extractP256PubkeyFromRegistration is the dispatch helper — it tries the SPKI fast path first (Chrome, Safari, modern Edge), then falls back to decoding the CBOR attestationObject for older browsers. You only need these utilities if you’re writing your own registration-response handler.
Legacy PRF passkey [#legacy-prf-passkey]
The PRF passkey path encrypts a STARK private key with a key derived from the WebAuthn PRF extension. It backs CHIPI v29 wallets — both legacy on-chain accounts and the dual-key (passkey primary + PIN backup) flow. New SHHH integrations don’t need this surface; existing CHIPI v29 integrations still do.
createWalletPasskey
Registers a passkey with the PRF extension enabled, derives an encryption key from the PRF output, and stores the credential metadata.
import { createWalletPasskey } from "@chipi-stack/chipi-passkey";
const result = await createWalletPasskey(userId: string, userName: string);
// result: {
// encryptKey: string, // hex — use to encrypt the wallet's STARK key
// credentialId: string,
// prfSupported: boolean, // false on Firefox without PRF
// }
Persists { credentialId, createdAt, userId, transports, prfSupported } to localStorage under chipi_wallet_passkey_credential. Separate storage key from the SHHH path — both surfaces coexist on the same device.
getWalletEncryptKey
Authenticates with an existing PRF passkey and returns the same encryptKey derived at registration time. Use it before every wallet operation that needs the STARK key (transfer, session-key registration, encryption rotation).
import { getWalletEncryptKey } from "@chipi-stack/chipi-passkey";
const encryptKey = await getWalletEncryptKey(
credentialId?: string,
options?: { prfSupported?: boolean },
);
// encryptKey: string | null // null if user cancels the biometric prompt
The PRF output is deterministic — same passkey, same PRF salt, same encryption key. If PRF is unavailable (Firefox before 122, or a browser switch after registering on Chrome), the function falls back to PBKDF2 derivation only if the credential was originally registered without PRF. Otherwise it throws — see When passkeys fail.
Status checks
import {
isWebAuthnSupported,
isWebAuthnPRFSupported,
hasWalletPasskey,
verifyWalletPasskey,
verifyWalletPasskeyDetailed,
} from "@chipi-stack/chipi-passkey";
isWebAuthnSupported(); // boolean
isWebAuthnPRFSupported(); // alias for isWebAuthnSupported (kept for backcompat)
hasWalletPasskey(); // boolean — is a credential stored locally?
await verifyWalletPasskey(); // boolean
await verifyWalletPasskeyDetailed(); // { valid, reason?, message? }
Credential management
import {
getStoredCredential,
removeStoredCredential,
} from "@chipi-stack/chipi-passkey";
const stored = getStoredCredential();
// stored | null:
// {
// credentialId: string,
// createdAt: string,
// userId: string,
// transports?: AuthenticatorTransport[],
// prfSupported?: boolean,
// }
removeStoredCredential();
React hooks (legacy passkey path)
The hooks today wrap the legacy PRF surface — usePasskeySetup calls createWalletPasskey, usePasskeyAuth calls getWalletEncryptKey. Import from the /hooks subpath so Next.js bundles get the right "use client" directives.
import {
usePasskeySetup,
usePasskeyAuth,
usePasskeyStatus,
} from "@chipi-stack/chipi-passkey/hooks";
usePasskeySetup
function CreateWalletButton({ userId, userName }: { userId: string; userName: string }) {
const { setupPasskey, isLoading, error, data } = usePasskeySetup();
async function onClick() {
const result = await setupPasskey(userId, userName);
// result: { encryptKey, credentialId, prfSupported }
}
return (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? "Creating…" : "Create wallet"}
</button>
);
}
usePasskeyAuth
function SignButton() {
const { authenticate, isLoading, error, data } = usePasskeyAuth();
async function onClick() {
const encryptKey = await authenticate(); // null if user cancelled
if (encryptKey) {
// use encryptKey for the next wallet operation
}
}
}
usePasskeyStatus
function PasskeyStatusBanner() {
const { status, hasPasskey, refresh } = usePasskeyStatus();
// status: "loading" | "ready" | "not_supported" | "no_passkey" | "error"
if (status === "no_passkey") return <SetupPrompt />;
if (status === "not_supported") return <PinFallback />;
return null;
}
TypeScript types
import type {
ShhhPasskeyCredential,
CreateShhhPasskeyResult,
ShhhAssertion,
CreateWalletPasskeyResult,
WalletCredentialMetadata,
PasskeyVerifyResult,
UsePasskeySetupResult,
UsePasskeyAuthResult,
UsePasskeyStatusResult,
PasskeyStatus,
} from "@chipi-stack/chipi-passkey";
Related