secure-storage

Keep login tokens and secrets in the phone or computer's secure vault instead of plain storage.

Device featuresClient Server

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.