Files
eranos/src/lib/secureStorage.ts
T
Alex Gleason 0d3b8ed23d Harden CSS/URL handling, NWC storage, and Android backup
- Sanitize event-sourced URLs before CSS url() interpolation in
  ProfileCard banner and letter stationery background (closes H-1, H-2)
- Sanitize event-sourced font families at the parse layer and in letter
  card/detail consumers that bypass resolveStationery (closes M-6)
- Export sanitizeCssString for broader reuse
- Route NWC wallet connection URIs and active pointer through a new
  useSecureLocalStorage hook, storing in iOS Keychain / Android KeyStore
  on native (closes M-1)
- Add removeItem to secureStorage
- Add Android backup/data-extraction rules that exclude WebView storage
  and Capacitor secure-storage SharedPreferences so wallet credentials
  don't leak via Google Auto Backup (closes M-5)
- Document that GOOGLE_PLAY_SERVICE_ACCOUNT_JSON must be base64-encoded
  to match what the CI job expects (closes M-2)
2026-04-16 14:20:26 -05:00

58 lines
1.7 KiB
TypeScript

import { Capacitor } from '@capacitor/core';
import { SecureStoragePlugin } from 'capacitor-secure-storage-plugin';
/**
* Storage adapter that uses native secure storage (iOS Keychain / Android KeyStore)
* on Capacitor builds and falls back to localStorage on web.
*
* Implements the `NLoginStorage` interface from @nostrify/react.
*
* On the first native read, if the key is not found in secure storage but exists
* in localStorage, it is automatically migrated to secure storage and the
* plaintext localStorage copy is removed.
*/
export const secureStorage = {
async getItem(key: string): Promise<string | null> {
if (!Capacitor.isNativePlatform()) {
return localStorage.getItem(key);
}
try {
const { value } = await SecureStoragePlugin.get({ key });
return value;
} catch {
// Key not found in secure storage — check localStorage for migration.
const legacy = localStorage.getItem(key);
if (legacy !== null) {
// Migrate to secure storage and remove the plaintext copy.
await SecureStoragePlugin.set({ key, value: legacy });
localStorage.removeItem(key);
return legacy;
}
return null;
}
},
async setItem(key: string, value: string): Promise<void> {
if (!Capacitor.isNativePlatform()) {
localStorage.setItem(key, value);
return;
}
await SecureStoragePlugin.set({ key, value });
},
async removeItem(key: string): Promise<void> {
if (!Capacitor.isNativePlatform()) {
localStorage.removeItem(key);
return;
}
try {
await SecureStoragePlugin.remove({ key });
} catch {
// Key didn't exist — ignore.
}
},
};