Skip to main content

wallet-sdk

The wallet-sdk is a small TypeScript package that implements the high-level flows for eVault provisioning, platform authentication (signing), and public-key sync. It is crypto-agnostic: you supply a CryptoAdapter (BYOC – bring your own crypto), and the SDK handles the HTTP and protocol steps.

The eID Wallet uses wallet-sdk with an adapter that delegates to its KeyService, so hardware/software key manager behavior is unchanged.

Overview​

  • Package: wallet-sdk (workspace package under packages/wallet-sdk/)
  • Exports: CryptoAdapter type, provision, authenticate, syncPublicKeyToEvault, and their option/result types
  • Dependencies: jose (for JWT verification when checking existing keys during sync). Uses the global fetch for HTTP

CryptoAdapter (BYOC)​

Implement this interface to plug in your key storage (e.g. KeyService + hardware/software managers):

interface CryptoAdapter {
getPublicKey(keyId: string, context: string): Promise<string | undefined>;
signPayload(keyId: string, context: string, payload: string): Promise<string>;
ensureKey(keyId: string, context: string): Promise<{ created: boolean }>;
}
  • getPublicKey: Return the public key for the given key id and context, or undefined if the key does not exist.
  • signPayload: Sign the payload with the same key as used for getPublicKey. Return the signature string (encoding is up to the adapter, e.g. base64 or multibase).
  • ensureKey: Ensure a key exists for the given key id and context; create it if needed. Return { created: true } if a new key was created, { created: false } otherwise.

Contexts used by the eID Wallet include "onboarding", "pre-verification", and "signing". The SDK does not interpret contexts; it only passes them through to the adapter.

API​

provision(adapter, options)​

Provisions an eVault: fetches entropy from the Registry, gets the public key from the adapter, and POSTs to the Provisioner.

Flow:

  1. GET {registryUrl}/entropy → obtain entropy token
  2. adapter.getPublicKey(keyId, context) → public key (must exist; ensure key before calling if needed)
  3. POST {provisionerUrl}/provision with { registryEntropy, namespace, verificationId, publicKey }

Options: registryUrl, provisionerUrl, namespace, verificationId, and optionally keyId (default "default"), context (default derived from isPreVerification), isPreVerification.

Returns: { success, w3id, uri }. Throws on HTTP or validation errors.

Example (eID Wallet pre-verification):

const result = await provision(globalState.walletSdkAdapter, {
registryUrl: PUBLIC_REGISTRY_URL,
provisionerUrl: PUBLIC_PROVISIONER_URL,
namespace: uuidv4(),
verificationId,
keyId: "default",
context: "pre-verification",
isPreVerification: true,
});
// result.uri, result.w3id

authenticate(adapter, options)​

Ensures the key exists and signs the session payload. The caller is responsible for sending the signature to the platform (e.g. POST to redirect URL or open deeplink).

Flow:

  1. adapter.ensureKey(keyId, context)
  2. adapter.signPayload(keyId, context, sessionId)
  3. Return { signature }

Options: sessionId, context, and optionally keyId (default "default").

Returns: { signature }.

Example (eID Wallet auth):

const { signature } = await authenticate(globalState.walletSdkAdapter, {
sessionId: sessionPayload,
keyId: "default",
context: isFake ? "pre-verification" : "onboarding",
});
// Then POST to redirect URL: { ename, session, signature, appVersion }

syncPublicKeyToEvault(adapter, options)​

Syncs the adapter’s public key to the eVault: calls /whois, optionally skips PATCH if the current key is already present in key-binding certificates (using Registry JWKS), then PATCH /public-key if needed.

Flow:

  1. GET {evaultUri}/whois with header X-ENAME: {eName}
  2. If registryUrl is provided and whois returns key-binding certificates, verify with Registry’s /.well-known/jwks.json and skip PATCH if the current public key is already in a valid cert for this eName
  3. adapter.getPublicKey(keyId, context)
  4. PATCH {evaultUri}/public-key with { publicKey }, headers X-ENAME and optional Authorization: Bearer {authToken}

The SDK does not read or write localStorage; the caller can set a hint (e.g. publicKeySaved_${eName}) after a successful sync.

Options: evaultUri, eName, context, and optionally keyId (default "default"), authToken, registryUrl (for skip-if-present verification).

Example (eID Wallet):

await syncPublicKeyToEvault(globalState.walletSdkAdapter, {
evaultUri: vault.uri,
eName,
keyId: "default",
context: isFake ? "pre-verification" : "onboarding",
authToken: PUBLIC_EID_WALLET_TOKEN || null,
registryUrl: PUBLIC_REGISTRY_URL,
});

Use in the eID Wallet​

The eID Wallet:

  1. Implements a CryptoAdapter by wrapping KeyService in createKeyServiceCryptoAdapter(keyService) (see src/lib/wallet-sdk-adapter.ts).
  2. Exposes this adapter on GlobalState as walletSdkAdapter and passes it into VaultController.
  3. Uses provision in the onboarding (pre-verification) and verify (real user) flows instead of inline entropy + provision calls.
  4. Uses authenticate in the scan-qr auth and signing flows, then performs the POST or deeplink open in the UI.
  5. Uses syncPublicKeyToEvault inside VaultController.syncPublicKey(eName) instead of inline whois + PATCH logic.

See eID Wallet for architecture and key manager details.

References​

  • eID Wallet – Consumer of wallet-sdk; KeyService and CryptoAdapter
  • Registry – Entropy and key-binding certificates
  • eVault – Whois and public-key storage
  • Links – Production URLs (Provisioner, Registry)