mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix(nostr): prevent nip-07 fallback from overwriting concurrent login
- Added a check in `ensureActiveSignerForPubkey` to verify if a valid signer (e.g. NIP-46) was registered during the extension wait period. - Modified `nip07Permissions.js` to respect global timeout overrides for structured permission requests, fixing flaky tests. - Added regression test `tests/nostr-signer-race.test.mjs` to simulate the race condition. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
0c479319e6
commit
7cb603b652
3 files changed
+72
-1
No files matched your search
@@ -2642,6 +2642,12 @@ export class NostrClient {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a signer appeared while we were waiting (e.g. NIP-46 or concurrent NIP-07 logic)
|
||||
const recheckedSigner = resolveActiveSigner(normalizedPubkey);
|
||||
if (recheckedSigner && typeof recheckedSigner.signEvent === "function") {
|
||||
return recheckedSigner;
|
||||
}
|
||||
|
||||
if (!extension) {
|
||||
return existingSigner;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ export async function requestEnablePermissions(
|
||||
// If we fall back to standard enable() (null), allow the full user interaction time.
|
||||
const variantTimeoutOverrides = options
|
||||
? {
|
||||
timeoutMs: 3000,
|
||||
timeoutMs: Math.min(3000, getEnableVariantTimeoutMs()),
|
||||
retryMultiplier: 1,
|
||||
}
|
||||
: {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
import "./test-helpers/setup-localstorage.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { NostrClient, setActiveSigner, getActiveSigner } from "../js/nostr/client.js";
|
||||
|
||||
const HEX_PUBKEY = "a".repeat(64);
|
||||
|
||||
test("ensureActiveSignerForPubkey prefers concurrent login over late extension injection", async () => {
|
||||
// 1. Setup Environment
|
||||
const originalWindow = global.window;
|
||||
global.window = {};
|
||||
|
||||
try {
|
||||
const client = new NostrClient();
|
||||
client.pubkey = HEX_PUBKEY;
|
||||
// Pre-condition: Cache permissions to trigger the waitForNip07Extension logic
|
||||
client.extensionPermissionCache.add("sign_event");
|
||||
|
||||
// Mock a NIP-46 signer that might be set during the race
|
||||
const mockNip46Signer = {
|
||||
type: "nip46",
|
||||
pubkey: HEX_PUBKEY,
|
||||
signEvent: async () => ({ id: "mock-signed" }),
|
||||
nip04: { encrypt: async () => "enc", decrypt: async () => "dec" }
|
||||
};
|
||||
|
||||
// Mock the NIP-07 extension that appears late
|
||||
const mockExtension = {
|
||||
getPublicKey: async () => HEX_PUBKEY,
|
||||
signEvent: async () => ({ id: "ext-signed" }),
|
||||
nip04: { encrypt: async () => "ext-enc", decrypt: async () => "ext-dec" }
|
||||
};
|
||||
|
||||
// 2. Start the race
|
||||
// Calling ensureActiveSignerForPubkey while window.nostr is missing but permissions exist
|
||||
// causes it to enter waitForNip07Extension loop (polling).
|
||||
const ensurePromise = client.ensureActiveSignerForPubkey(HEX_PUBKEY);
|
||||
|
||||
// 3. Simulate Concurrent Login (e.g. NIP-46 restore completes)
|
||||
// We wait a small amount to ensure ensureActiveSignerForPubkey has started polling
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
setActiveSigner(mockNip46Signer);
|
||||
|
||||
// 4. Simulate Extension Loading late
|
||||
global.window.nostr = mockExtension;
|
||||
|
||||
// 5. Await result
|
||||
const resultSigner = await ensurePromise;
|
||||
|
||||
// 6. Assertions
|
||||
const finalActiveSigner = getActiveSigner();
|
||||
|
||||
// We expect the NIP-46 signer to be preserved and not overwritten by the NIP-07 fallback
|
||||
assert.equal(finalActiveSigner.type, "nip46", "Race condition! NIP-46 signer was overwritten by NIP-07 fallback.");
|
||||
assert.equal(resultSigner.type, "nip46", "Returned signer should be the NIP-46 one.");
|
||||
} finally {
|
||||
if (originalWindow) {
|
||||
global.window = originalWindow;
|
||||
} else {
|
||||
delete global.window;
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user