diff --git a/js/nostr/client.js b/js/nostr/client.js index b076fc92..bf0decef 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -214,7 +214,7 @@ import { queueSignEvent } from "./signRequestQueue.js"; import { EventsMap } from "./eventsMap.js"; import { PersistenceManager } from "./managers/PersistenceManager.js"; import { ConnectionManager } from "./managers/ConnectionManager.js"; -import { SignerManager } from "./managers/SignerManager.js"; +import { SignerManager, resolveSignerCapabilities } from "./managers/SignerManager.js"; import { RelayBatchFetcher } from "./relayBatchFetcher.js"; function normalizeProfileFromEvent(event) { @@ -754,6 +754,8 @@ export class NostrClient { get sessionActorCipherClosuresPrivateKey() { return this.signerManager.sessionActorCipherClosuresPrivateKey; } set sessionActorCipherClosuresPrivateKey(val) { this.signerManager.sessionActorCipherClosuresPrivateKey = val; } + get extensionPermissionCache() { return this.signerManager.extensionPermissionCache; } + get pool() { return this.connectionManager.pool; } set pool(val) { this.connectionManager.pool = val; } @@ -2913,6 +2915,15 @@ export class NostrClient { return this.signerManager.ensureActiveSignerForPubkey(pubkey); } + async registerPrivateKeySigner({ privateKey, pubkey }) { + if (!privateKey) { + throw new Error("Private key is required."); + } + const adapter = await createNsecAdapter({ privateKey, pubkey }); + this.signerManager.setActiveSigner(adapter); + return adapter; + } + async loginWithExtension(options) { return this.signerManager.loginWithExtension(options); } diff --git a/js/nostr/managers/SignerManager.js b/js/nostr/managers/SignerManager.js index e794c862..beaee255 100644 --- a/js/nostr/managers/SignerManager.js +++ b/js/nostr/managers/SignerManager.js @@ -53,7 +53,7 @@ import { queueSignEvent } from "../signRequestQueue.js"; import { signEventWithPrivateKey } from "../publishHelpers.js"; import { ensureNostrTools, getCachedNostrTools } from "../toolkit.js"; -function resolveSignerCapabilities(signer) { +export function resolveSignerCapabilities(signer) { const fallback = { sign: false, nip44: false, @@ -263,6 +263,10 @@ export class SignerManager { this.extensionReady = false; this.extensionPermissionsGranted = false; this.extensionPermissionCache = new Map(); + const storedPermissions = readStoredNip07Permissions(); + for (const method of storedPermissions) { + this.extensionPermissionCache.set(method, true); + } this.sessionActorCipherClosures = null; this.sessionActorCipherClosuresPrivateKey = null; this.remoteSignerListeners = new Set(); @@ -423,6 +427,9 @@ export class SignerManager { if (missing.length === 0) { this.extensionPermissionsGranted = true; this.extensionPermissionCache.set(cacheKey, true); + for (const method of methods) { + this.extensionPermissionCache.set(method, true); + } return { ok: true }; } @@ -435,11 +442,14 @@ export class SignerManager { const message = resolvePermissionStatusMessage(missing, context); try { - const response = await requestEnablePermissions(missing); - if (response?.enabled) { + const response = await requestEnablePermissions(window.nostr, missing); + if (response?.ok) { writeStoredNip07Permissions(missing); this.extensionPermissionsGranted = true; this.extensionPermissionCache.set(cacheKey, true); + for (const method of missing) { + this.extensionPermissionCache.set(method, true); + } return { ok: true }; } return { ok: false, error: "permission-denied" }; diff --git a/tests/nostr-login-permissions.test.mjs b/tests/nostr-login-permissions.test.mjs index 7d56fc2a..9451b3c8 100644 --- a/tests/nostr-login-permissions.test.mjs +++ b/tests/nostr-login-permissions.test.mjs @@ -55,6 +55,7 @@ function setupLoginEnvironment({ enableImpl, getPublicKey = HEX_PUBKEY } = {}) { return Promise.resolve(); }, getPublicKey: () => Promise.resolve(getPublicKey), + signEvent: () => Promise.resolve({}), }; windowRef.nostr = nostrStub; @@ -456,10 +457,11 @@ describe("NIP-07 Login Permissions", () => { it("NIP-07 login does not wait for deferred permission grants", async () => { clearStoredPermissions(); const env = setupLoginEnvironment(); - const originalEnsurePermissions = nostrClient.ensureExtensionPermissions; + const target = nostrClient.signerManager || nostrClient; + const originalEnsurePermissions = target.ensureExtensionPermissions; let completionPromise; - nostrClient.ensureExtensionPermissions = async () => { + target.ensureExtensionPermissions = async () => { completionPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error("permission denied")), 300); }); @@ -482,7 +484,7 @@ describe("NIP-07 Login Permissions", () => { "deferred permission grants should still reject", ); } finally { - nostrClient.ensureExtensionPermissions = originalEnsurePermissions; + target.ensureExtensionPermissions = originalEnsurePermissions; env.restore(); nostrClient.logout(); clearStoredPermissions();