pediatric-ai-scribe-v3/public/js/secureStorage.js
Daniel c736782c15 Add hardware-backed secure storage for mobile auth token
Web still uses localStorage; Capacitor native app now routes
token/user/session-id through capacitor-secure-storage-plugin
(iOS Keychain, Android EncryptedSharedPreferences / Keystore).

A thin SecureStorage wrapper detects Capacitor at runtime and
falls back to localStorage elsewhere, keeping a single auth.js
codebase for both targets.

To activate on mobile: cd mobile && npm install && npx cap sync android
2026-04-14 02:33:32 +02:00

73 lines
2.5 KiB
JavaScript

// ============================================================
// SECURE STORAGE WRAPPER
// Browser: localStorage
// Capacitor mobile: iOS Keychain / Android EncryptedSharedPreferences
// via capacitor-secure-storage-plugin
// ============================================================
(function() {
var memCache = {};
function isNative() {
try {
return !!(window.Capacitor && typeof window.Capacitor.isNativePlatform === 'function' && window.Capacitor.isNativePlatform());
} catch(e) { return false; }
}
function plugin() {
try {
var p = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SecureStoragePlugin;
return p || null;
} catch(e) { return null; }
}
window.SecureStorage = {
isNative: isNative,
get: function(key) {
if (isNative() && plugin()) {
return plugin().get({ key: key })
.then(function(r) { memCache[key] = r.value; return r.value; })
.catch(function() { return null; });
}
try { return Promise.resolve(localStorage.getItem(key)); } catch(e) { return Promise.resolve(null); }
},
set: function(key, value) {
memCache[key] = value;
if (isNative() && plugin()) {
return plugin().set({ key: key, value: value }).catch(function() {});
}
try { localStorage.setItem(key, value); } catch(e) {}
return Promise.resolve();
},
remove: function(key) {
delete memCache[key];
if (isNative() && plugin()) {
var p = plugin().remove({ key: key }).catch(function() {});
// Also clear legacy localStorage copy if any
try { localStorage.removeItem(key); } catch(e) {}
return p;
}
try { localStorage.removeItem(key); } catch(e) {}
return Promise.resolve();
},
// Synchronous read for hot paths (fetch headers). On native returns cached
// value populated by prior get/set. On web reads localStorage directly.
getSync: function(key) {
if (isNative()) return memCache[key] || null;
try { return localStorage.getItem(key); } catch(e) { return null; }
},
// Hydrate the memory cache from native storage at boot. Resolves when ready.
hydrate: function(keys) {
if (!isNative() || !plugin()) return Promise.resolve();
return Promise.all(keys.map(function(k) {
return plugin().get({ key: k })
.then(function(r) { memCache[k] = r.value; })
.catch(function() { memCache[k] = null; });
}));
}
};
})();