Storing secrets on a mobile device sounds simple until you try to do it properly. You want them encrypted at rest, gated behind the user's biometrics or passcode, and you want that unlock to cover a whole session rather than firing a prompt on every read. That's exactly the gap the new Capacitor Vault plugin fills.
In this post, we'll look at why it exists, what it does, and how to use it.
Why Another Storage Plugin?
We already ship two related plugins:
- Secure Preferences — encrypted key/value storage the app reads freely in the background.
- Biometrics — on-demand biometric prompts.
Neither covers the "active lock + session" pattern. Secure Preferences is silent: there's no user-facing gate. Biometrics gives you a single prompt, but no notion of a session that stays unlocked across many reads and writes.
That session pattern is the one Ionic Identity Vault popularized — and it's being discontinued. The Capacitor Vault plugin fills that gap with a drop-in alternative, and adds first-class multi-vault support along the way.
What You Get
- Active lock state — a single biometric or passcode prompt unlocks the vault, so you can read and write many values before it locks again.
-
Three vault types —
Biometric,BiometricOrDevicePasscode, andDevicePasscode. - Auto-lock on background — configure how long the app can be backgrounded before the vault locks itself.
-
Lock and unlock events — with a
triggerreason (MANUALorTIMEOUT). - Multi-vault — independent vaults with their own keys and lock policies.
- Hardware-backed encryption — keys live in the Android Keystore and iOS Keychain, with AES-256-GCM as the data cipher.
-
Key invalidation — a typed
KEY_INVALIDATEDerror when the device's biometric set changes. - Export and import — a built-in migration API.
-
Typed error codes — branch on conditions like
UNLOCK_CANCELEDorBIOMETRY_NOT_AVAILABLE.
The plugin supports Capacitor 8 and above. A localStorage-backed web implementation is included for cross-platform development, but is clearly marked unsafe for production.
A Quick Tour
Initialize the Vault
Before anything else, initialize the vault once per session:
import { Vault, VaultType } from '@capawesome-team/capacitor-vault';
await Vault.initialize({
type: VaultType.Biometric,
title: 'Unlock vault',
cancelButtonText: 'Cancel',
iosFallbackButtonText: 'Use Passcode',
lockAfterBackgrounded: 30000, // Use 0 to lock immediately on background
});
Unlock the Vault
Calling unlock() triggers the platform's authentication prompt, and typed errors tell you exactly what happened:
import { Vault, ErrorCode } from '@capawesome-team/capacitor-vault';
try {
await Vault.unlock();
} catch (error) {
if (error.code === ErrorCode.UnlockCanceled) {
// User dismissed the prompt
} else if (error.code === ErrorCode.KeyInvalidated) {
// Biometric set changed — re-enrollment required
} else {
throw error;
}
}
Read and Write Values
While the vault is unlocked, reads and writes behave like any key/value store:
import { Vault } from '@capawesome-team/capacitor-vault';
await Vault.setValue({ key: 'session_token', value: 'eyJhbGciOiJIUzI1NiIs...' });
const { value } = await Vault.getValue({ key: 'session_token' });
Lock and Listen for Events
Lock manually with lock(), and subscribe to events to keep your UI in sync:
import { Vault } from '@capawesome-team/capacitor-vault';
await Vault.addListener('lock', ({ vaultId, trigger }) => {
console.log(`Vault ${vaultId} locked (trigger: ${trigger}).`);
});
await Vault.addListener('unlock', ({ vaultId }) => {
console.log(`Vault ${vaultId} unlocked.`);
});
Multiple Vaults
Every method accepts an optional vaultId. Pass different identifiers and you get fully independent vaults — separate keys, separate lock state, separate prompts:
import { Vault, VaultType } from '@capawesome-team/capacitor-vault';
await Vault.initialize({ vaultId: 'alice', type: VaultType.Biometric, title: "Unlock Alice's vault" });
await Vault.initialize({ vaultId: 'bob', type: VaultType.BiometricOrDevicePasscode, title: "Unlock Bob's vault", lockAfterBackgrounded: 60000 });
This is handy for multi-account apps, where each user's secrets live behind their own lock.
Vault, Secure Preferences, or SQLite?
The right choice depends on how the data is accessed:
- SQLite — relational data with queries, joins, and indexes.
- Secure Preferences — encrypted key/value the app reads freely in the background (OAuth refresh tokens, API keys).
- Vault — encrypted key/value the user must actively unlock with biometrics or a passcode (password manager entries, TOTP secrets, app-lock screens).
Migrating from Ionic Identity Vault
If you're moving off Ionic Identity Vault, the exportData(...) and importData(...) methods give you a one-shot migration path.
Try It Out
The Capacitor Vault plugin is available now to all Capawesome Insiders. There's also an open-source demo app that walks through the full flow.
This article was originally published on the Capawesome blog. If you have questions or feedback, join us on Discord or subscribe to the newsletter.
Top comments (0)