mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Add shared NIP-07 permission gate
This commit is contained in:
1 parent
16f4b6b9fa
commit
cf9082b83f
10 files changed
+185
-67
No files matched your search
+57
-22
@@ -395,6 +395,16 @@ function shouldRequestExtensionPermissions(signer) {
|
||||
return signer.type === "extension";
|
||||
}
|
||||
|
||||
function normalizePermissionMethods(methods) {
|
||||
if (!Array.isArray(methods)) {
|
||||
return [];
|
||||
}
|
||||
const normalized = methods
|
||||
.map((method) => normalizePermissionMethod(method))
|
||||
.filter(Boolean);
|
||||
return Array.from(new Set(normalized));
|
||||
}
|
||||
|
||||
const EVENTS_CACHE_STORAGE_KEY = "bitvid:eventsCache:v1";
|
||||
// We use the policy TTL, but currently the storage backend is hardcoded to IDB (with localStorage fallback).
|
||||
// Future refactors should make EventsCacheStore dynamic based on CACHE_POLICIES[NOTE_TYPES.VIDEO_POST].storage.
|
||||
@@ -1156,7 +1166,9 @@ export class NostrClient {
|
||||
getActivePubkey: () => this.pubkey,
|
||||
getSessionActor: () => this.sessionActor,
|
||||
ensureSessionActor: () => this.ensureSessionActor(),
|
||||
ensureExtensionPermissions: (...args) => this.ensureExtensionPermissions(...args),
|
||||
ensureExtensionPermissions: (...args) => this.ensureExtensionPermissionsGate(...args),
|
||||
hasRequiredExtensionPermissions: (...args) =>
|
||||
this.hasRequiredExtensionPermissions(...args),
|
||||
resolveActiveSigner,
|
||||
shouldRequestExtensionPermissions,
|
||||
signEventWithPrivateKey,
|
||||
@@ -1266,6 +1278,7 @@ export class NostrClient {
|
||||
this.pendingHandshakeCancel = null;
|
||||
this.remoteSignerListeners = new Set();
|
||||
this.sessionActorListeners = new Set();
|
||||
this.extensionPermissionRequestPromise = null;
|
||||
const storedRemoteSigner = this.getStoredNip46Metadata();
|
||||
this.remoteSignerStatus = {
|
||||
state: storedRemoteSigner.hasSession ? "stored" : "idle",
|
||||
@@ -2776,16 +2789,8 @@ export class NostrClient {
|
||||
this.extensionPermissionCache = new Set();
|
||||
}
|
||||
|
||||
if (!Array.isArray(methods)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let didChange = false;
|
||||
for (const method of methods) {
|
||||
const normalized = normalizePermissionMethod(method);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
for (const normalized of normalizePermissionMethods(methods)) {
|
||||
if (!this.extensionPermissionCache.has(normalized)) {
|
||||
didChange = true;
|
||||
}
|
||||
@@ -2801,20 +2806,50 @@ export class NostrClient {
|
||||
}
|
||||
}
|
||||
|
||||
async ensureExtensionPermissions(methods = DEFAULT_NIP07_PERMISSION_METHODS) {
|
||||
if (!Array.isArray(methods)) {
|
||||
methods = [];
|
||||
hasRequiredExtensionPermissions(
|
||||
methods = DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
) {
|
||||
const normalized = normalizePermissionMethods(methods);
|
||||
if (!normalized.length) {
|
||||
return true;
|
||||
}
|
||||
if (!this.extensionPermissionCache) {
|
||||
return false;
|
||||
}
|
||||
return normalized.every((method) => this.extensionPermissionCache.has(method));
|
||||
}
|
||||
|
||||
async ensureExtensionPermissionsGate(
|
||||
methods = DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
) {
|
||||
const normalized = normalizePermissionMethods(methods);
|
||||
if (!normalized.length) {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
const normalized = Array.from(
|
||||
new Set(
|
||||
methods
|
||||
.map((method) =>
|
||||
typeof method === "string" && method.trim() ? method.trim() : "",
|
||||
)
|
||||
.filter(Boolean),
|
||||
),
|
||||
);
|
||||
if (this.hasRequiredExtensionPermissions(normalized)) {
|
||||
return { ok: true, cached: true };
|
||||
}
|
||||
|
||||
if (this.extensionPermissionRequestPromise) {
|
||||
return this.extensionPermissionRequestPromise;
|
||||
}
|
||||
|
||||
const requestPromise = this.ensureExtensionPermissions(normalized)
|
||||
.catch((error) => ({ ok: false, error }))
|
||||
.finally(() => {
|
||||
if (this.extensionPermissionRequestPromise === requestPromise) {
|
||||
this.extensionPermissionRequestPromise = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.extensionPermissionRequestPromise = requestPromise;
|
||||
|
||||
return requestPromise;
|
||||
}
|
||||
|
||||
async ensureExtensionPermissions(methods = DEFAULT_NIP07_PERMISSION_METHODS) {
|
||||
const normalized = normalizePermissionMethods(methods);
|
||||
|
||||
if (!normalized.length) {
|
||||
return { ok: true };
|
||||
|
||||
@@ -1213,10 +1213,21 @@ export async function publishComment(
|
||||
? shouldRequestExtensionPermissions(signer)
|
||||
: false;
|
||||
|
||||
if (shouldRequestPermissions) {
|
||||
permissionResult = await client.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
const hasCachedPermissions =
|
||||
typeof client?.hasRequiredExtensionPermissions === "function" &&
|
||||
client.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
|
||||
if (shouldRequestPermissions && !hasCachedPermissions) {
|
||||
const permissionGate =
|
||||
typeof client?.ensureExtensionPermissionsGate === "function"
|
||||
? client.ensureExtensionPermissionsGate.bind(client)
|
||||
: typeof client?.ensureExtensionPermissions === "function"
|
||||
? client.ensureExtensionPermissions.bind(client)
|
||||
: null;
|
||||
|
||||
if (permissionGate) {
|
||||
permissionResult = await permissionGate(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissionResult.ok) {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const nostrClient = new NostrClient();
|
||||
registerNostrClient(nostrClient, {
|
||||
requestPermissions: (
|
||||
methods = DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
) => nostrClient.ensureExtensionPermissions(methods),
|
||||
) => nostrClient.ensureExtensionPermissionsGate(methods),
|
||||
});
|
||||
|
||||
export function requestDefaultExtensionPermissions(
|
||||
@@ -24,6 +24,12 @@ export function requestDefaultExtensionPermissions(
|
||||
return requestRegisteredPermissions(methods);
|
||||
}
|
||||
|
||||
export function hasDefaultExtensionPermissions(
|
||||
methods = DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
) {
|
||||
return nostrClient.hasRequiredExtensionPermissions(methods);
|
||||
}
|
||||
|
||||
export {
|
||||
NostrClient,
|
||||
DEFAULT_NIP07_ENCRYPTION_METHODS,
|
||||
|
||||
@@ -101,10 +101,21 @@ export async function publishDmReadReceipt(
|
||||
? shouldRequestExtensionPermissions(signer)
|
||||
: false;
|
||||
|
||||
if (shouldRequestPermissions) {
|
||||
permissionResult = await client.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
const hasCachedPermissions =
|
||||
typeof client?.hasRequiredExtensionPermissions === "function" &&
|
||||
client.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
|
||||
if (shouldRequestPermissions && !hasCachedPermissions) {
|
||||
const permissionGate =
|
||||
typeof client?.ensureExtensionPermissionsGate === "function"
|
||||
? client.ensureExtensionPermissionsGate.bind(client)
|
||||
: typeof client?.ensureExtensionPermissions === "function"
|
||||
? client.ensureExtensionPermissions.bind(client)
|
||||
: null;
|
||||
|
||||
if (permissionGate) {
|
||||
permissionResult = await permissionGate(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissionResult.ok) {
|
||||
@@ -242,10 +253,21 @@ export async function publishDmTypingIndicator(
|
||||
? shouldRequestExtensionPermissions(signer)
|
||||
: false;
|
||||
|
||||
if (shouldRequestPermissions) {
|
||||
permissionResult = await client.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
const hasCachedPermissions =
|
||||
typeof client?.hasRequiredExtensionPermissions === "function" &&
|
||||
client.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
|
||||
if (shouldRequestPermissions && !hasCachedPermissions) {
|
||||
const permissionGate =
|
||||
typeof client?.ensureExtensionPermissionsGate === "function"
|
||||
? client.ensureExtensionPermissionsGate.bind(client)
|
||||
: typeof client?.ensureExtensionPermissions === "function"
|
||||
? client.ensureExtensionPermissions.bind(client)
|
||||
: null;
|
||||
|
||||
if (permissionGate) {
|
||||
permissionResult = await permissionGate(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissionResult.ok) {
|
||||
|
||||
@@ -435,10 +435,21 @@ export async function publishVideoReaction(
|
||||
|
||||
if (canUseActiveSigner) {
|
||||
let permissionResult = { ok: true };
|
||||
if (shouldRequestExtensionPermissions(signer)) {
|
||||
permissionResult = await client.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS
|
||||
);
|
||||
const hasCachedPermissions =
|
||||
typeof client?.hasRequiredExtensionPermissions === "function" &&
|
||||
client.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
|
||||
if (shouldRequestExtensionPermissions(signer) && !hasCachedPermissions) {
|
||||
const permissionGate =
|
||||
typeof client?.ensureExtensionPermissionsGate === "function"
|
||||
? client.ensureExtensionPermissionsGate.bind(client)
|
||||
: typeof client?.ensureExtensionPermissions === "function"
|
||||
? client.ensureExtensionPermissions.bind(client)
|
||||
: null;
|
||||
|
||||
if (permissionGate) {
|
||||
permissionResult = await permissionGate(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
if (permissionResult.ok) {
|
||||
try {
|
||||
|
||||
+15
-4
@@ -787,10 +787,21 @@ export async function publishViewEvent(
|
||||
|
||||
if (canUseActiveSigner) {
|
||||
let permissionResult = { ok: true };
|
||||
if (shouldRequestExtensionPermissions(signer)) {
|
||||
permissionResult = await client.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS
|
||||
);
|
||||
const hasCachedPermissions =
|
||||
typeof client?.hasRequiredExtensionPermissions === "function" &&
|
||||
client.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
|
||||
if (shouldRequestExtensionPermissions(signer) && !hasCachedPermissions) {
|
||||
const permissionGate =
|
||||
typeof client?.ensureExtensionPermissionsGate === "function"
|
||||
? client.ensureExtensionPermissionsGate.bind(client)
|
||||
: typeof client?.ensureExtensionPermissions === "function"
|
||||
? client.ensureExtensionPermissions.bind(client)
|
||||
: null;
|
||||
|
||||
if (permissionGate) {
|
||||
permissionResult = await permissionGate(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
if (permissionResult.ok) {
|
||||
try {
|
||||
|
||||
@@ -1530,7 +1530,12 @@ class WatchHistoryManager {
|
||||
|
||||
if (activeSigner) {
|
||||
if (this.deps.shouldRequestExtensionPermissions?.(activeSigner)) {
|
||||
await this.deps.ensureExtensionPermissions?.(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
const hasCachedPermissions =
|
||||
typeof this.deps.hasRequiredExtensionPermissions === "function" &&
|
||||
this.deps.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
if (!hasCachedPermissions) {
|
||||
await this.deps.ensureExtensionPermissions?.(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
const encryptors = [];
|
||||
@@ -1863,7 +1868,12 @@ class WatchHistoryManager {
|
||||
const decryptSigner = canUseActiveSignerDecrypt ? signer : null;
|
||||
|
||||
if (decryptSigner && this.deps.shouldRequestExtensionPermissions?.(decryptSigner)) {
|
||||
await this.deps.ensureExtensionPermissions?.(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
const hasCachedPermissions =
|
||||
typeof this.deps.hasRequiredExtensionPermissions === "function" &&
|
||||
this.deps.hasRequiredExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
if (!hasCachedPermissions) {
|
||||
await this.deps.ensureExtensionPermissions?.(DEFAULT_NIP07_PERMISSION_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
devLogger.info("[nostr] Fetching watch history from relays.", {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
export {
|
||||
nostrClient,
|
||||
hasDefaultExtensionPermissions,
|
||||
requestDefaultExtensionPermissions,
|
||||
} from "./nostr/defaultClient.js";
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ export function registerNostrClient(client, options = {}) {
|
||||
) {
|
||||
requestPermissionsDelegate = (...args) =>
|
||||
registeredNostrClient.ensureExtensionPermissions(...args);
|
||||
if (typeof registeredNostrClient.ensureExtensionPermissionsGate === "function") {
|
||||
requestPermissionsDelegate = (...args) =>
|
||||
registeredNostrClient.ensureExtensionPermissionsGate(...args);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+29
-22
@@ -1,6 +1,7 @@
|
||||
// js/subscriptions.js
|
||||
import {
|
||||
getActiveSigner,
|
||||
hasDefaultExtensionPermissions,
|
||||
nostrClient,
|
||||
requestDefaultExtensionPermissions,
|
||||
} from "./nostrClientFacade.js";
|
||||
@@ -750,16 +751,19 @@ class SubscriptionsManager {
|
||||
(!signerHasNip04 && !windowHasNip04 && requiresNip04)
|
||||
) {
|
||||
try {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult?.ok) {
|
||||
const error =
|
||||
permissionResult?.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error(
|
||||
"Extension permissions denied while decrypting subscriptions."
|
||||
);
|
||||
error.code = "nostr-permission-denied";
|
||||
return { ok: false, error };
|
||||
const hasCachedPermissions = hasDefaultExtensionPermissions();
|
||||
if (!hasCachedPermissions) {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult?.ok) {
|
||||
const error =
|
||||
permissionResult?.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error(
|
||||
"Extension permissions denied while decrypting subscriptions."
|
||||
);
|
||||
error.code = "nostr-permission-denied";
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const wrapped = error instanceof Error ? error : new Error(String(error));
|
||||
@@ -979,18 +983,21 @@ class SubscriptionsManager {
|
||||
}
|
||||
|
||||
if (signer.type === "extension") {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult.ok) {
|
||||
userLogger.warn(
|
||||
"[SubscriptionsManager] Signer permissions denied while updating subscriptions.",
|
||||
permissionResult.error,
|
||||
);
|
||||
const error = new Error(
|
||||
"The active signer must allow encryption and signing before updating subscriptions.",
|
||||
);
|
||||
error.code = "extension-permission-denied";
|
||||
error.cause = permissionResult.error;
|
||||
throw error;
|
||||
const hasCachedPermissions = hasDefaultExtensionPermissions();
|
||||
if (!hasCachedPermissions) {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult.ok) {
|
||||
userLogger.warn(
|
||||
"[SubscriptionsManager] Signer permissions denied while updating subscriptions.",
|
||||
permissionResult.error,
|
||||
);
|
||||
const error = new Error(
|
||||
"The active signer must allow encryption and signing before updating subscriptions.",
|
||||
);
|
||||
error.code = "extension-permission-denied";
|
||||
error.cause = permissionResult.error;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user