diff --git a/src/app/session.ts b/src/app/session.ts index 5df74ab9..f5aff720 100644 --- a/src/app/session.ts +++ b/src/app/session.ts @@ -5,7 +5,7 @@ import type {Session} from "@welshman/app" import {getTestEvents, maybeGetTestSession} from "@lib/test/session" import {app, login, session} from "@app/core" import {wallet} from "@app/lightning" -import {kv, ss, storage} from "@app/storage" +import {hasCachedIdentity, kv, ss, storage} from "@app/storage" import {deactivateCurrentPomadeSession} from "@app/pomade" import {Push} from "@app/push" @@ -58,11 +58,24 @@ const readLegacySession = async () => { return legacy ? toCurrentSession(legacy) : undefined } +const readStoredSession = async () => { + const session = (await ss.get("session")) ?? (await readLegacySession()) + + // A nip01 secret is the account rather than a credential, and a local signer can't stall startup. + if (session && session.method !== "nip01" && !(await hasCachedIdentity())) { + await ss.set("session", undefined) + + return undefined + } + + return session +} + // The session derives from the app's user, so read it back at startup and persist it on a change. export const restoreSession = async () => { // Test-only: an injected window.__TEST_SESSION__ wins over storage and is stripped from production. const testSession = import.meta.env.DEV ? maybeGetTestSession() : undefined - const $session = testSession ?? (await ss.get("session")) ?? (await readLegacySession()) + const $session = testSession ?? (await readStoredSession()) if ($session) { await login($session) diff --git a/src/app/storage.ts b/src/app/storage.ts index f44cb762..ff620340 100644 --- a/src/app/storage.ts +++ b/src/app/storage.ts @@ -206,6 +206,14 @@ const isExpired = (item: EventItem) => type PlaintextItem = {key: string; value: string} +const DATABASE_PREFIX = "flotilla-9gl" + +// indexeddb goes with the app container on every platform; the iOS keychain the session lives in does not. +export const hasCachedIdentity = async () => + typeof indexedDB.databases === "function" + ? (await indexedDB.databases()).some(db => db.name?.startsWith(DATABASE_PREFIX)) + : true + /** Caches an app's repository, tracker and local collections in indexeddb, one database per identity. */ class Storage { ready: Promise @@ -220,10 +228,10 @@ class Storage { constructor(private readonly app: IApp) { // Every identity used to share one database; drop it rather than leave it on disk. - void deleteDB("flotilla-9gl") + void deleteDB(DATABASE_PREFIX) this.pubkey = User.require(app).pubkey - this.db = new IDB({name: `flotilla-9gl-${this.pubkey}`, stores: TABLES}) + this.db = new IDB({name: `${DATABASE_PREFIX}-${this.pubkey}`, stores: TABLES}) this.ready = this.start() }