mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-29 08:52:25 +00:00
refactor(auth): decouple NIP-07 logic and optimize list fetching (agent)
- Moved NIP-07 extension detection and permission logic from `NostrClient` to `nip07` provider. - Removed redundant `login` method from `NostrClient`. - Increased concurrency and added timeouts to `fetchListIncrementally` to prevent hanging on slow relays. - Updated `UserBlocks` to force full fetch on cache miss, fixing "empty list" issues. - Updated unit tests for login permissions and hashtag preferences to reflect architectural changes. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
967bb079f5
commit
3c7a396da9
6 files changed
+241
-207
No files matched your search
+14
-167
@@ -3534,7 +3534,7 @@ export class NostrClient {
|
||||
|
||||
const normalizedRelays = sanitizeRelayList(this.getHealthyRelays(relaysToUse));
|
||||
const results = [];
|
||||
const concurrencyLimit = 4;
|
||||
const concurrencyLimit = 8;
|
||||
|
||||
// We'll use the pool's list method if fetchFn isn't provided,
|
||||
// but we need to call it per-relay.
|
||||
@@ -3594,7 +3594,13 @@ export class NostrClient {
|
||||
}
|
||||
|
||||
try {
|
||||
let events = await actualFetchFn(relayUrl, filter);
|
||||
// Wrap fetch with a timeout to prevent hanging on slow relays
|
||||
let events = await withRequestTimeout(
|
||||
actualFetchFn(relayUrl, filter),
|
||||
6000,
|
||||
null,
|
||||
`Fetch from ${relayUrl} timed out`
|
||||
);
|
||||
|
||||
// If incremental returned empty, we assume no updates (success).
|
||||
// But if we did a full fetch (no since), empty means empty.
|
||||
@@ -3624,7 +3630,12 @@ export class NostrClient {
|
||||
if (!doFullFetch) {
|
||||
try {
|
||||
delete filter.since;
|
||||
const events = await actualFetchFn(relayUrl, filter);
|
||||
const events = await withRequestTimeout(
|
||||
actualFetchFn(relayUrl, filter),
|
||||
6000,
|
||||
null,
|
||||
`Full fetch fallback from ${relayUrl} timed out`
|
||||
);
|
||||
// Update lastSeen on success of fallback
|
||||
let maxCreated = 0;
|
||||
if (Array.isArray(events)) {
|
||||
@@ -4030,170 +4041,6 @@ export class NostrClient {
|
||||
return source.filter((url) => !this.unreachableRelays.has(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates the user via a NIP-07 browser extension (e.g., Alby, nos2x).
|
||||
*
|
||||
* **Process:**
|
||||
* 1. Checks for `window.nostr` presence.
|
||||
* 2. Requests permissions (sign_event, nip04, etc.) via `ensureExtensionPermissions`.
|
||||
* 3. Retrieves the public key.
|
||||
* 4. Validates access control (whitelist/blacklist) via `accessControl`.
|
||||
* 5. Sets the active signer to a `Nip07Adapter`.
|
||||
*
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.allowAccountSelection=false] - Whether to prompt the extension to select an account (NIP-07 extension).
|
||||
* @param {string} [options.expectPubkey] - Enforce that the login matches a specific pubkey.
|
||||
* @returns {Promise<string>} The authenticated public key (hex).
|
||||
* @throws {Error} If extension is missing, permissions denied, or account blocked.
|
||||
*/
|
||||
async login(options = {}) {
|
||||
try {
|
||||
let extension = null;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 3;
|
||||
|
||||
while (!extension && attempts < maxAttempts) {
|
||||
try {
|
||||
await waitForNip07Extension();
|
||||
extension = window.nostr;
|
||||
} catch (waitError) {
|
||||
devLogger.log(
|
||||
`Timed out waiting for extension injection (attempt ${
|
||||
attempts + 1
|
||||
}/${maxAttempts}):`,
|
||||
waitError,
|
||||
);
|
||||
}
|
||||
|
||||
if (extension) {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
if (attempts < maxAttempts) {
|
||||
devLogger.log("Retrying NIP-07 detection...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
extension = window.nostr;
|
||||
|
||||
if (!extension) {
|
||||
devLogger.log("No Nostr extension found");
|
||||
throw new Error(
|
||||
"Please install a Nostr extension (Alby, nos2x, etc.)."
|
||||
);
|
||||
}
|
||||
|
||||
const { allowAccountSelection = false, expectPubkey } =
|
||||
typeof options === "object" && options !== null ? options : {};
|
||||
const normalizedExpectedPubkey =
|
||||
typeof expectPubkey === "string" && expectPubkey.trim()
|
||||
? expectPubkey.trim().toLowerCase()
|
||||
: null;
|
||||
|
||||
if (typeof extension.getPublicKey !== "function") {
|
||||
throw new Error(
|
||||
"This NIP-07 extension is missing getPublicKey support. Please update the extension."
|
||||
);
|
||||
}
|
||||
|
||||
const permissionResult = await this.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
if (!permissionResult.ok) {
|
||||
const denialMessage =
|
||||
'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;
|
||||
}
|
||||
throw denialError;
|
||||
}
|
||||
|
||||
if (allowAccountSelection && typeof extension.selectAccounts === "function") {
|
||||
try {
|
||||
const selection = await runNip07WithRetry(
|
||||
() => extension.selectAccounts(expectPubkey ? [expectPubkey] : undefined),
|
||||
{ label: "extension.selectAccounts" }
|
||||
);
|
||||
|
||||
const didCancelSelection =
|
||||
selection === undefined ||
|
||||
selection === null ||
|
||||
selection === false ||
|
||||
(Array.isArray(selection) && selection.length === 0);
|
||||
|
||||
if (didCancelSelection) {
|
||||
throw new Error("Account selection was cancelled.");
|
||||
}
|
||||
} catch (selectionErr) {
|
||||
const message =
|
||||
selectionErr && typeof selectionErr.message === "string"
|
||||
? selectionErr.message
|
||||
: "Account selection was cancelled.";
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
const pubkey = await runNip07WithRetry(() => extension.getPublicKey(), {
|
||||
label: "extension.getPublicKey",
|
||||
});
|
||||
if (!pubkey || typeof pubkey !== "string") {
|
||||
throw new Error(
|
||||
"The NIP-07 extension did not return a public key. Please try again."
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
normalizedExpectedPubkey &&
|
||||
pubkey.toLowerCase() !== normalizedExpectedPubkey
|
||||
) {
|
||||
throw new Error(
|
||||
"The selected account doesn't match the expected profile. Please try again."
|
||||
);
|
||||
}
|
||||
const nip19Tools = await ensureNostrTools();
|
||||
const npubEncode = nip19Tools?.nip19?.npubEncode;
|
||||
if (typeof npubEncode !== "function") {
|
||||
throw new Error("NostrTools nip19 encoder is unavailable.");
|
||||
}
|
||||
const npub = npubEncode(pubkey);
|
||||
|
||||
devLogger.log("Got pubkey:", pubkey);
|
||||
devLogger.log("Converted to npub:", npub);
|
||||
devLogger.log("Whitelist:", accessControl.getWhitelist());
|
||||
devLogger.log("Blacklist:", accessControl.getBlacklist());
|
||||
// Access control
|
||||
if (!accessControl.canAccess(npub)) {
|
||||
if (accessControl.isBlacklisted(npub)) {
|
||||
throw new Error("Your account has been blocked on this platform.");
|
||||
} else {
|
||||
throw new Error("Access restricted to admins and moderators users only.");
|
||||
}
|
||||
}
|
||||
this.pubkey = pubkey;
|
||||
devLogger.log("Logged in with extension. Pubkey:", this.pubkey);
|
||||
|
||||
const adapter = await createNip07Adapter(extension);
|
||||
adapter.pubkey = pubkey;
|
||||
setActiveSigner(adapter);
|
||||
|
||||
const postLoginPermissions = await this.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
if (!postLoginPermissions.ok && postLoginPermissions.error) {
|
||||
userLogger.warn(
|
||||
"[nostr] Extension permissions were not fully granted after login:",
|
||||
postLoginPermissions.error,
|
||||
);
|
||||
}
|
||||
return this.pubkey;
|
||||
} catch (err) {
|
||||
userLogger.error("Login error:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out the current user and clears all session state.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
import {
|
||||
waitForNip07Extension,
|
||||
runNip07WithRetry,
|
||||
requestEnablePermissions,
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
} from "../../nostr/nip07Permissions.js";
|
||||
import { createNip07Adapter } from "../../nostr/adapters/nip07Adapter.js";
|
||||
import { setActiveSigner } from "../../nostrClientRegistry.js";
|
||||
import { ensureNostrTools } from "../../nostr/toolkit.js";
|
||||
import { accessControl } from "../../accessControl.js";
|
||||
import { devLogger, userLogger } from "../../utils/logger.js";
|
||||
|
||||
const PROVIDER_ID = "nip07";
|
||||
const PROVIDER_LABEL = "extension (nip-07)";
|
||||
const PROVIDER_DESCRIPTION =
|
||||
@@ -43,6 +55,180 @@ function normalizePubkey(result) {
|
||||
return "";
|
||||
}
|
||||
|
||||
async function performNip07Login(options = {}, nostrClient = null) {
|
||||
let extension = null;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 3;
|
||||
|
||||
// Optimistic check: if window.nostr is already there, use it immediately.
|
||||
if (typeof window !== "undefined" && window.nostr) {
|
||||
extension = window.nostr;
|
||||
} else {
|
||||
while (!extension && attempts < maxAttempts) {
|
||||
try {
|
||||
// Fast wait for the first attempt, slightly longer for retries
|
||||
const timeout = attempts === 0 ? 1500 : 3000;
|
||||
await waitForNip07Extension(timeout);
|
||||
extension = window.nostr;
|
||||
} catch (waitError) {
|
||||
devLogger.log(
|
||||
`Timed out waiting for extension injection (attempt ${
|
||||
attempts + 1
|
||||
}/${maxAttempts}):`,
|
||||
waitError,
|
||||
);
|
||||
}
|
||||
|
||||
if (extension) {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
if (attempts < maxAttempts) {
|
||||
devLogger.log("Retrying NIP-07 detection...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!extension) {
|
||||
devLogger.log("No Nostr extension found");
|
||||
throw new Error(
|
||||
"Please install a Nostr extension (Alby, nos2x, etc.)."
|
||||
);
|
||||
}
|
||||
|
||||
const { allowAccountSelection = false, expectPubkey } =
|
||||
typeof options === "object" && options !== null ? options : {};
|
||||
const normalizedExpectedPubkey =
|
||||
typeof expectPubkey === "string" && expectPubkey.trim()
|
||||
? expectPubkey.trim().toLowerCase()
|
||||
: null;
|
||||
|
||||
if (typeof extension.getPublicKey !== "function") {
|
||||
throw new Error(
|
||||
"This NIP-07 extension is missing getPublicKey support. Please update the extension."
|
||||
);
|
||||
}
|
||||
|
||||
// Use the client instance if available to ensure permission cache is updated
|
||||
if (nostrClient && typeof nostrClient.ensureExtensionPermissions === "function") {
|
||||
const permissionResult = await nostrClient.ensureExtensionPermissions(
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
if (!permissionResult.ok) {
|
||||
const denialMessage =
|
||||
'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;
|
||||
}
|
||||
throw denialError;
|
||||
}
|
||||
} else {
|
||||
const permissionResult = await requestEnablePermissions(
|
||||
extension,
|
||||
DEFAULT_NIP07_PERMISSION_METHODS,
|
||||
);
|
||||
if (!permissionResult.ok) {
|
||||
const denialMessage =
|
||||
'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;
|
||||
}
|
||||
throw denialError;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowAccountSelection && typeof extension.selectAccounts === "function") {
|
||||
try {
|
||||
const selection = await runNip07WithRetry(
|
||||
() => extension.selectAccounts(expectPubkey ? [expectPubkey] : undefined),
|
||||
{ label: "extension.selectAccounts" }
|
||||
);
|
||||
|
||||
const didCancelSelection =
|
||||
selection === undefined ||
|
||||
selection === null ||
|
||||
selection === false ||
|
||||
(Array.isArray(selection) && selection.length === 0);
|
||||
|
||||
if (didCancelSelection) {
|
||||
throw new Error("Account selection was cancelled.");
|
||||
}
|
||||
} catch (selectionErr) {
|
||||
const message =
|
||||
selectionErr && typeof selectionErr.message === "string"
|
||||
? selectionErr.message
|
||||
: "Account selection was cancelled.";
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
const pubkey = await runNip07WithRetry(() => extension.getPublicKey(), {
|
||||
label: "extension.getPublicKey",
|
||||
});
|
||||
|
||||
if (!pubkey || typeof pubkey !== "string") {
|
||||
throw new Error(
|
||||
"The NIP-07 extension did not return a public key. Please try again."
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
normalizedExpectedPubkey &&
|
||||
pubkey.toLowerCase() !== normalizedExpectedPubkey
|
||||
) {
|
||||
throw new Error(
|
||||
"The selected account doesn't match the expected profile. Please try again."
|
||||
);
|
||||
}
|
||||
|
||||
const nip19Tools = await ensureNostrTools();
|
||||
const npubEncode = nip19Tools?.nip19?.npubEncode;
|
||||
if (typeof npubEncode !== "function") {
|
||||
throw new Error("NostrTools nip19 encoder is unavailable.");
|
||||
}
|
||||
const npub = npubEncode(pubkey);
|
||||
|
||||
devLogger.log("Got pubkey:", pubkey);
|
||||
devLogger.log("Converted to npub:", npub);
|
||||
|
||||
// Access control
|
||||
if (!accessControl.canAccess(npub)) {
|
||||
if (accessControl.isBlacklisted(npub)) {
|
||||
throw new Error("Your account has been blocked on this platform.");
|
||||
} else {
|
||||
throw new Error("Access restricted to admins and moderators users only.");
|
||||
}
|
||||
}
|
||||
|
||||
const adapter = await createNip07Adapter(extension);
|
||||
adapter.pubkey = pubkey;
|
||||
setActiveSigner(adapter);
|
||||
|
||||
// Ensure full permissions are requested post-login (e.g. encryption)
|
||||
// This is non-blocking to avoid stalling if the user already approved
|
||||
if (nostrClient && typeof nostrClient.ensureExtensionPermissions === "function") {
|
||||
nostrClient.ensureExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS).catch((err) => {
|
||||
userLogger.warn(
|
||||
"[nostr] Extension permissions were not fully granted after login:",
|
||||
err
|
||||
);
|
||||
});
|
||||
} else {
|
||||
requestEnablePermissions(extension, DEFAULT_NIP07_PERMISSION_METHODS).catch((err) => {
|
||||
userLogger.warn(
|
||||
"[nostr] Extension permissions were not fully granted after login:",
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { pubkey, signer: adapter };
|
||||
}
|
||||
|
||||
export default {
|
||||
id: PROVIDER_ID,
|
||||
label: PROVIDER_LABEL,
|
||||
@@ -55,13 +241,7 @@ export default {
|
||||
messages: PROVIDER_MESSAGES,
|
||||
errorMessage: PROVIDER_MESSAGES.error,
|
||||
async login({ nostrClient, options } = {}) {
|
||||
if (!nostrClient || typeof nostrClient.login !== "function") {
|
||||
const error = new Error("Nostr login is not available.");
|
||||
error.code = "provider-unavailable";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const result = await nostrClient.login(options || {});
|
||||
const result = await performNip07Login(options, nostrClient);
|
||||
const pubkey = normalizePubkey(result);
|
||||
const signer =
|
||||
result && typeof result === "object" && result.signer
|
||||
|
||||
@@ -374,18 +374,6 @@ export default class AuthService {
|
||||
const detailAuthType =
|
||||
typeof authType === "string" && authType.trim() ? authType.trim() : "nip07";
|
||||
|
||||
if (authType === "nip07") {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult.ok) {
|
||||
const error = new Error(
|
||||
"The NIP-07 extension denied the permission request required to finish logging in.",
|
||||
);
|
||||
error.code = "extension-permission-denied";
|
||||
error.cause = permissionResult.error;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const trimmed = typeof pubkey === "string" ? pubkey.trim() : "";
|
||||
if (!trimmed) {
|
||||
return { pubkey: null, providerId, authType: detailAuthType, signer };
|
||||
|
||||
@@ -729,6 +729,8 @@ class UserBlockListManager {
|
||||
emitStatus({ status: "loading", relays: Array.from(nostrClient.relays || []) });
|
||||
|
||||
const localBlocks = this._loadLocal(normalized);
|
||||
const hasLocalData = !!localBlocks;
|
||||
|
||||
if (localBlocks) {
|
||||
this.blockedPubkeys = localBlocks;
|
||||
this.loaded = true;
|
||||
@@ -1067,18 +1069,24 @@ class UserBlockListManager {
|
||||
}
|
||||
};
|
||||
|
||||
// If we don't have local data, we must ignore the sync metadata and force a full fetch (since: 0).
|
||||
// Otherwise, we might miss data if the metadata store says we are up-to-date but the actual list is missing.
|
||||
const fetchSince = hasLocalData ? undefined : 0;
|
||||
|
||||
// Concurrent incremental fetch for both kinds
|
||||
const [muteEvents, blockEvents] = await Promise.all([
|
||||
nostrClient.fetchListIncrementally({
|
||||
kind: 10000,
|
||||
pubkey: normalized,
|
||||
relayUrls: relays,
|
||||
since: fetchSince,
|
||||
}),
|
||||
nostrClient.fetchListIncrementally({
|
||||
kind: 30002,
|
||||
pubkey: normalized,
|
||||
dTag: BLOCK_LIST_IDENTIFIER,
|
||||
relayUrls: relays,
|
||||
since: fetchSince,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -242,16 +242,19 @@ test(
|
||||
const signedEvent = await hashtagPreferences.publish();
|
||||
assert.equal(signedEvent.id, "signed-event");
|
||||
|
||||
assert.equal(plaintextCalls.length, 1);
|
||||
assert.equal(plaintextCalls[0].target, pubkey);
|
||||
const payload = JSON.parse(plaintextCalls[0].plaintext);
|
||||
// Expect 3 calls: addInterest (auto-save), addDisinterest (auto-save), manual publish
|
||||
assert.equal(plaintextCalls.length, 3);
|
||||
const lastCall = plaintextCalls[plaintextCalls.length - 1];
|
||||
assert.equal(lastCall.target, pubkey);
|
||||
const payload = JSON.parse(lastCall.plaintext);
|
||||
assert.equal(payload.version, 1);
|
||||
assert.deepEqual(payload.interests, ["taga"]);
|
||||
assert.deepEqual(payload.disinterests, ["tagb"]);
|
||||
|
||||
assert.equal(publishedEvents.length, 1);
|
||||
const publishedEvent = publishedEvents[0].event;
|
||||
assert.deepEqual(publishedEvents[0].urls, ["wss://relay.publish"]);
|
||||
// Expect 3 publish events
|
||||
assert.equal(publishedEvents.length, 3);
|
||||
const publishedEvent = publishedEvents[publishedEvents.length - 1].event;
|
||||
assert.deepEqual(publishedEvents[publishedEvents.length - 1].urls, ["wss://relay.publish"]);
|
||||
assert.equal(publishedEvent.kind, 30015);
|
||||
const encryptedTag = publishedEvent.tags.find(
|
||||
(tag) => Array.isArray(tag) && tag[0] === "encrypted",
|
||||
@@ -310,16 +313,18 @@ test(
|
||||
const signedEvent = await hashtagPreferences.publish();
|
||||
|
||||
assert.equal(signedEvent.id, "signed-window");
|
||||
// We expect 1 call since the first scheme (nip44_v2) succeeds
|
||||
assert.equal(encryptCalls.length, 1);
|
||||
assert.equal(encryptCalls[0].scheme, "nip44_call");
|
||||
assert.equal(encryptCalls[0].target, pubkey);
|
||||
assert.ok(encryptCalls[0].plaintext.includes("windowtag"));
|
||||
// Expect 2 calls: addInterest (auto-save) + manual publish
|
||||
assert.equal(encryptCalls.length, 2);
|
||||
const lastEncryptCall = encryptCalls[encryptCalls.length - 1];
|
||||
assert.equal(lastEncryptCall.scheme, "nip44_call");
|
||||
assert.equal(lastEncryptCall.target, pubkey);
|
||||
assert.ok(lastEncryptCall.plaintext.includes("windowtag"));
|
||||
|
||||
assert.equal(publishedEvents.length, 1);
|
||||
assert.deepEqual(publishedEvents[0].urls, ["wss://relay.window"]);
|
||||
assert.equal(publishedEvents[0].event.kind, 30015);
|
||||
const encryptedTag = publishedEvents[0].event.tags.find(
|
||||
assert.equal(publishedEvents.length, 2);
|
||||
const lastPublished = publishedEvents[publishedEvents.length - 1];
|
||||
assert.deepEqual(lastPublished.urls, ["wss://relay.window"]);
|
||||
assert.equal(lastPublished.event.kind, 30015);
|
||||
const encryptedTag = lastPublished.event.tags.find(
|
||||
(tag) => Array.isArray(tag) && tag[0] === "encrypted",
|
||||
);
|
||||
// nip44_v2 is the first tried scheme, so it is the one used
|
||||
|
||||
@@ -4,6 +4,7 @@ import test from "node:test";
|
||||
|
||||
import { nostrClient } from "../js/nostrClientFacade.js";
|
||||
import { NostrClient, __testExports } from "../js/nostr/client.js";
|
||||
import nip07Provider from "../js/services/authProviders/nip07.js";
|
||||
import { accessControl } from "../js/accessControl.js";
|
||||
|
||||
const {
|
||||
@@ -150,7 +151,8 @@ function setupLoginEnvironment({ enableImpl, getPublicKey = HEX_PUBKEY } = {}) {
|
||||
test("NIP-07 login requests decrypt permissions upfront", async () => {
|
||||
const env = setupLoginEnvironment();
|
||||
try {
|
||||
const pubkey = await nostrClient.login();
|
||||
const result = await nip07Provider.login({ nostrClient });
|
||||
const pubkey = result.pubkey;
|
||||
assert.equal(pubkey, HEX_PUBKEY);
|
||||
assert.ok(env.enableCalls.length >= 1, "extension.enable should be invoked");
|
||||
assert.ok(
|
||||
@@ -208,7 +210,8 @@ test("NIP-07 decrypt reuses cached extension permissions", async () => {
|
||||
},
|
||||
};
|
||||
|
||||
const pubkey = await nostrClient.login();
|
||||
const result = await nip07Provider.login({ nostrClient });
|
||||
const pubkey = result.pubkey;
|
||||
assert.equal(pubkey, HEX_PUBKEY);
|
||||
for (const method of EXPECTED_ENCRYPTION_PERMISSIONS) {
|
||||
assert.ok(
|
||||
@@ -306,7 +309,8 @@ test("NIP-07 login falls back when structured permissions fail", async () => {
|
||||
});
|
||||
|
||||
try {
|
||||
const pubkey = await nostrClient.login();
|
||||
const result = await nip07Provider.login({ nostrClient });
|
||||
const pubkey = result.pubkey;
|
||||
assert.equal(pubkey, HEX_PUBKEY);
|
||||
assert.ok(env.enableCalls.length >= 2, "should retry with alternate payloads");
|
||||
const [objectCall, stringCall, plainCall] = env.enableCalls;
|
||||
@@ -346,7 +350,8 @@ test("NIP-07 login supports extensions that only allow enable() without payload"
|
||||
});
|
||||
|
||||
try {
|
||||
const pubkey = await nostrClient.login();
|
||||
const result = await nip07Provider.login({ nostrClient });
|
||||
const pubkey = result.pubkey;
|
||||
assert.equal(pubkey, HEX_PUBKEY);
|
||||
assert.equal(
|
||||
env.enableCalls.length,
|
||||
@@ -398,7 +403,8 @@ test("NIP-07 login quickly retries when a permission payload stalls", async () =
|
||||
|
||||
try {
|
||||
const start = Date.now();
|
||||
const pubkey = await nostrClient.login();
|
||||
const result = await nip07Provider.login({ nostrClient });
|
||||
const pubkey = result.pubkey;
|
||||
const duration = Date.now() - start;
|
||||
assert.equal(pubkey, HEX_PUBKEY);
|
||||
assert.ok(
|
||||
@@ -432,7 +438,7 @@ test("NIP-07 login surfaces enable permission errors", async () => {
|
||||
});
|
||||
|
||||
try {
|
||||
await assert.rejects(() => nostrClient.login(), /permission denied/);
|
||||
await assert.rejects(() => nip07Provider.login({ nostrClient }), /permission denied/);
|
||||
} finally {
|
||||
env.restore();
|
||||
nostrClient.logout();
|
||||
|
||||
Reference in new issue
Block a user