Skip to main content

Credential Storage

WalletSdk.credentials exposes WalletCredentials — the storage facade over the SDK's encrypted SQLCipher database. Everything the wallet persists (wallets, credentials, DIDs, sharing logs) goes through this API.

Credentials accepted via acceptOffer are stored automatically; you mainly use this API to read and display them, and to keep wallet/DID records in sync with your backend.

The VaultCredential entity​

Every stored credential is a VaultCredential:

FieldTypeDescription
idStringCredential identifier (referenced by getCredential, sharing logs, prompts)
walletStringOwning wallet's external id
documentStringRaw credential document (e.g. the SD-JWT compact serialization)
disclosuresStringSD-JWT disclosures associated with the credential
formatStringCredential format, e.g. vc+sd-jwt
parsedDocumentMap<String, dynamic>Decoded credential payload for display
addedOnDateTimeWhen the credential was stored
deletedOnDateTime?Soft-delete timestamp, null while active
pendingboolWhether the credential is awaiting finalization

VaultCredential supports fromJson / toJson / copyWith for host-side serialization.

Wallets and DIDs​

Credentials are grouped under a wallet keyed by your API wallet id (walletExternalId). Create or update the local wallet record after sign-in:

await sdk.credentials.ensureWallet(
externalId: apiWalletId,
userId: userId,
name: 'Primary wallet',
);

Store the holder DID your backend provisioned:

await sdk.credentials.upsertDid(
walletExternalId: apiWalletId,
did: 'did:key:z6Mk...',
alias: 'primary', // optional
document: didDocument, // optional DID document JSON
keyId: keyId, // optional
);

final didRow = await sdk.credentials.getDidByWalletExternalId(apiWalletId);
final did = didRow?['did'] as String?;

Look up stored wallets:

final byUser = await sdk.credentials.getWalletByUserId(userId);
final byExternalId = await sdk.credentials.getWalletByExternalId(apiWalletId);
// Both return StoredWallet? (localId, externalId, userId, name, timestamps).

Listing and reading credentials​

// All active credentials in a wallet.
final credentials = await sdk.listCredentials(walletExternalId: apiWalletId);

// One credential by id (null when not found).
final credential = await sdk.getCredential(credentialId);

Both are also available on sdk.credentials directly. A typical list screen:

ListView(
children: [
for (final credential in credentials)
ListTile(
title: Text(credential.parsedDocument['vct']?.toString() ?? 'Credential'),
subtitle: Text('Added ${credential.addedOn} — ${credential.format}'),
onTap: () => openDetail(credential.id),
),
],
);

Saving credentials manually​

acceptOffer stores credentials for you. Use saveCredential only when you obtain a credential through your own channel:

final id = await sdk.credentials.saveCredential(
credential: vaultCredential,
walletExternalId: apiWalletId,
checkDuplicate: true, // default — rejects an identical existing credential
);

Duplicate detection helpers:

final exists = await sdk.credentials.isDuplicate(
credential: vaultCredential,
walletExternalId: apiWalletId,
);

final sameTypeAndIssuer = await sdk.credentials.getCredentialByTypeAndIssuer(
walletExternalId: apiWalletId,
credential: vaultCredential,
);

Deleting credentials​

Deletion is a soft delete — the row is retained with deletedOn set and excluded from listings:

await sdk.credentials.deleteCredential(credentialId);

Sharing logs​

The SDK records an audit trail of presentations. Query it to build a "sharing history" screen:

// Newest-first across all credentials; optionally filter by status.
final all = await sdk.credentials.getAllSharingLogs();

// Logs for one credential.
final logs = await sdk.credentials.getSharingLogsForCredential(credentialId);

// Single entry.
final entry = await sdk.credentials.getSharingLogById(logId);

Manage entries when your app tracks a custom sharing lifecycle:

final id = await sdk.credentials.insertSharingLog(row);
await sdk.credentials.updateSharingLogStatus(id, 'completed');
await sdk.credentials.deleteSharingLog(id);

Storage security​

  • The database is encrypted with SQLCipher; the key comes from your storageEncryptionKeyProvider and never leaves the device.
  • The SDK database (wallet_sdk.db by default) is fully SDK-owned — do not open or migrate it from host code.
  • Losing the encryption key makes stored credentials unrecoverable. Treat key storage (Keychain / EncryptedSharedPreferences) as production-critical.