SoroPass

Quickstart

Add passkey sign-in for Stellar smart accounts three ways (headless SDK, drop-in UI components, or inside Stellar Wallets Kit), each with a runnable example.

SoroPass adds passkey smart accounts to your app or wallet. It is a layer you integrate, not a wallet of its own: your users authorize with Face ID, Touch ID, or a security key, and their account is a Soroban smart contract that the passkey controls.

There are three ways to adopt, each with a runnable example. They stack, so start with the one that fits and add the others later:

  • Path A (headless SDK) runs today with only @soropass/core. Any framework, or none. Pick it when you own your UI.
  • Path B (UI components) adds drop-in create / sign / recover screens. Pick it when you want ready-made, themeable screens.
  • Path C (Stellar Wallets Kit) registers passkeys as one more wallet in the kit's picker, branded as yours. Pick it when you already use stellar-wallets-kit.

Install

The SDK is the only required dependency; @stellar/stellar-sdk is a peer you already have (or add it alongside):

bash pnpm add @soropass/core "@stellar/stellar-sdk@>=17"
bash npm i @soropass/core "@stellar/stellar-sdk@>=17"

Install @stellar/stellar-sdk at version 17 or newer: @soropass/core@0.3.1 builds against the stellar-sdk 17 XDR API (on stellar-sdk 12 through 16, install @soropass/core@0.2.1 instead; 0.2.1 signs only classic address credentials, while 0.3.x also signs the addressV2 credentials Protocol 23 networks return from simulation). @soropass/core installs from npm and runs in the browser without a Buffer polyfill. Path B's @soropass/ui installs from npm the same way: npm i @soropass/ui.

One-time config

Pick your network, an RP id, and a deployer (a stable, funded classic account that deploys new wallets and pays fees, holding no user value):

import { Networks } from '@stellar/stellar-sdk';
import { smartWalletV1Deployer, smartWalletV1Indexer } from '@soropass/core';

const cfg = {
  rpId: location.hostname, // passkeys are origin-bound, must be your real domain
  rpName: 'Example',
  rpcUrl: 'https://soroban-testnet.stellar.org',
  networkPassphrase: Networks.TESTNET,
};

const deployer = smartWalletV1Deployer({ ...cfg, deployerSecret: DEPLOYER_SECRET });
const indexer = smartWalletV1Indexer({ rpcUrl: cfg.rpcUrl });

Path A: headless (any framework, or none)

Create, sign, and recover cover the whole lifecycle. This is the runnable core. DEPLOYER_SECRET here is a development convenience: in production the fee-source secret stays server-side or behind a relayer, never in browser code.

import { createPasskey } from '@soropass/core/create';
import { sendSmartWalletTx, browserPasskeySigner } from '@soropass/core/sign';
import { recover } from '@soropass/core/recover';

// 1. Create: mint an ES256 passkey and deploy its smart account.
const account = await createPasskey({ ...cfg, userName: 'alice', deployer });
//   account.contractId · account.credentialId · account.publicKey  → persist credentialId

// 2. Sign + submit: any Soroban operation, authorized by the passkey.
const res = await sendSmartWalletTx({
  operation, // build with @stellar/stellar-sdk (a SAC transfer, a contract call, …)
  rpcUrl: cfg.rpcUrl,
  networkPassphrase: cfg.networkPassphrase,
  sourceSecret: FEE_SOURCE_SECRET, // fee payer; swap in a relayer adapter for production
  sign: browserPasskeySigner({ rpId: cfg.rpId, allowCredentials: [account.credentialId] }),
});
//   res.status · res.hash  → on Stellar Expert

// 3. Recover: on a new device, resolve the wallets this passkey controls.
const wallets = await recover({ rpId: cfg.rpId, indexer });

// 4. Add a backup device: a second passkey signer, on-chain (see /docs/sdk/accounts#addsigner).

Every failure throws a typed KitError with one of 10 frozen codes. See errors. For CI, @soropass/core/testing's createPasskeyKit({ mode: 'mock' }) runs the same shape with no network and no authenticator (Testing); swap to mode: 'live' in production.

Path B: with the UI components

Wrap a core call in a headless flow (a state machine: idle → prompting → deploying → success | error) and mount the styled screen. Framework-agnostic, themed by tokens.css.

import { createCreatePasskeyFlow } from '@soropass/ui/headless';
import { mountCreateScreen } from '@soropass/ui/styled';
import '@soropass/ui/styled.css';

const flow = createCreatePasskeyFlow({
  userActivation: navigator.userActivation, // enforces the Safari user-gesture rule
  async create({ userName }, report) {
    // call report.deploying() when you enter the on-chain deploy phase
    return createPasskey({ ...cfg, userName, deployer });
  },
});

const { unmount } = mountCreateScreen(document.getElementById('slot'), { flow });

The same shape gives you mountSignScreen, mountRecoverScreen, and mountAddDeviceScreen. Every visual value is a --pk-* token. Re-skin without touching component code. See Components and Theming. For React, a ~20-line wrapper adopts the styled layer with no runtime dependency (details).

Path C: via Stellar Wallets Kit

Register a PasskeyModule and passkey becomes one more wallet in @creit.tech/stellar-wallets-kit. Your existing getAddress / signTransaction calls are unchanged.

import { PasskeyModule, PASSKEY_ID } from '@creit.tech/stellar-wallets-kit/modules/passkey';
import { StellarWalletsKit } from '@creit.tech/stellar-wallets-kit/sdk';
import { Networks } from '@creit.tech/stellar-wallets-kit/types';

const passkey = new PasskeyModule({ ...cfg, indexer, deployer });
StellarWalletsKit.init({ network: Networks.TESTNET, modules: [passkey] });

await passkey.createAccount('alice'); // createAccount lives on the module
StellarWalletsKit.setWallet(PASSKEY_ID);
const { address } = await StellarWalletsKit.getAddress();
const { signedTxXdr } = await StellarWalletsKit.signTransaction(txXdr, { networkPassphrase });

The PasskeyModule implements the @creit.tech/stellar-wallets-kit ModuleInterface (v2.5.0) and is proposed to the kit maintainers in Stellar-Wallets-Kit issue #95. The published kit does not include the ./modules/passkey export yet, so this path runs once the module lands in a kit release. The integration guide documents the full module contract and how you run the same flows today through @soropass/core, which is on npm.

Next

On this page