Skip to content

Discussion: PasskeyModule for stellar-wallets-kit (passkey sign-in for Soroban smart accounts) #95

Description

@justmert

Hi. I built SoroPass, a passkey sign-in layer for Stellar smart accounts, including a PasskeyModule for this kit (v2.2.0). The module is implemented and tested against the kit's ModuleInterface. I have a couple of questions about the interface.

What it is. Users create and operate a Soroban smart account with a device passkey (Face ID, Touch ID, or a security key) instead of a seed phrase. The signature is a WebAuthn secp256r1 assertion the account contract verifies on-chain (Protocol 21 / CAP-0051). It is a layer for wallets, not a wallet: no custody, no key storage, no infra. Any wallet on the kit could offer passkey sign-in by registering one module.

How I mapped it to ModuleInterface (v2.2.0):

Member PasskeyModule
moduleType HOT_WALLET (no smart-account type exists, see question 1)
productId / productName "passkey" / "Passkey (Smart Account)"
isAvailable() isUVPAA() behind a 500 ms timeout; returns false, never throws
getAddress() resolves the connected smart-account C... address via the indexer (new accounts use a separate createAccount() helper)
signTransaction / signAuthEntry my SDK assembles the Soroban auth, signs with the passkey, low-S normalizes the secp256r1 signature
signMessage() returns the low-S assertion signature (verification is application-defined, see question 2)
getNetwork() the configured network

Registering it adds "Passkey" to the kit's picker:

StellarWalletsKit.init({
  modules: [ new PasskeyModule({ rpId, rpName, networkPassphrase, indexer, deployer }) ],
});
StellarWalletsKit.setWallet('passkey');

What is done:

  • A minimal SDK (@soropass/core, ~5.9 KB gzip; @stellar/stellar-sdk is a peer dep, never bundled): ES256-only, always low-S, typed errors.
  • The kit module, tested against v2.2.0.
  • Drop-in, themeable create / sign / recover UI components.
  • A living browser-compatibility matrix.

Proven on testnet. A passkey-signed SorobanAuthorizationEntry passes a real deployed __check_auth / secp256r1_verify, and a wrong key is rejected on-chain:

Try it. You can see and test a live demo on the site below. The create, sign, and recover flows run with the SoroPass pre-built components on testnet, so you can run them yourself.

Image

Questions:

  1. Smart-account (C-address) wallets. getAddress() returns an opaque { address } with no G/C distinction, but mine is a C... contract. Do you want a typed signal (or a new moduleType) for contract-account modules, or should it stay an opaque string and let dApps opt into the Soroban path?
  2. signMessage on a contract account has no standard on-chain verification. Should a module reject it, or do you have a preferred convention?

I can match your conventions or split the module differently. Thanks for building the kit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions