diff --git a/js/nostr/client.js b/js/nostr/client.js index 4ce6aecb..585a8751 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -3057,8 +3057,8 @@ export class NostrClient { return this.signerManager.disconnectRemoteSigner(options); } - async ensureSessionActor() { - return this.signerManager.ensureSessionActor(); + async ensureSessionActor(...args) { + return this.signerManager.ensureSessionActor(...args); } clearStoredSessionActor() { diff --git a/js/nostr/managers/SignerManager.js b/js/nostr/managers/SignerManager.js index a78463f0..14b35e27 100644 --- a/js/nostr/managers/SignerManager.js +++ b/js/nostr/managers/SignerManager.js @@ -325,7 +325,7 @@ export class SignerManager { return resolveActiveSigner(pubkey); } - async ensureSessionActor() { + async ensureSessionActor(createIfMissing = false) { if (this.sessionActor) { return this.sessionActor; } @@ -341,6 +341,40 @@ export class SignerManager { return storedEntry; } + if (createIfMissing) { + const tools = (await ensureNostrTools()) || getCachedNostrTools(); + if (tools) { + let privateKey; + let pubkey; + + if (typeof tools.generatePrivateKey === "function") { + const raw = tools.generatePrivateKey(); + if (typeof raw === "string") { + privateKey = raw; + pubkey = tools.getPublicKey(privateKey); + } else { + pubkey = tools.getPublicKey(raw); + privateKey = Array.from(raw) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + } + } else if (typeof tools.generateSecretKey === "function") { + const secret = tools.generateSecretKey(); + pubkey = tools.getPublicKey(secret); + privateKey = Array.from(secret) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + } + + if (privateKey && pubkey) { + const newActor = { privateKey, pubkey }; + this.sessionActor = newActor; + persistSessionActorEntry(newActor); + return newActor; + } + } + } + return null; } diff --git a/js/nostr/publishHelpers.js b/js/nostr/publishHelpers.js index 3931e574..9571e2a2 100644 --- a/js/nostr/publishHelpers.js +++ b/js/nostr/publishHelpers.js @@ -140,7 +140,21 @@ export function signEventWithPrivateKey(event, privateKey) { throw new Error("Missing signing primitives"); } - const finalized = finalizeFn(prepared, normalizedKey); + let secretKey = normalizedKey; + if (typeof Uint8Array !== "undefined") { + try { + const len = normalizedKey.length; + const bytes = new Uint8Array(len / 2); + for (let i = 0; i < len; i += 2) { + bytes[i / 2] = parseInt(normalizedKey.substring(i, i + 2), 16); + } + secretKey = bytes; + } catch (error) { + // Ignore conversion error, secretKey remains string + } + } + + const finalized = finalizeFn(prepared, secretKey); if ( !finalized || typeof finalized.id !== "string" || diff --git a/js/nostr/sessionActor.js b/js/nostr/sessionActor.js index bbbafc2f..25227dc6 100644 --- a/js/nostr/sessionActor.js +++ b/js/nostr/sessionActor.js @@ -484,7 +484,17 @@ export function clearStoredSessionActor() { export function isSessionActor(nostrClient) { const sa = nostrClient?.sessionActor; - return !!sa && sa.source !== "nsec"; + if (!sa || sa.source === "nsec") { + return false; + } + const clientPubkey = + typeof nostrClient?.pubkey === "string" ? nostrClient.pubkey : null; + const sessionPubkey = typeof sa.pubkey === "string" ? sa.pubkey : null; + + if (clientPubkey && sessionPubkey && clientPubkey !== sessionPubkey) { + return false; + } + return true; } function _closeSessionActorDb() { diff --git a/js/nostr/viewEvents.js b/js/nostr/viewEvents.js index fc93c988..d2740263 100644 --- a/js/nostr/viewEvents.js +++ b/js/nostr/viewEvents.js @@ -700,8 +700,10 @@ export async function publishViewEvent( return { ok: false, error: "invalid-pointer" }; } - const actorPubkey = await client.ensureSessionActor(); + const actorObj = await client.ensureSessionActor(true); // actorPubkey derived via client.ensureSessionActor() + const actorPubkey = actorObj?.pubkey || (typeof actorObj === "string" ? actorObj : null); + if (!actorPubkey) { return { ok: false, error: "missing-actor" }; } diff --git a/test_output.txt b/test_output.txt index 5ee02f4d..f84eb770 100644 --- a/test_output.txt +++ b/test_output.txt @@ -1,29 +1,46 @@ + +→ Running tests/nostr/integration-remote-flow.test.mjs +VIEW RESULT ERROR: { ok: false, error: 'missing-actor' } TAP version 13 -# Subtest: WebTorrent Regression Tests - # Subtest: probePeers should report healthy if webseed is present even with 0 peers - ok 1 - probePeers should report healthy if webseed is present even with 0 peers - --- - duration_ms: 204.415485 - type: 'test' - ... - # Subtest: probePeers should report unhealthy if no webseed and 0 peers - ok 2 - probePeers should report unhealthy if no webseed and 0 peers - --- - duration_ms: 52.544489 - type: 'test' - ... - 1..2 -ok 1 - WebTorrent Regression Tests +# Subtest: nostr login + remote signing + publish + watch history integration +not ok 1 - nostr login + remote signing + publish + watch history integration --- - duration_ms: 259.856073 - type: 'suite' + duration_ms: 60.800483 + type: 'test' + location: '/app/tests/nostr/integration-remote-flow.test.mjs:52:1' + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly equal: + + false !== true + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: true + actual: false + operator: 'strictEqual' + stack: |- + TestContext. (file:///app/tests/nostr/integration-remote-flow.test.mjs:272:83) + async Test.run (node:internal/test_runner/test:1054:7) + async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3) ... 1..1 -# tests 2 -# suites 1 -# pass 2 -# fail 0 +# tests 1 +# suites 0 +# pass 0 +# fail 1 # cancelled 0 # skipped 0 # todo 0 -# duration_ms 269.477515 +# duration_ms 72.354406 +file:///app/scripts/run-targeted-tests.mjs:44 + reject(new Error(`${relativePath} failed with exit code ${code}`)); + ^ + +Error: tests/nostr/integration-remote-flow.test.mjs failed with exit code 1 + at ChildProcess. (file:///app/scripts/run-targeted-tests.mjs:44:16) + at ChildProcess.emit (node:events:519:28) + at maybeClose (node:internal/child_process:1101:16) + at ChildProcess._handle.onexit (node:internal/child_process:304:5) + +Node.js v22.22.0 diff --git a/tests/nostr/integration-remote-flow.test.mjs b/tests/nostr/integration-remote-flow.test.mjs index 3bcf4764..d494406a 100644 --- a/tests/nostr/integration-remote-flow.test.mjs +++ b/tests/nostr/integration-remote-flow.test.mjs @@ -130,6 +130,7 @@ test("nostr login + remote signing + publish + watch history integration", async const toolkit = { finalizeEvent, + generateSecretKey, getEventHash, getPublicKey, nip04,