diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index b711150d..da6f2661 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -9,6 +9,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { + implementation project(':aparajita-capacitor-secure-storage') implementation project(':capacitor-community-safe-area') implementation project(':capacitor-app') implementation project(':capacitor-filesystem') diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 4a050d9f..317412b2 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -2,6 +2,9 @@ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.0.1_@capacitor+core@8.0.1/node_modules/@capacitor/android/capacitor') +include ':aparajita-capacitor-secure-storage' +project(':aparajita-capacitor-secure-storage').projectDir = new File('../node_modules/.pnpm/@aparajita+capacitor-secure-storage@8.0.0/node_modules/@aparajita/capacitor-secure-storage/android') + include ':capacitor-community-safe-area' project(':capacitor-community-safe-area').projectDir = new File('../node_modules/.pnpm/@capacitor-community+safe-area@8.0.1_@capacitor+core@8.0.1/node_modules/@capacitor-community/safe-area/android') diff --git a/ios/App/Podfile b/ios/App/Podfile index a9cd6181..549e20cf 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -11,6 +11,7 @@ install! 'cocoapods', :disable_input_output_paths => true def capacitor_pods pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@8.0.1_@capacitor+core@8.0.1/node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@8.0.1_@capacitor+core@8.0.1/node_modules/@capacitor/ios' + pod 'AparajitaCapacitorSecureStorage', :path => '../../node_modules/.pnpm/@aparajita+capacitor-secure-storage@8.0.0/node_modules/@aparajita/capacitor-secure-storage' pod 'CapacitorCommunitySafeArea', :path => '../../node_modules/.pnpm/@capacitor-community+safe-area@8.0.1_@capacitor+core@8.0.1/node_modules/@capacitor-community/safe-area' pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@8.0.0_@capacitor+core@8.0.1/node_modules/@capacitor/app' pod 'CapacitorFilesystem', :path => '../../node_modules/.pnpm/@capacitor+filesystem@8.1.0_@capacitor+core@8.0.1/node_modules/@capacitor/filesystem' diff --git a/package.json b/package.json index d2369b2d..391f4ef6 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ }, "type": "module", "dependencies": { + "@aparajita/capacitor-secure-storage": "^8.0.0", "@capacitor-community/safe-area": "^8.0.1", "@capacitor/android": "^8.0.1", "@capacitor/app": "^8.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4bd063e0..9a8ef707 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: .: dependencies: + '@aparajita/capacitor-secure-storage': + specifier: ^8.0.0 + version: 8.0.0 '@capacitor-community/safe-area': specifier: ^8.0.1 version: 8.0.1(@capacitor/core@8.0.1) @@ -232,6 +235,10 @@ packages: '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + '@aparajita/capacitor-secure-storage@8.0.0': + resolution: {integrity: sha512-oYnwSjdIh23aRNgz8982+TmFvQH/2yZkEdw1iIg+H2ziFJoOVELPTc7u6Ez2HwOuDIW5AGqBX75GvrzQ+D70Qg==} + engines: {node: '>=20.0.0'} + '@apideck/better-ajv-errors@0.3.6': resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} engines: {node: '>=10'} @@ -756,6 +763,11 @@ packages: peerDependencies: '@capacitor/core': ^8.0.0 + '@capacitor/android@8.2.0': + resolution: {integrity: sha512-XLm5OsWLPfXQxDxzFS7SOdMEgGvW+2c7TGLXkTR2cSKdkWK5Abns4imlT5qghKYhjM9r74IrDkBWg/9ALUGNKQ==} + peerDependencies: + '@capacitor/core': ^8.2.0 + '@capacitor/app@8.0.0': resolution: {integrity: sha512-OwzIkUs4w433Bu9WWAEbEYngXEfJXZ9Wmdb8eoaqzYBgB0W9/3Ed/mh6sAYPNBAZlpyarmewgP7Nb+d3Vrh+xA==} peerDependencies: @@ -779,6 +791,9 @@ packages: '@capacitor/core@8.0.1': resolution: {integrity: sha512-5UqSWxGMp/B8KhYu7rAijqNtYslhcLh+TrbfU48PfdMDsPfaU/VY48sMNzC22xL8BmoFoql/3SKyP+pavTOvOA==} + '@capacitor/core@8.2.0': + resolution: {integrity: sha512-oKaoNeNtH2iIZMDFVrb1atoyRECDGHcfLMunJ5KWN8DtvpVBeeA4c41e20NTuhMxw1cSYbpq2PV2hb+/9CJxlQ==} + '@capacitor/filesystem@8.1.0': resolution: {integrity: sha512-AfawIxQ8xBmKsEn/vEpgurGQB9+hFXRtwEiCXR+SSS0MkTw4bJrvLGnloZ/PblegYefvnay1q079Yz3PQ6y1dA==} peerDependencies: @@ -789,6 +804,11 @@ packages: peerDependencies: '@capacitor/core': ^8.0.0 + '@capacitor/ios@8.2.0': + resolution: {integrity: sha512-X2/VtM4qP/R1SM0VQ5W/VotEc6PS/KTooD33EijsfAHWBdee+xmBapW8SeNLnu16wJ+tsfWlvtipaJEyfKbRKQ==} + peerDependencies: + '@capacitor/core': ^8.2.0 + '@capacitor/keyboard@8.0.0': resolution: {integrity: sha512-ycPW6iQyFwzDK95jihesj5EGiyyGSfbBqNek11iNp9tBOB7zDeYkUA2S/vPpOETt3dhP6pWr7a9gNVGuEfj11g==} peerDependencies: @@ -5108,6 +5128,14 @@ snapshots: '@antfu/utils@0.7.10': {} + '@aparajita/capacitor-secure-storage@8.0.0': + dependencies: + '@capacitor/android': 8.2.0(@capacitor/core@8.2.0) + '@capacitor/app': 8.0.0(@capacitor/core@8.2.0) + '@capacitor/core': 8.2.0 + '@capacitor/ios': 8.2.0(@capacitor/core@8.2.0) + '@capacitor/keyboard': 8.0.0(@capacitor/core@8.2.0) + '@apideck/better-ajv-errors@0.3.6(ajv@8.18.0)': dependencies: ajv: 8.18.0 @@ -5789,10 +5817,18 @@ snapshots: dependencies: '@capacitor/core': 8.0.1 + '@capacitor/android@8.2.0(@capacitor/core@8.2.0)': + dependencies: + '@capacitor/core': 8.2.0 + '@capacitor/app@8.0.0(@capacitor/core@8.0.1)': dependencies: '@capacitor/core': 8.0.1 + '@capacitor/app@8.0.0(@capacitor/core@8.2.0)': + dependencies: + '@capacitor/core': 8.2.0 + '@capacitor/assets@3.0.5(@types/node@25.0.10)(typescript@5.9.3)': dependencies: '@capacitor/cli': 5.7.8 @@ -5863,6 +5899,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@capacitor/core@8.2.0': + dependencies: + tslib: 2.8.1 + '@capacitor/filesystem@8.1.0(@capacitor/core@8.0.1)': dependencies: '@capacitor/core': 8.0.1 @@ -5872,10 +5912,18 @@ snapshots: dependencies: '@capacitor/core': 8.0.1 + '@capacitor/ios@8.2.0(@capacitor/core@8.2.0)': + dependencies: + '@capacitor/core': 8.2.0 + '@capacitor/keyboard@8.0.0(@capacitor/core@8.0.1)': dependencies: '@capacitor/core': 8.0.1 + '@capacitor/keyboard@8.0.0(@capacitor/core@8.2.0)': + dependencies: + '@capacitor/core': 8.2.0 + '@capacitor/preferences@8.0.0(@capacitor/core@8.0.1)': dependencies: '@capacitor/core': 8.0.1 diff --git a/src/app/core/storage.ts b/src/app/core/storage.ts index b05be817..32e05969 100644 --- a/src/app/core/storage.ts +++ b/src/app/core/storage.ts @@ -1,4 +1,5 @@ import {call} from "@welshman/lib" +import {SecureStorage} from "@aparajita/capacitor-secure-storage" import {Preferences} from "@capacitor/preferences" import {IDB} from "@lib/indexeddb" @@ -30,6 +31,46 @@ export const kv = call(() => { return {get, set, clear} }) +export const ss = call(() => { + let p = Promise.resolve() + + const get = async (key: string): Promise => { + let value = await SecureStorage.getItem(key) + + if (!value) { + const legacy = await Preferences.get({key}) + + if (legacy.value) { + value = legacy.value + await SecureStorage.setItem(key, legacy.value) + await Preferences.remove({key}) + } + } + + if (!value) return undefined + + try { + return JSON.parse(value) + } catch (e) { + return undefined + } + } + + const set = async (key: string, value: T): Promise => { + p = p.then(() => SecureStorage.setItem(key, JSON.stringify(value))) + + await p + } + + const clear = async () => { + p = p.then(() => SecureStorage.clear()) + + await p + } + + return {get, set, clear} +}) + export const db = new IDB({ name: "flotilla-9gl", version: 1, diff --git a/src/app/util/logout.ts b/src/app/util/logout.ts index 23a3f9ac..cef124a7 100644 --- a/src/app/util/logout.ts +++ b/src/app/util/logout.ts @@ -1,4 +1,4 @@ -import {kv, db} from "@app/core/storage" +import {db, kv, ss} from "@app/core/storage" import {Push} from "@app/util/notifications" import {deactivateCurrentPomadeSession} from "@app/util/pomade" @@ -6,6 +6,7 @@ export const logout = async () => { await deactivateCurrentPomadeSession() await Push.disable() await kv.clear() + await ss.clear() await db.clear() localStorage.clear() diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 57311b28..ea0e1e2b 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -27,7 +27,7 @@ import {setupHistory} from "@app/util/history" import {setupAnalytics} from "@app/util/analytics" import {authPolicy, blockPolicy, trustPolicy, mostlyRestrictedPolicy} from "@app/util/policies" - import {kv, db} from "@app/core/storage" + import {db, kv, ss} from "@app/core/storage" import {device, userSettingsValues, notificationSettings, pushState} from "@app/core/state" import {syncApplicationData} from "@app/core/sync" import * as commands from "@app/core/commands" @@ -96,7 +96,7 @@ const unsubscribe = call(async () => { const unsubscribers: Unsubscriber[] = [] - // Sync stuff to localstorage + // Sync stuff to storage await Promise.all([ sync({ key: "device", @@ -111,7 +111,7 @@ sync({ key: "sessions", store: sessions, - storage: kv, + storage: ss, }), sync({ key: "shouldUnwrap",