SoroPass
SDK

recover & connect

Bring a returning SoroPass user back to their Stellar smart account — silent connect() on a known device, discoverable recover() for new devices.

Two ways to bring a returning user back to their smart account. connect() is the silent happy path on a known device; recover() is the new-device / cleared-storage path. Both map a passkey credential to its C-address through an IndexerAdapter.

Overview

The two functions ship from dedicated subpaths so a wallet only pulls in what it uses:

import { connect } from '@soropass/core/connect';
import { recover } from '@soropass/core/recover';
Try connect() first. When it returns null there is no stored credential or no account — fall through to recover(), which performs a discoverable get and resolves every account controlled by the credential.
@stellar/stellar-sdk is a peer dependency — it is never bundled into @soropass/core. The account resolution that turns a credential into a C-address is delegated entirely to your IndexerAdapter.

Highlights

connect()

Silent reconnect using the stored credential id. Where conditional mediation is available, does a best-effort mediation:'silent' liveness check; the C-address is resolved via the IndexerAdapter regardless, so connect degrades gracefully where silent mediation is unsupported. Returns null when there is no stored credential (the caller should recover) or no account.

connect(options: ConnectOptions): Promise<ConnectResult | null>

ConnectOptions

FieldTypeDescription
rpId (required)stringRelying Party ID for the WebAuthn ceremony.
indexer (required)IndexerAdapterResolves the credential to its C-address(es).
webauthnWebAuthnClientOptional WebAuthn client override (defaults to the platform navigator.credentials).
storageCredentialStorageWhere the stored credential id is read from.
silentMediationSupportedbooleanOverride the isConditionalMediationAvailable probe.

ConnectResult

FieldTypeDescription
contractIdstringThe resolved smart-account C-address.
credentialIdstringThe passkey credential id that controls it.

ReturnsPromise<ConnectResult | null>. null means no stored credential or no account; the caller should fall through to recover().

Usage

import { connect } from '@soropass/core/connect';

const session = await connect({
  rpId: 'wallet.example.com',
  indexer,
  storage,
});

if (session) {
  // known device — silent reconnect succeeded
  use(session.contractId, session.credentialId);
} else {
  // no stored credential or no account — fall through to recover()
}

recover()

The lost-localStorage / new-device path. Performs a discoverable-credential get() (no allowCredentials), then resolves every smart account controlled by that credential via the IndexerAdapter.

recover(options: RecoverOptions): Promise<RecoverResult[]>

RecoverOptions

FieldTypeDescription
rpId (required)stringRelying Party ID for the WebAuthn ceremony.
indexer (required)IndexerAdapterResolves the recovered credential to its C-address(es).
webauthnWebAuthnClientOptional WebAuthn client override (defaults to the platform navigator.credentials).
challengeUint8ArrayOptional challenge bytes for the get() ceremony.
userActivation{ isActive: boolean }Caller-supplied user-gesture state for the WebAuthn call.

RecoverResult

FieldTypeDescription
contractIdstringA smart-account C-address controlled by the credential.
credentialIdstringThe discoverable passkey credential id that was used.

ReturnsPromise<RecoverResult[]>. One credential can control several accounts; recover() handles both 1 and many — present a picker when the array has more than one entry.

Usage

import { recover } from '@soropass/core/recover';

const accounts = await recover({
  rpId: 'wallet.example.com',
  indexer,
  userActivation: { isActive: true },
});

if (accounts.length === 1) {
  open(accounts[0].contractId);
} else if (accounts.length > 1) {
  // one credential, several smart accounts — let the user choose
  showPicker(accounts);
}

IndexerAdapter

Both functions take an IndexerAdapter to map a credential to its C-address(es). It exposes a single method, resolveByCredential, which returns the accounts a credential controls. See Adapters for the bundled events indexer and how to write your own.

interface IndexerAdapter {
  resolveByCredential(credentialId: string): Promise<ResolvedAccount[]>;
}

// ResolvedAccount = { contractId: string }
FieldTypeDescription
resolveByCredential(credentialId: string) => Promise<ResolvedAccount[]>Returns every account whose signer is the given credential.

Connect-then-recover

connect() is the happy path on a known device — a null result falls through to recover(), the new-device / cleared-storage path. This is the idiomatic startup sequence:

import { connect } from '@soropass/core/connect';
import { recover } from '@soropass/core/recover';

async function restore(rpId, indexer, storage) {
  const session = await connect({ rpId, indexer, storage });
  if (session) return [session];

  // null -> no stored credential / no account: recover
  return recover({ rpId, indexer, userActivation: { isActive: true } });
}

On this page