From 27d35f47efb735d4d678ba95e79eea59a6a36ee2 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 9 Mar 2026 00:13:37 +0100 Subject: [PATCH] fixes --- shared/src/wallet/database/wallet-db.ts | 66 +++++++++++-------------- web/src/wallet/wallet-service.ts | 14 +++--- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/shared/src/wallet/database/wallet-db.ts b/shared/src/wallet/database/wallet-db.ts index c9889c3..711592c 100644 --- a/shared/src/wallet/database/wallet-db.ts +++ b/shared/src/wallet/database/wallet-db.ts @@ -587,12 +587,10 @@ export class WalletDB { */ async revokeAllCapabilities(appId: string): Promise { const keysToDelete: string[] = []; + const prefix = `${appId}:`; - // Collect all keys for this app - for await (const [key, _] of this.authorizations.entriesAsync()) { - if (key.startsWith(`${appId}:`)) { - keysToDelete.push(key); - } + for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + keysToDelete.push(key); } // Delete all keys @@ -616,13 +614,12 @@ export class WalletDB { appId: string, ): Promise { // Collect all authorization keys for this app (excluding metadata) + const prefix = `${appId}:`; const keys: string[] = []; - for await (const [key, _] of this.authorizations.entriesAsync()) { - if (key.startsWith(`${appId}:`)) { - const storageKey = key.substring(appId.length + 1); - if (!storageKey.startsWith("__")) { - keys.push(storageKey); - } + for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + const storageKey = key.substring(prefix.length); + if (!storageKey.startsWith("__")) { + keys.push(storageKey); } } @@ -905,7 +902,7 @@ export class WalletDB { async listAuthorizedApps(): Promise { const appIds = new Set(); const MARKERS = [":__requested__", ":__behavior__"]; - for await (const [key, _] of this.authorizations.entriesAsync()) { + for await (const key of this.authorizations.keysAsync()) { for (const marker of MARKERS) { if (key.endsWith(marker)) { appIds.add(key.slice(0, -marker.length)); @@ -959,10 +956,8 @@ export class WalletDB { async revokeAppAuthorizations(appId: string) { const prefix = `${appId}:`; const keysToDelete: string[] = []; - for await (const [key, _] of this.authorizations.entriesAsync()) { - if (key.startsWith(prefix)) { - keysToDelete.push(key); - } + for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + keysToDelete.push(key); } for (const key of keysToDelete) { @@ -1062,11 +1057,8 @@ export class WalletDB { async getAllAuthorizationKeys(appId: string): Promise { const prefix = `${appId}:`; const keys: string[] = []; - for await (const [key] of this.authorizations.entriesAsync()) { - const keyStr = key.toString(); - if (keyStr.startsWith(prefix)) { - keys.push(keyStr.substring(prefix.length)); // Remove appId prefix - } + for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + keys.push(key.substring(prefix.length)); } return keys; } @@ -1149,15 +1141,12 @@ export class WalletDB { const prefix = `${appId}:`; const entries: Record = {}; - for await (const [key, value] of this.authorizations.entriesAsync()) { - if (key.startsWith(prefix)) { - const storageKey = key.substring(prefix.length); - try { - entries[storageKey] = JSON.parse(value.toString()); - } catch { - // If not valid JSON, store as raw string - entries[storageKey] = value.toString(); - } + for await (const [key, value] of this.authorizations.entriesAsync({ start: prefix, end: `${prefix}\uffff` })) { + const storageKey = key.substring(prefix.length); + try { + entries[storageKey] = JSON.parse(value.toString()); + } catch { + entries[storageKey] = value.toString(); } } @@ -1182,16 +1171,21 @@ export class WalletDB { for (const { appId, entries } of apps) { for (const [storageKey, value] of Object.entries(entries)) { const fullKey = `${appId}:${storageKey}`; - await this.authorizations.set( - fullKey, - Buffer.from(jsonStringify(value)), - ); - imported++; + // Only import keys that don't already exist locally. + // This prevents stale cookie data from overwriting local revocations. + const existing = await this.authorizations.getAsync(fullKey); + if (!existing) { + await this.authorizations.set( + fullKey, + Buffer.from(jsonStringify(value)), + ); + imported++; + } } } this.logger.info( - `Imported ${imported} authorization entries for ${apps.length} app(s)`, + `Imported ${imported} new authorization entries for ${apps.length} app(s)`, ); return imported; } diff --git a/web/src/wallet/wallet-service.ts b/web/src/wallet/wallet-service.ts index 2632da0..6353dd6 100644 --- a/web/src/wallet/wallet-service.ts +++ b/web/src/wallet/wallet-service.ts @@ -80,14 +80,13 @@ function scheduleCookieSync(db: WalletDB): void { _cookieSyncTimer = setTimeout(async () => { _cookieSyncTimer = null; if (_cookieSyncDb && _cookiePassphrase) { - // Accounts and contacts are standalone-only (source of truth). if (!IS_IFRAME) { + // Accounts and contacts are standalone-only (source of truth). await syncAccountsToCookie(_cookieSyncDb); await syncContactsToCookie(_cookieSyncDb); } - // Capabilities sync bidirectionally — grants made in the iframe - // (via requestCapabilities from the dApp) must propagate back to - // the standalone wallet's Authorized Apps view. + // Capabilities sync bidirectionally — both standalone and iframe + // write the cookie after every modification. await syncCapabilitiesToCookie(_cookieSyncDb); } }, 500); @@ -192,9 +191,8 @@ export async function getOrCreateSession( >(); if (_cookiePassphrase) { - // Capabilities sync bidirectionally: import from cookies first so - // grants made in the iframe are merged into WalletDB, then export - // the combined state back. + // Import capabilities from cookie first (additive-only: won't overwrite + // local state, only adds entries missing locally). await bootstrapCapabilitiesFromCookie(db); if (!IS_IFRAME) { @@ -202,7 +200,7 @@ export async function getOrCreateSession( await syncAccountsToCookie(db); await syncContactsToCookie(db); } - // Re-export the merged capabilities (includes both local + cookie grants). + // Export capabilities to cookie (includes both local + newly imported). await syncCapabilitiesToCookie(db); }