Refactor NIP-07 helpers and session actor storage

This commit is contained in:
thePR0M3TH3AN committed 2025-10-22 11:25:46 -04:00
1 parent 35ac62be15
commit 3382185512
6 files changed
+935 -659

No files matched your search

+76
View File
@@ -0,0 +1,76 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
readStoredNip07Permissions,
writeStoredNip07Permissions,
clearStoredNip07Permissions,
requestEnablePermissions,
} from "../../js/nostr/nip07Permissions.js";
test("writeStoredNip07Permissions normalizes and persists granted methods", () => {
clearStoredNip07Permissions();
writeStoredNip07Permissions([" get_public_key ", "", "sign_event"]);
const stored = readStoredNip07Permissions();
assert.ok(stored instanceof Set);
const sorted = [...stored].sort();
assert.deepStrictEqual(sorted, ["get_public_key", "sign_event"]);
});
test("clearStoredNip07Permissions removes persisted grants", () => {
clearStoredNip07Permissions();
writeStoredNip07Permissions(["get_public_key"]);
assert.notStrictEqual(
globalThis.localStorage.getItem("bitvid:nip07:permissions"),
null,
);
clearStoredNip07Permissions();
const afterClear = readStoredNip07Permissions();
assert.strictEqual(afterClear.size, 0);
assert.strictEqual(
globalThis.localStorage.getItem("bitvid:nip07:permissions"),
null,
);
});
test("requestEnablePermissions retries explicit and fallback variants", async () => {
clearStoredNip07Permissions();
const calls = [];
let attempt = 0;
const extension = {
enable: (options) => {
calls.push(options);
attempt += 1;
if (attempt < 3) {
return Promise.reject(new Error(`attempt-${attempt}`));
}
return Promise.resolve();
},
};
const result = await requestEnablePermissions(
extension,
["sign_event"],
{ isDevMode: false },
);
assert.deepStrictEqual(calls, [
{ permissions: [{ method: "sign_event" }] },
{ permissions: ["sign_event"] },
undefined,
]);
assert.deepStrictEqual(result, { ok: true });
});
test("requestEnablePermissions reports unavailable extension", async () => {
clearStoredNip07Permissions();
const result = await requestEnablePermissions(null, ["sign_event"]);
assert.strictEqual(result.ok, false);
assert.strictEqual(result.error?.message, "extension-unavailable");
});
+122
View File
@@ -0,0 +1,122 @@
import assert from "node:assert/strict";
import test from "node:test";
import { webcrypto } from "node:crypto";
const originalCrypto = globalThis.crypto;
if (originalCrypto === undefined) {
Object.defineProperty(globalThis, "crypto", {
value: webcrypto,
configurable: true,
writable: false,
enumerable: false,
});
}
import {
encryptSessionPrivateKey,
decryptSessionPrivateKey,
persistSessionActor,
readStoredSessionActorEntry,
clearStoredSessionActor,
SESSION_ACTOR_STORAGE_KEY,
} from "../../js/nostr/sessionActor.js";
test.after(() => {
clearStoredSessionActor();
if (originalCrypto === undefined) {
delete globalThis.crypto;
}
});
test("encryptSessionPrivateKey + decryptSessionPrivateKey roundtrip", async () => {
clearStoredSessionActor();
const privateKey = "a".repeat(64);
const passphrase = "correct horse";
const encrypted = await encryptSessionPrivateKey(privateKey, passphrase);
assert.match(encrypted.ciphertext, /.+/);
assert.ok(encrypted.salt.length > 0);
assert.ok(encrypted.iv.length > 0);
const decrypted = await decryptSessionPrivateKey(
{
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
},
passphrase,
);
assert.strictEqual(decrypted, privateKey);
});
test("persistSessionActor stores plain-text session actors", () => {
clearStoredSessionActor();
persistSessionActor({
pubkey: "npub1test",
privateKey: "b".repeat(64),
createdAt: 123,
});
const raw = globalThis.localStorage.getItem(SESSION_ACTOR_STORAGE_KEY);
assert.ok(raw, "session actor should be persisted");
const parsed = JSON.parse(raw);
assert.deepStrictEqual(parsed, {
pubkey: "npub1test",
privateKey: "b".repeat(64),
createdAt: 123,
});
});
test("persistSessionActor stores encrypted payload metadata", async () => {
clearStoredSessionActor();
const privateKey = "c".repeat(64);
const passphrase = "battery staple";
const encrypted = await encryptSessionPrivateKey(privateKey, passphrase);
persistSessionActor({
pubkey: "npub1encrypted",
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
createdAt: 456,
});
const entry = readStoredSessionActorEntry();
assert.ok(entry);
assert.strictEqual(entry.pubkey, "npub1encrypted");
assert.strictEqual(entry.privateKey, "");
assert.strictEqual(entry.privateKeyEncrypted, encrypted.ciphertext);
assert.deepStrictEqual(entry.encryption, {
version: encrypted.version,
algorithm: encrypted.algorithm,
salt: encrypted.salt,
iv: encrypted.iv,
iterations: encrypted.iterations,
hash: encrypted.hash,
});
assert.strictEqual(entry.createdAt, 456);
const raw = globalThis.localStorage.getItem(SESSION_ACTOR_STORAGE_KEY);
assert.ok(raw);
const parsed = JSON.parse(raw);
assert.strictEqual(parsed.privateKey, undefined);
assert.strictEqual(parsed.pubkey, "npub1encrypted");
assert.strictEqual(parsed.privateKeyEncrypted, encrypted.ciphertext);
});
test("clearStoredSessionActor removes persisted payload", () => {
clearStoredSessionActor();
persistSessionActor({
pubkey: "npub1cleanup",
privateKey: "d".repeat(64),
});
assert.ok(globalThis.localStorage.getItem(SESSION_ACTOR_STORAGE_KEY));
clearStoredSessionActor();
assert.strictEqual(
globalThis.localStorage.getItem(SESSION_ACTOR_STORAGE_KEY),
null,
);
});