From bb33b126497e82d36f02b9fb1b08855752807fef Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 21 Jun 2026 21:52:52 -0400 Subject: [PATCH] fix(auth): anonymous view-event session actor no longer reports you as logged out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After watching/opening a video, zapping showed "Log in and connect a wallet" even though logged in. Recording a view creates an anonymous session actor with source "session" (SignerManager.ensureSessionActor). isUserLoggedIn() treated any declaredSource on a different-pubkey session actor as a managed alternate identity and returned false — so the view-event telemetry actor logged the user out app-wide (zap gate, etc.). Fix: "session" is the anonymous telemetry source, not a login identity — only a managed source (e.g. "nsec") for a different pubkey disqualifies. (Commit 7985a481 intended to allow anonymous actors but only covered the sourceless shape; the real actor carries source "session".) tests/app/is-user-logged-in.test.mjs: added the real view-event actor shape (source "session", different pubkey) → logged in; the "nsec" rejection case still holds. Mutation-verified. Co-Authored-By: Claude Opus 4.8 --- js/app.js | 8 +++++++- tests/app/is-user-logged-in.test.mjs | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/js/app.js b/js/app.js index 82dd525f..ad5ce4fc 100644 --- a/js/app.js +++ b/js/app.js @@ -3351,9 +3351,15 @@ class Application { typeof sessionActor?.source === "string" ? sessionActor.source.trim() : ""; + // The anonymous view-event actor is created with source "session" (see + // SignerManager.ensureSessionActor). It's telemetry, NOT an alternate login + // identity, so it must not disqualify a real login — otherwise watching any + // video flips isUserLoggedIn() to false and breaks zaps/etc. Only a managed + // real source (e.g. "nsec") for a DIFFERENT pubkey disqualifies. + const isManagedSource = Boolean(declaredSource) && declaredSource !== "session"; const isPersisted = sessionActor?.persisted === true; - if (!hasEmbeddedPrivateKey || declaredSource || isPersisted) { + if (!hasEmbeddedPrivateKey || isManagedSource || isPersisted) { return false; } } diff --git a/tests/app/is-user-logged-in.test.mjs b/tests/app/is-user-logged-in.test.mjs index 57779a5f..50c4d703 100644 --- a/tests/app/is-user-logged-in.test.mjs +++ b/tests/app/is-user-logged-in.test.mjs @@ -112,6 +112,31 @@ test("isUserLoggedIn ignores anonymous session actor mismatches", () => { } }); +test("isUserLoggedIn allows the anonymous view-event session actor (source 'session')", () => { + // Regression: watching a video creates an anonymous view-event actor with + // source "session" and a different pubkey. It must NOT log the user out (which + // broke zaps with "Log in and connect a wallet"). + const previousPubkey = nostrClient.pubkey; + const previousSessionActor = nostrClient.sessionActor; + + try { + const pubkey = "1".repeat(64); + nostrClient.pubkey = pubkey; + nostrClient.sessionActor = { + pubkey: "2".repeat(64), + privateKey: "3".repeat(64), + source: "session", + createdAt: Date.now(), + }; + + const app = createTestApp(pubkey); + assert.equal(app.isUserLoggedIn(), true); + } finally { + nostrClient.pubkey = previousPubkey; + nostrClient.sessionActor = previousSessionActor; + } +}); + test("isUserLoggedIn rejects mismatched managed session actors", () => { const previousPubkey = nostrClient.pubkey; const previousSessionActor = nostrClient.sessionActor;