@aparajita/capacitor-secure-storage
Encrypted key-value storage for Capacitor apps, backed by the iOS Keychain and Android Keystore.
Repository Health
Technical Analysis
@aparajita/capacitor-secure-storage is a Capacitor plugin that gives iOS and Android apps a simple get/set/remove API for storing sensitive data, while the underlying platform handles the actual encryption. On iOS it writes to the system Keychain (with optional iCloud Keychain sync), and on Android it encrypts values with AES-256-GCM using a key generated and held by the Android Keystore before persisting them in SharedPreferences. On the web it falls back to plain localStorage for local development and debugging only.
Beyond basic storage, it supports storing any JSON-serializable value (strings, numbers, booleans, objects, arrays) plus native Date round-tripping, a configurable key prefix to avoid collisions between apps or features, per-call or global iCloud sync control, and per-item Keychain accessibility policies (e.g. whenUnlocked, afterFirstUnlockThisDeviceOnly). It also exposes getItem/setItem/removeItem methods that conform to the @vueuse StorageLikeAsync interface, so it can be dropped in as a persistence adapter for Vue composables. It was built as a companion to @aparajita/capacitor-biometric-auth for securely storing login credentials behind biometric gates, but works standalone for any sensitive key-value data.
What You Get
- A unified get/set/remove/keys/clear API that works identically across iOS, Android, and web
- iOS storage in the system Keychain with optional iCloud Keychain sync (global or per-call override)
- Android storage encrypted with AES/GCM using a key held in the Android Keystore, persisted in app-scoped SharedPreferences
- Automatic JSON serialization for strings, numbers, booleans, objects, arrays, and Date values (with ISO 8601 round-tripping)
- A configurable key prefix to namespace stored items and avoid collisions
- Per-item iOS Keychain accessibility control (whenUnlocked, afterFirstUnlock, and device-only variants)
- Low-level getItem/setItem/removeItem methods compatible with the @vueuse StorageLikeAsync interface
- Typed StorageError with a .code you can match against StorageErrorType (missingKey, invalidData, osError, unknownError)
Common Use Cases
- Storing auth tokens or refresh tokens for a mobile app without touching plaintext localStorage
- Persisting login credentials behind biometric authentication alongside @aparajita/capacitor-biometric-auth
- Caching API keys or per-user secrets that must survive app restarts but not app deletion
- Syncing lightweight secure preferences (e.g. a saved PIN or session flag) across a user’s iOS devices via iCloud Keychain
Under The Hood
Architecture The plugin follows an abstract-base + platform-adapter pattern: SecureStorageBase (src/base.ts) implements the shared logic — key prefixing, error normalization via tryOperation(), Date-to-ISO conversion, and JSON serialization — while leaving native-facing methods (internalGetItem, internalSetItem, internalRemoveItem, getPrefixedKeys, clearItemsWithPrefix, setSynchronizeKeychain) abstract. SecureStorageWeb (src/web.ts) and SecureStorageNative (src/native.ts) each extend the base and fill in those methods: the web adapter reads/writes localStorage directly, while the native adapter proxies calls through Capacitor’s registerPlugin bridge (src/index.ts) to platform-native code — SecureStorage.java on Android (android/src/main/java/…/SecureStorage.java) encrypts values with an AndroidKeyStore-held AES/GCM key before storing them in a dedicated SharedPreferences file, while Plugin.swift on iOS (ios/Sources/SecureStoragePlugin/Plugin.swift) reads/writes the system Keychain via the Security framework. This three-tier split keeps the encryption itself entirely inside OS-provided secure storage rather than in JS.
Tech Stack Written in TypeScript (ES modules) targeting Capacitor 8, built with Rollup and vue-tsc for declaration output, and linted with oxlint, ESLint (flat config, neostandard, eslint-plugin-unicorn/vue) and Prettier. The Android side is plain Java using platform android.security.keystore APIs (KeyGenParameterSpec, KeyStore, AES/GCM/NoPadding Cipher) with no third-party crypto dependency; the iOS side is Swift using Apple’s Security framework directly, distributed via both CocoaPods and Swift Package Manager. Runtime dependencies are limited to the @capacitor/* peer family — encryption is delegated entirely to the native platforms rather than a JS crypto library.
Code Quality The repository has native-side test coverage (ios/PluginTests/PluginTests.swift) but no visible JavaScript/TypeScript unit tests for base.ts, web.ts, or native.ts. Error handling is explicit and typed: native rejections are caught in tryOperation() and re-thrown as a typed StorageError with a StorageErrorType code (missingKey, invalidData, osError, unknownError) instead of leaking raw native exceptions, and a dedicated ISO-8601 regex parser in base.ts guards Date conversion instead of relying on Date.parse. Naming is consistent (internalX/X method pairs) and the shared implementation stays in one readable ~300-line file. Enforcement is strict for a project this size — oxlint runs with —deny-warnings and vue-tsc —noEmit gates every build via the prebuild script.
API Design
The public surface (SecureStoragePlugin in src/definitions.ts) is small and consistent: get/set/remove share the same (key, …optional overrides) shape, every method returns a Promise, and optional per-call parameters (sync, access, convertDate) transparently override instance-level defaults set via setSynchronize/setDefaultKeychainAccess. Errors surface as a single typed StorageError class rather than ad hoc strings. Two API tiers sit side by side: the primary get/set/remove/keys/clear methods with Date/JSON handling, and lower-level getItem/setItem/removeItem string-only methods that intentionally mirror @vueuse’s StorageLikeAsync interface, letting Vue/Ionic developers plug the plugin directly into vueuse composables. Getting started requires only the pre-wired SecureStorage export and a standard npx cap sync — no manual native linking.
Used by 2 apps in this directory
Bramble
Password Manager · Security · Authentication
Local-first, end-to-end encrypted password manager that syncs your vault directly between your own devices over a private peer-to-peer mesh — no server, no account, no cloud in the middle.
Logseq
Note Taking · Knowledge Management
A privacy-first, open-source knowledge graph platform combining Markdown, Org-mode, bidirectional linking, and local-first storage for building your second brain.