secure-storage
Keep login tokens and secrets in the phone or computer's secure vault instead of plain storage.
Every export of kitr/secure-storage, kitr/secure-storage/react, kitr/secure-storage/provider, read from the published type definitions. The guide shows them in use.
kitr/secure-storage
createMemorySecureStorageDriver
function createMemorySecureStorageDriver(name?: string): SecureStorageDriver;
createSecureStorage
function createSecureStorage(config?: SecureStorageConfig, app?: KitrApp): SecureStorage;
createWebSecureStorageDriver
function createWebSecureStorageDriver(storage?: StorageDriver): SecureStorageDriver;
Encrypts values with AES-GCM before handing them to a StorageDriver. The key never leaves
the browser's key store, so the ciphertext in local storage cannot be read elsewhere.
defineSecureItem
function defineSecureItem<T>(key: string, options?: SecureItemOptions<T>): (storage: SecureStorage) => SecureItem<T>;
A typed, subscribable secret: const session = defineSecureItem<Session>('session').
Bind it to an app with session.bind(app.container.use('secureStorage')) or use it through the provider.
useSecureStorage
function useSecureStorage(app: KitrApp): SecureStorage;
defineConfig
const defineConfig: <const C extends SecureStorageConfig>(config: C) => C;
SecureItem
interface SecureItem<T> {
key: string;
get(): Promise<T | undefined>;
set(value: T): Promise<void>;
remove(): Promise<void>;
subscribe(listener: (value: T | undefined) => void): () => void;
}
SecureItemOptions
interface SecureItemOptions<T> {
/** Validate what comes back from the vault; invalid values are removed. */
schema?: StandardSchemaV1<unknown, T>;
/** Serialise with something other than JSON. */
codec?: {
encode(value: T): string;
decode(raw: string): T;
};
}
SecureStorage
interface SecureStorage {
readonly driver: SecureStorageDriver;
/** True when values live in an OS vault rather than the encrypted web fallback. */
readonly secure: boolean;
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
remove(key: string): Promise<void>;
/** Keys written through this service (not every item in the vault). */
keys(): Promise<string[]>;
clear(): Promise<void>;
}
SecureStorageConfig
interface SecureStorageConfig {
/** Prefix for every key. Default `kitr:secure:`. */
prefix?: string;
/** Replace the resolved driver (tests, custom vaults). */
driver?: SecureStorageDriver;
/** Where the web fallback keeps the ciphertext. Default: the app's local storage driver. */
storage?: StorageDriver;
/** Refuse to store anything when only the web fallback is available (production kiosks, banking). */
requireSecure?: boolean;
}
SecureStorageDriver
interface SecureStorageDriver {
name: string;
/** Backed by hardware or an OS vault (false for the web fallback). */
secure: boolean;
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
remove(key: string): Promise<void>;
keys?(): Promise<string[]>;
}
Secrets that must not sit in plain storage: auth tokens, API keys, refresh tokens.
Platform packages register a SecureStorageDriver backed by the Keychain, the Android Keystore
or Electron's safeStorage; on the web the fallback encrypts values with AES-GCM under a key
kept in IndexedDB (non-extractable), so a copied localStorage entry is useless on its own.
kitr/secure-storage/react
useSecureItem
function useSecureItem<T>(define: (storage: SecureStorage) => SecureItem<T>): [
T | undefined,
(value: T | undefined) => Promise<void>,
{
loading: boolean;
secure: boolean;
}
];
Read and write one secret: const [session, setSession, { loading }] = useSecureItem(sessionItem).
The value is undefined until the vault answers (and always on the server).
useSecureStorage
function useSecureStorage(): SecureStorage;
kitr/secure-storage/provider
secureStorageProvider
const secureStorageProvider: import("@kitr/core").KitrProvider;
Binds secureStorage to the platform vault (registered by the mobile/desktop providers under
the secure-storage capability) or the encrypted web fallback. On the server it is an in-memory
store, so code that runs in both places needs no branches.