Persist NIP-07 permissions cache to storage

This commit is contained in:
thePR0M3TH3AN committed 2025-10-20 09:11:38 -04:00
1 parent 26c6ea363f
commit c85567468f
3 files changed
+118 -23

No files matched your search

+65 -22
View File
@@ -62,6 +62,7 @@ const EVENTS_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
const NIP07_LOGIN_TIMEOUT_MS = 60_000; // 60 seconds
const NIP07_LOGIN_TIMEOUT_ERROR_MESSAGE =
"Timed out waiting for the NIP-07 extension. Confirm the extension prompt in your browser toolbar and try again.";
const NIP07_PERMISSIONS_STORAGE_KEY = "bitvid:nip07:permissions";
// Give the NIP-07 extension enough time to surface its approval prompt and let
// users unlock/authorize it. Seven seconds proved too aggressive once vendors
@@ -88,20 +89,38 @@ function normalizePermissionMethod(method) {
return typeof method === "string" && method.trim() ? method.trim() : "";
}
function readStoredNip07Permissions() {
if (typeof localStorage === "undefined" || !localStorage) {
return [];
function getNip07PermissionStorage() {
const scope =
typeof globalThis !== "undefined" && globalThis ? globalThis : undefined;
const browserWindow =
typeof window !== "undefined" && window ? window : undefined;
if (browserWindow?.localStorage) {
return browserWindow.localStorage;
}
let rawValue;
if (scope?.localStorage) {
return scope.localStorage;
}
return null;
}
function readStoredNip07Permissions() {
const storage = getNip07PermissionStorage();
if (!storage) {
return new Set();
}
let rawValue = null;
try {
rawValue = localStorage.getItem(NIP07_PERMISSIONS_STORAGE_KEY);
rawValue = storage.getItem(NIP07_PERMISSIONS_STORAGE_KEY);
} catch (error) {
return [];
return new Set();
}
if (!rawValue) {
return [];
return new Set();
}
try {
@@ -113,21 +132,20 @@ function readStoredNip07Permissions() {
: Array.isArray(parsed?.methods)
? parsed.methods
: [];
return storedMethods
.map((method) => normalizePermissionMethod(method))
.filter(Boolean);
return new Set(
storedMethods
.map((method) => normalizePermissionMethod(method))
.filter(Boolean),
);
} catch (error) {
try {
localStorage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
} catch (cleanupError) {
// ignore cleanup errors
}
return [];
clearStoredNip07Permissions();
return new Set();
}
}
function writeStoredNip07Permissions(methods) {
if (typeof localStorage === "undefined" || !localStorage) {
const storage = getNip07PermissionStorage();
if (!storage) {
return;
}
@@ -141,11 +159,11 @@ function writeStoredNip07Permissions(methods) {
try {
if (!normalized.length) {
localStorage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
storage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
return;
}
localStorage.setItem(
storage.setItem(
NIP07_PERMISSIONS_STORAGE_KEY,
JSON.stringify({ grantedMethods: normalized }),
);
@@ -153,6 +171,19 @@ function writeStoredNip07Permissions(methods) {
// ignore persistence failures
}
}
function clearStoredNip07Permissions() {
const storage = getNip07PermissionStorage();
if (!storage) {
return;
}
try {
storage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
} catch (error) {
// ignore cleanup issues
}
}
const SESSION_ACTOR_STORAGE_KEY = "bitvid:sessionActor:v1";
const VIEW_EVENT_GUARD_PREFIX = "bitvid:viewed";
const REBROADCAST_GUARD_PREFIX = "bitvid:rebroadcast:v1";
@@ -163,7 +194,6 @@ const WATCH_HISTORY_REPUBLISH_BASE_DELAY_MS = 2000;
const WATCH_HISTORY_REPUBLISH_MAX_DELAY_MS = 5 * 60 * 1000;
const WATCH_HISTORY_REPUBLISH_MAX_ATTEMPTS = 8;
const WATCH_HISTORY_REPUBLISH_JITTER = 0.25;
const NIP07_PERMISSIONS_STORAGE_KEY = "bitvid:nip07:permissions";
export const DEFAULT_NIP07_PERMISSION_METHODS = Object.freeze([
// Core auth + relay metadata
@@ -3141,7 +3171,9 @@ export class NostrClient {
this.watchHistoryLastCreatedAt = 0;
this.countRequestCounter = 0;
this.countUnsupportedRelays = new Set();
this.extensionPermissionCache = new Set(readStoredNip07Permissions());
const storedPermissions = readStoredNip07Permissions();
this.extensionPermissionCache =
storedPermissions instanceof Set ? storedPermissions : new Set();
}
markExtensionPermissions(methods = []) {
@@ -3166,7 +3198,11 @@ export class NostrClient {
}
if (didChange) {
writeStoredNip07Permissions(this.extensionPermissionCache);
try {
writeStoredNip07Permissions(this.extensionPermissionCache);
} catch (error) {
// Ignore storage persistence issues in non-browser environments
}
}
}
@@ -6022,6 +6058,13 @@ export class NostrClient {
this.watchHistoryRefreshPromises.clear();
this.watchHistoryLastCreatedAt = 0;
this.watchHistoryStorage = null;
if (
this.extensionPermissionCache &&
typeof this.extensionPermissionCache.clear === "function"
) {
this.extensionPermissionCache.clear();
}
clearStoredNip07Permissions();
devLogger.log("User logged out.");
}
+2
View File
@@ -78,6 +78,7 @@ function setupLoginEnvironment({ enableImpl, getPublicKey = HEX_PUBKEY } = {}) {
) {
nostrClient.extensionPermissionCache.clear();
}
clearStoredPermissions();
return {
enableCalls,
@@ -123,6 +124,7 @@ function setupLoginEnvironment({ enableImpl, getPublicKey = HEX_PUBKEY } = {}) {
) {
nostrClient.extensionPermissionCache.clear();
}
clearStoredPermissions();
},
};
}
+51 -1
View File
@@ -69,9 +69,54 @@ const originalExtensionPermissionSnapshot = Array.isArray(
originalExtensionPermissionCache,
)
? Array.from(originalExtensionPermissionCache)
: [];
: originalExtensionPermissionCache instanceof Set
? Array.from(originalExtensionPermissionCache)
: [];
const NIP07_PERMISSIONS_STORAGE_KEY = "bitvid:nip07:permissions";
function clearStoredExtensionPermissions() {
if (typeof localStorage === "undefined" || !localStorage) {
return;
}
try {
localStorage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
} catch (error) {
// ignore cleanup errors in tests
}
}
function writeStoredExtensionPermissions(methods = []) {
if (typeof localStorage === "undefined" || !localStorage) {
return;
}
const normalized = Array.from(
new Set(
Array.from(methods)
.filter((method) => typeof method === "string")
.map((method) => method.trim())
.filter(Boolean),
),
);
try {
if (!normalized.length) {
localStorage.removeItem(NIP07_PERMISSIONS_STORAGE_KEY);
return;
}
localStorage.setItem(
NIP07_PERMISSIONS_STORAGE_KEY,
JSON.stringify({ grantedMethods: normalized }),
);
} catch (error) {
// ignore persistence errors in tests
}
}
nostrClient.extensionPermissionCache = new Set();
clearStoredExtensionPermissions();
nostrClient.watchHistoryCache = new Map();
nostrClient.watchHistoryStorage = {
@@ -2013,6 +2058,11 @@ if (originalExtensionPermissionCache instanceof Set) {
}
}
nostrClient.extensionPermissionCache = originalExtensionPermissionCache;
if (originalExtensionPermissionSnapshot.length) {
writeStoredExtensionPermissions(originalExtensionPermissionSnapshot);
} else {
clearStoredExtensionPermissions();
}
if (!originalFlag) {
setWatchHistoryV2Enabled(false);