From 576513ecdb9715a52729c349f4d408cfecd0fc40 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 21 Feb 2026 16:00:02 -0500 Subject: [PATCH] updated nip-07 login --- js/nostr/managers/SignerManager.js | 24 ++++++++-- js/nostr/nip07Permissions.js | 71 ++++++++++++---------------- tests/nostr/nip07Permissions.test.js | 28 +++++++++++ 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/js/nostr/managers/SignerManager.js b/js/nostr/managers/SignerManager.js index c8b1a429..d92b6231 100644 --- a/js/nostr/managers/SignerManager.js +++ b/js/nostr/managers/SignerManager.js @@ -481,8 +481,6 @@ export class SignerManager { extension = window.nostr; } - const message = resolvePermissionStatusMessage(missing, context); - try { const response = await requestEnablePermissions(extension, missing); if (response?.ok) { @@ -494,7 +492,10 @@ export class SignerManager { } return { ok: true }; } - return { ok: false, error: "permission-denied" }; + return { + ok: false, + error: response?.error || "permission-denied", + }; } catch (error) { devLogger.warn("[nostr] Extension permission request failed:", error); return { ok: false, error: "request-failed" }; @@ -572,8 +573,23 @@ export class SignerManager { { context: "login", logMetrics: true, showStatus: false }, ); if (!permissionResult.ok) { + const permissionErrorDetail = + permissionResult && typeof permissionResult === "object" + ? permissionResult.error + : null; + const permissionDetailMessage = + permissionErrorDetail instanceof Error && + typeof permissionErrorDetail.message === "string" && + permissionErrorDetail.message.trim() + ? permissionErrorDetail.message.trim() + : typeof permissionErrorDetail === "string" && + permissionErrorDetail.trim() + ? permissionErrorDetail.trim() + : ""; const denialMessage = - 'The NIP-07 extension reported "permission denied". Please approve the prompt and try again.'; + permissionDetailMessage + ? `The NIP-07 extension denied the permission request (${permissionDetailMessage}). Please approve the prompt and try again.` + : 'The NIP-07 extension reported "permission denied". Please approve the prompt and try again.'; const denialError = new Error(denialMessage); if (permissionResult.error) { denialError.cause = permissionResult.error; diff --git a/js/nostr/nip07Permissions.js b/js/nostr/nip07Permissions.js index 1864fec5..49f09087 100644 --- a/js/nostr/nip07Permissions.js +++ b/js/nostr/nip07Permissions.js @@ -345,8 +345,10 @@ export async function requestEnablePermissions( } let lastError = null; - if (hasRequestPermissions) { + const runPermissionMethod = async (methodName, method) => { for (const options of permissionVariants) { + // Specific permission payloads should fail fast; prompt-based no-arg calls + // get a longer timeout so users have time to confirm in extension UI. const variantTimeoutOverrides = options ? { timeoutMs: Math.min(3000, getEnableVariantTimeoutMs()), @@ -364,64 +366,49 @@ export async function requestEnablePermissions( await runNip07WithRetry( () => options === null - ? extension.requestPermissions() - : extension.requestPermissions(options), + ? method() + : methodName === "enable" + ? method(options) + : method(options), { - label: "extension.requestPermissions", + label: `extension.${methodName}`, ...variantTimeoutOverrides, }, ); - return { ok: true }; + return true; } catch (error) { lastError = error; if (options && isDevMode) { userLogger.warn( - "[nostr] extension.requestPermissions call failed:", + `[nostr] extension.${methodName} permission request failed:`, error, ); } } } - } + return false; + }; - if (!hasEnable) { - return { - ok: false, - error: lastError || new Error("permission-denied"), - }; + // Compatibility-first: prefer standard NIP-07 enable() where available. + if (hasEnable) { + const enableSucceeded = await runPermissionMethod( + "enable", + extension.enable.bind(extension), + ); + if (enableSucceeded) { + return { ok: true }; + } } - for (const options of permissionVariants) { - // If specific permissions are requested (variants 1 & 2), fail fast (3s) - // to avoid hanging if the extension doesn't support the structured format. - // If we fall back to standard enable() (null), allow the full user interaction time. - const variantTimeoutOverrides = options - ? { - timeoutMs: Math.min(3000, getEnableVariantTimeoutMs()), - retryMultiplier: 1, - } - : { - timeoutMs: Math.min( - NIP07_LOGIN_TIMEOUT_MS, - getEnableVariantTimeoutMs(), - ), - retryMultiplier: 1, - }; - - try { - await runNip07WithRetry( - () => (options ? extension.enable(options) : extension.enable()), - { label: "extension.enable", ...variantTimeoutOverrides }, - ); + // Fallback for extensions that implement requestPermissions() but not enable(), + // or where enable() payload handling is broken. + if (hasRequestPermissions) { + const requestPermissionsSucceeded = await runPermissionMethod( + "requestPermissions", + extension.requestPermissions.bind(extension), + ); + if (requestPermissionsSucceeded) { return { ok: true }; - } catch (error) { - lastError = error; - if (options && isDevMode) { - userLogger.warn( - "[nostr] extension.enable request with explicit permissions failed:", - error, - ); - } } } diff --git a/tests/nostr/nip07Permissions.test.js b/tests/nostr/nip07Permissions.test.js index 2d7903cf..14aab3ff 100644 --- a/tests/nostr/nip07Permissions.test.js +++ b/tests/nostr/nip07Permissions.test.js @@ -89,6 +89,34 @@ test("requestEnablePermissions supports requestPermissions-only extensions", asy assert.deepStrictEqual(calls, [{ permissions: [{ method: "sign_event" }] }]); }); +test("requestEnablePermissions prefers enable over requestPermissions when both exist", async () => { + clearStoredNip07Permissions(); + const calls = { + enable: [], + requestPermissions: [], + }; + const extension = { + enable: (options) => { + calls.enable.push(options); + return Promise.resolve(); + }, + requestPermissions: (options) => { + calls.requestPermissions.push(options); + return Promise.resolve({ granted: true }); + }, + }; + + const result = await requestEnablePermissions( + extension, + ["sign_event"], + { isDevMode: false }, + ); + + assert.deepStrictEqual(result, { ok: true }); + assert.deepStrictEqual(calls.enable, [{ permissions: [{ method: "sign_event" }] }]); + assert.deepStrictEqual(calls.requestPermissions, []); +}); + test("requestEnablePermissions fails when requestPermissions exists but denies", async () => { clearStoredNip07Permissions(); const extension = {