mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
refactor(auth): consolidate login logic in NostrClient and enhance security
- Move NIP-07 extension login logic from `js/services/authProviders/nip07.js` to `NostrClient.loginWithExtension`. - Add `validator` support to `NostrClient` login methods (`loginWithExtension`, `connectRemoteSigner`, `useStoredRemoteSigner`, `registerPrivateKeySigner`) to allow pre-session security checks. - Update `nip07.js`, `nip46.js`, and `nsec.js` providers to inject `accessControl` checks via the new validator hook, ensuring consistent enforcement of whitelists/blacklists before activating the signer. - This creates a unified "harness" for all signing methods within `NostrClient` while keeping business logic (access control) in the service layer. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
287671aca5
commit
16ad3320fe
5 files changed
+330
-186
No files matched your search
@@ -1900,6 +1900,157 @@ export class NostrClient {
|
||||
* 4. **Authorize**: Handles any `auth_url` challenges if the signer requires out-of-band approval.
|
||||
* 5. **Session**: Stores the session metadata locally (if `remember: true`) to allow auto-reconnect.
|
||||
*/
|
||||
async loginWithExtension(options = {}) {
|
||||
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.",
|
||||
);
|
||||
}
|
||||
|
||||
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.",
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof options.validator === "function") {
|
||||
try {
|
||||
const isValid = await options.validator(pubkey);
|
||||
if (!isValid) {
|
||||
throw new Error("Access denied.");
|
||||
}
|
||||
} catch (validationError) {
|
||||
const message =
|
||||
validationError instanceof Error
|
||||
? validationError.message
|
||||
: "Access denied.";
|
||||
const error = new Error(message);
|
||||
error.code = "access-denied";
|
||||
if (validationError instanceof Error) {
|
||||
error.cause = validationError;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const adapter = await createNip07Adapter(extension);
|
||||
adapter.pubkey = pubkey;
|
||||
setActiveSigner(adapter);
|
||||
|
||||
this.ensureExtensionPermissions(DEFAULT_NIP07_PERMISSION_METHODS).catch(
|
||||
(err) => {
|
||||
userLogger.warn(
|
||||
"[nostr] Extension permissions were not fully granted after login:",
|
||||
err,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return { pubkey, signer: adapter };
|
||||
}
|
||||
|
||||
async connectRemoteSigner({
|
||||
connectionString,
|
||||
remember = true,
|
||||
@@ -2201,6 +2352,25 @@ export class NostrClient {
|
||||
remotePubkey: summarizeHexForLog(remotePubkey),
|
||||
userPubkey: summarizeHexForLog(userPubkey),
|
||||
});
|
||||
|
||||
if (typeof options?.validator === "function") {
|
||||
try {
|
||||
const isValid = await options.validator(userPubkey);
|
||||
if (!isValid) {
|
||||
throw new Error("Access denied.");
|
||||
}
|
||||
} catch (validationError) {
|
||||
const message =
|
||||
validationError instanceof Error
|
||||
? validationError.message
|
||||
: "Access denied.";
|
||||
const error = new Error(message);
|
||||
error.code = "access-denied";
|
||||
client.destroy(); // Cleanup client since we won't use it
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
client.metadata = metadata;
|
||||
const signer = await this.installNip46Client(client, { userPubkey });
|
||||
|
||||
@@ -2296,6 +2466,7 @@ export class NostrClient {
|
||||
options && typeof options === "object" ? options : {};
|
||||
const silent = normalizedOptions.silent === true;
|
||||
const forgetOnError = normalizedOptions.forgetOnError === true;
|
||||
const validator = typeof normalizedOptions.validator === "function" ? normalizedOptions.validator : null;
|
||||
|
||||
const stored = readStoredNip46Session();
|
||||
if (!stored) {
|
||||
@@ -2344,6 +2515,25 @@ export class NostrClient {
|
||||
await client.ensureSubscription();
|
||||
await client.connect({ permissions: stored.permissions });
|
||||
const userPubkey = await client.getUserPubkey();
|
||||
|
||||
if (validator) {
|
||||
try {
|
||||
const isValid = await validator(userPubkey);
|
||||
if (!isValid) {
|
||||
throw new Error("Access denied.");
|
||||
}
|
||||
} catch (validationError) {
|
||||
const message =
|
||||
validationError instanceof Error
|
||||
? validationError.message
|
||||
: "Access denied.";
|
||||
const error = new Error(message);
|
||||
error.code = "access-denied";
|
||||
client.destroy();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
client.metadata = stored.metadata;
|
||||
const signer = await this.installNip46Client(client, { userPubkey });
|
||||
|
||||
@@ -3122,6 +3312,7 @@ export class NostrClient {
|
||||
pubkey,
|
||||
persist = false,
|
||||
passphrase,
|
||||
validator,
|
||||
} = {}) {
|
||||
const normalizedPrivateKey =
|
||||
typeof privateKey === "string" && HEX64_REGEX.test(privateKey)
|
||||
@@ -3158,6 +3349,23 @@ export class NostrClient {
|
||||
throw new Error("Unable to resolve the public key for this private key.");
|
||||
}
|
||||
|
||||
if (typeof validator === "function") {
|
||||
try {
|
||||
const isValid = await validator(normalizedPubkey);
|
||||
if (!isValid) {
|
||||
throw new Error("Access denied.");
|
||||
}
|
||||
} catch (validationError) {
|
||||
const message =
|
||||
validationError instanceof Error
|
||||
? validationError.message
|
||||
: "Access denied.";
|
||||
const error = new Error(message);
|
||||
error.code = "access-denied";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
this.sessionActor = {
|
||||
pubkey: normalizedPubkey,
|
||||
privateKey: normalizedPrivateKey,
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
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)";
|
||||
@@ -55,180 +45,6 @@ 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,
|
||||
@@ -241,7 +57,23 @@ export default {
|
||||
messages: PROVIDER_MESSAGES,
|
||||
errorMessage: PROVIDER_MESSAGES.error,
|
||||
async login({ nostrClient, options } = {}) {
|
||||
const result = await performNip07Login(options, nostrClient);
|
||||
if (!nostrClient || typeof nostrClient.loginWithExtension !== "function") {
|
||||
const error = new Error("Extension login is not available in this environment.");
|
||||
error.code = "provider-unavailable";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const validator = (pubkey) => {
|
||||
if (!accessControl.canAccess(pubkey)) {
|
||||
if (accessControl.isBlacklisted(pubkey)) {
|
||||
throw new Error("Your account has been blocked on this platform.");
|
||||
}
|
||||
throw new Error("Access restricted to admins and moderators users only.");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const result = await nostrClient.loginWithExtension({ ...options, validator });
|
||||
const pubkey = normalizePubkey(result);
|
||||
const signer =
|
||||
result && typeof result === "object" && result.signer
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { devLogger } from "../../utils/logger.js";
|
||||
import { accessControl } from "../../accessControl.js";
|
||||
|
||||
function summarizeHexForLog(value) {
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
@@ -154,6 +155,16 @@ export default {
|
||||
});
|
||||
let result = null;
|
||||
|
||||
const validator = (pubkey) => {
|
||||
if (!accessControl.canAccess(pubkey)) {
|
||||
if (accessControl.isBlacklisted(pubkey)) {
|
||||
throw new Error("Your account has been blocked on this platform.");
|
||||
}
|
||||
throw new Error("Access restricted to admins and moderators users only.");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (normalized.reuseStored) {
|
||||
if (typeof nostrClient.useStoredRemoteSigner !== "function") {
|
||||
const error = new Error("No stored remote signer is available on this device.");
|
||||
@@ -162,7 +173,15 @@ export default {
|
||||
}
|
||||
devLogger.debug("[nip46] Reusing stored remote signer session");
|
||||
|
||||
result = await nostrClient.useStoredRemoteSigner();
|
||||
// useStoredRemoteSigner should ideally accept a validator too, but if it restores a session,
|
||||
// it might not expose a validator hook easily if not updated.
|
||||
// Let's check NostrClient.useStoredRemoteSigner in client.js.
|
||||
// It DOES NOT take a validator in my previous update (I missed it).
|
||||
// I should update client.js to add validator to useStoredRemoteSigner too.
|
||||
// For now, I will assume I will fix client.js later or accept that stored sessions are trusted?
|
||||
// No, I must fix useStoredRemoteSigner too.
|
||||
|
||||
result = await nostrClient.useStoredRemoteSigner({ validator });
|
||||
devLogger.debug("[nip46] Stored remote signer returned", {
|
||||
pubkey: summarizeHexForLog(normalizePubkey(result)),
|
||||
});
|
||||
@@ -184,6 +203,7 @@ export default {
|
||||
remember: normalized.remember,
|
||||
onAuthUrl: normalized.onAuthUrl,
|
||||
onStatus: normalized.onStatus,
|
||||
validator,
|
||||
});
|
||||
devLogger.debug("[nip46] Manual connect completed", {
|
||||
pubkey: summarizeHexForLog(normalizePubkey(result)),
|
||||
@@ -243,6 +263,7 @@ export default {
|
||||
onAuthUrl: normalized.onAuthUrl,
|
||||
onStatus: normalized.onStatus,
|
||||
handshakeTimeoutMs: normalized.handshakeTimeoutMs,
|
||||
validator,
|
||||
});
|
||||
|
||||
if (result && typeof result === "object") {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { accessControl } from "../../accessControl.js";
|
||||
|
||||
const PROVIDER_ID = "nsec";
|
||||
const PROVIDER_LABEL = "nsec or seed (direct private key)";
|
||||
const PROVIDER_DESCRIPTION =
|
||||
@@ -131,11 +133,22 @@ export default {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const validator = (pubkey) => {
|
||||
if (!accessControl.canAccess(pubkey)) {
|
||||
if (accessControl.isBlacklisted(pubkey)) {
|
||||
throw new Error("Your account has been blocked on this platform.");
|
||||
}
|
||||
throw new Error("Access restricted to admins and moderators users only.");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const registrationResult = await nostrClient.registerPrivateKeySigner({
|
||||
privateKey,
|
||||
pubkey,
|
||||
persist: shouldPersist,
|
||||
passphrase: shouldPersist ? passphrase : undefined,
|
||||
validator,
|
||||
});
|
||||
|
||||
const registeredPubkey = normalizePubkey(registrationResult) || pubkey;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import './setup-test-env.js';
|
||||
import { NostrClient, getActiveSigner } from '../../js/nostr/client.js';
|
||||
// import { userLogger, devLogger } from '../../js/utils/logger.js';
|
||||
|
||||
// Polyfill window.nostr
|
||||
const mockPubkey = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
const mockNostr = {
|
||||
getPublicKey: async () => mockPubkey,
|
||||
signEvent: async (e) => {
|
||||
e.sig = "mock-sig";
|
||||
e.id = "mock-id";
|
||||
return e;
|
||||
},
|
||||
nip04: {
|
||||
encrypt: async () => "mock-ciphertext",
|
||||
decrypt: async () => "mock-plaintext"
|
||||
}
|
||||
};
|
||||
|
||||
global.window.nostr = mockNostr;
|
||||
|
||||
async function run() {
|
||||
console.log("Starting NIP-07 Refactor Verification...");
|
||||
|
||||
const client = new NostrClient();
|
||||
// Skip client.init() to avoid network/relay connection attempts in this isolated test
|
||||
// await client.init();
|
||||
|
||||
console.log("Testing loginWithExtension...");
|
||||
try {
|
||||
const result = await client.loginWithExtension();
|
||||
console.log("Login returned:", { pubkey: result.pubkey, hasSigner: !!result.signer });
|
||||
|
||||
if (result.pubkey !== mockPubkey) {
|
||||
throw new Error(`Pubkey mismatch: expected ${mockPubkey}, got ${result.pubkey}`);
|
||||
}
|
||||
|
||||
const activeSigner = getActiveSigner();
|
||||
if (!activeSigner) {
|
||||
throw new Error("Active signer was not set!");
|
||||
}
|
||||
|
||||
if (activeSigner.pubkey !== mockPubkey) {
|
||||
throw new Error(`Active signer pubkey mismatch: ${activeSigner.pubkey}`);
|
||||
}
|
||||
|
||||
console.log("Testing validator failure...");
|
||||
try {
|
||||
await client.loginWithExtension({
|
||||
validator: async () => false // should fail
|
||||
});
|
||||
console.error("Validator failure test FAILED: Should have thrown error");
|
||||
process.exit(1);
|
||||
} catch (error) {
|
||||
if (error.message.includes("Access denied")) {
|
||||
console.log("Validator failure test SUCCESS!");
|
||||
} else {
|
||||
console.error("Validator failure test FAILED: Wrong error", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Verification SUCCESS!");
|
||||
} catch (error) {
|
||||
console.error("Verification FAILED:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
Reference in new issue
Block a user