Guides
The full SoroPass flow on Stellar testnet. Create a passkey wallet, sign a payment, watch a wrong key get rejected on-chain, add a backup device, and recover on another device.
The complete flow, end to end, on Stellar testnet: the same run you can try live at demo.soropass.dev, every transaction on Stellar Expert. It uses the headless Path A API throughout; the config (cfg, deployer, indexer) is the one from the Quickstart.
1 · Create a wallet
A passkey becomes the first signer of a freshly deployed smart-account contract. No seed phrase, nothing to write down. The private key never leaves the device.
import { createPasskey } from '@soropass/core/create';
const account = await createPasskey({ ...cfg, userName: 'alice', deployer });
// Touch ID / Windows Hello → ES256 passkey → v1 smart-wallet deployed + funded.
// Persist account.credentialId (and account.contractId) for fast reconnects.2 · Sign a payment
The wallet sends value; the passkey signs the exact Soroban auth entry (challenge bound to the auth preimage, low-S normalized) that the contract's __check_auth re-derives.
import { sendSmartWalletTx, browserPasskeySigner } from '@soropass/core/sign';
import { Contract, Address, nativeToScVal } from '@stellar/stellar-sdk';
// A native-asset (SAC) transfer FROM the smart wallet.
const transfer = new Contract(NATIVE_SAC).call(
'transfer',
Address.fromString(account.contractId).toScVal(),
Address.fromString(destination).toScVal(),
nativeToScVal(amount, { type: 'i128' }),
);
const res = await sendSmartWalletTx({
operation: transfer,
rpcUrl: cfg.rpcUrl,
networkPassphrase: cfg.networkPassphrase,
sourceSecret: FEE_SOURCE_SECRET,
sign: browserPasskeySigner({ rpId: cfg.rpId, allowCredentials: [account.credentialId] }),
});
// res.status === 'SUCCESS' · res.hash → on Stellar Expert3 · Reject a wrong key
The safety proof: the contract itself rejects a signature from the wrong key, on-chain, not the UI. sendSmartWalletTx re-simulates enforcing __check_auth, which runs secp256r1_verify against the stored signer and fails.
// Same call, but `sign` is a random software key that is NOT a registered signer.
try {
await sendSmartWalletTx({ operation: transfer, ...cfg, sourceSecret, sign: wrongKeySigner });
} catch (err) {
// Rejected on-chain during the enforcing re-simulation, exactly as intended. No funds move.
}4 · Add a backup device
Register a second passkey as an additional signer with an on-chain add_signer, authorized by the first passkey, so a lost device never locks you out.
import { registerPasskey } from '@soropass/core/create';
import { addSigner } from '@soropass/core/recover';
const backup = await registerPasskey({ ...cfg, userName: 'backup' }); // register WITHOUT deploying
await addSigner({
walletContractId: account.contractId,
newSigner: backup,
rpcUrl: cfg.rpcUrl,
networkPassphrase: cfg.networkPassphrase,
sourceSecret: FEE_SOURCE_SECRET,
sign: browserPasskeySigner({ rpId: cfg.rpId, allowCredentials: [account.credentialId] }),
});
// The wallet is now multi-signer: both passkeys work.5 · Recover on another device
On a fresh device with nothing stored, a discoverable passkey sign-in plus the on-chain indexer resolves the wallet(s) that passkey controls. No seed phrase, no local state.
import { recover } from '@soropass/core/recover';
const wallets = await recover({ rpId: cfg.rpId, indexer });
// wallets: { contractId, credentialId }[], resolved from on-chain signer_added events.Surfaces & the OS sheet
The same components adapt across surfaces. The native passkey sheet (Face ID / Touch ID) is the OS. The card stays calm behind it, never a competing spinner.
Interactive sign preview
The real component runs in mock mode on the demo. Open the demo →
Calm opaque panel + pulsing glyph behind the native sheet.
Interactive create preview
The real component runs in mock mode on the demo. Open the demo →
Works from 360px; ≥44–48px touch targets; a bottom-edge OS-sheet hint bar; safe-area aware.
Desktop initiates, the phone that holds the passkey approves (hybrid / caBLE / QR). hybrid_transport is a documented, sourced surface (see the matrix row).
Previews run in mock mode (no network, no authenticator) because browsers restrict WebAuthn inside cross-origin iframes. The real ceremonies run at demo.soropass.dev, every transaction linked to Stellar Expert.
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.
Existing wallets
Add passkeys to users who already hold a classic (G) Stellar account. The migration to a smart account, backup signers, and who pays fees on testnet vs mainnet.