UNPKG

shogun-core

Version:

SHOGUN CORE - Core library for Shogun Ecosystem

426 lines (425 loc) 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NostrConnectorPlugin = void 0; const base_1 = require("../base"); const nostrConnector_1 = require("./nostrConnector"); const nostrSigner_1 = require("./nostrSigner"); const errorHandler_1 = require("../../utils/errorHandler"); /** * Plugin for managing Bitcoin wallet functionality in ShogunCore * Supports Alby, Nostr extensions, or direct key management */ class NostrConnectorPlugin extends base_1.BasePlugin { name = "nostr"; version = "1.0.0"; description = "Provides Bitcoin wallet connection and authentication for ShogunCore"; bitcoinConnector = null; signer = null; /** * @inheritdoc */ initialize(core) { super.initialize(core); // Initialize the Bitcoin wallet module this.bitcoinConnector = new nostrConnector_1.NostrConnector(); this.signer = new nostrSigner_1.NostrSigner(this.bitcoinConnector); } /** * @inheritdoc */ destroy() { if (this.bitcoinConnector) { this.bitcoinConnector.cleanup(); } this.bitcoinConnector = null; this.signer = null; super.destroy(); } /** * Ensure that the Bitcoin wallet module is initialized * @private */ assertBitcoinConnector() { this.assertInitialized(); if (!this.bitcoinConnector) { throw new Error("Bitcoin wallet module not initialized"); } return this.bitcoinConnector; } /** * Assicura che il signer sia inizializzato * @private */ assertSigner() { this.assertInitialized(); if (!this.signer) { throw new Error("Nostr signer not initialized"); } return this.signer; } /** * @inheritdoc */ isAvailable() { return this.assertBitcoinConnector().isAvailable(); } /** * Check if Alby extension is available * Note: Alby is deprecated in favor of Nostr */ isAlbyAvailable() { return this.isNostrExtensionAvailable(); } /** * Check if Nostr extension is available */ isNostrExtensionAvailable() { return this.assertBitcoinConnector().isNostrExtensionAvailable(); } /** * Connect to Nostr wallet automatically * This is a convenience method for easy wallet connection */ async connectNostrWallet() { try { if (!this.isNostrExtensionAvailable()) { return { success: false, error: "Nostr extension not available. Please install a Nostr extension like nos2x, Alby, or Coracle.", }; } const result = await this.connectBitcoinWallet("nostr"); if (result.success) { } return result; } catch (error) { console.error("[nostrConnectorPlugin] Error connecting to Nostr wallet:", error); return { success: false, error: error.message || "Unknown error connecting to Nostr wallet", }; } } /** * @inheritdoc */ async connectBitcoinWallet(type = "nostr") { // Prioritize nostr over alby (since they are functionally identical) // If type is alby, try to use nostr instead if (type === "alby") { type = "nostr"; } return this.assertBitcoinConnector().connectWallet(type); } /** * @inheritdoc */ async generateCredentials(address, signature, message) { return this.assertBitcoinConnector().generateCredentials(address, signature, message); } /** * @inheritdoc */ cleanup() { this.assertBitcoinConnector().cleanup(); } /** * Clear signature cache for better user recovery * @param address - Optional specific address to clear, or clear all if not provided */ clearSignatureCache(address) { this.assertBitcoinConnector().clearSignatureCache(address); } /** * @inheritdoc */ async verifySignature(message, signature, address) { return this.assertBitcoinConnector().verifySignature(message, signature, address); } /** * @inheritdoc */ async generatePassword(signature) { return this.assertBitcoinConnector().generatePassword(signature); } // === NOSTR SIGNER METHODS === /** * Creates a new Nostr signing credential * CONSISTENT with normal Nostr approach */ async createSigningCredential(address) { try { return await this.assertSigner().createSigningCredential(address); } catch (error) { console.error(`Error creating Nostr signing credential: ${error.message}`); throw error; } } /** * Creates an authenticator function for Nostr signing */ createAuthenticator(address) { try { return this.assertSigner().createAuthenticator(address); } catch (error) { console.error(`Error creating Nostr authenticator: ${error.message}`); throw error; } } /** * Creates a derived key pair from Nostr credential */ async createDerivedKeyPair(address, extra) { try { return await this.assertSigner().createDerivedKeyPair(address, extra); } catch (error) { console.error(`Error creating derived key pair: ${error.message}`); throw error; } } /** * Signs data with derived keys after Nostr verification */ async signWithDerivedKeys(data, address, extra) { try { return await this.assertSigner().signWithDerivedKeys(data, address, extra); } catch (error) { console.error(`Error signing with derived keys: ${error.message}`); throw error; } } /** * Get signing credential by address */ getSigningCredential(address) { return this.assertSigner().getCredential(address); } /** * List all signing credentials */ listSigningCredentials() { return this.assertSigner().listCredentials(); } /** * Remove a signing credential */ removeSigningCredential(address) { return this.assertSigner().removeCredential(address); } // === CONSISTENCY METHODS === /** * Creates a Gun user from Nostr signing credential * This ensures the SAME user is created as with normal approach */ async createGunUserFromSigningCredential(address) { try { const core = this.assertInitialized(); return await this.assertSigner().createGunUser(address, core.gun); } catch (error) { console.error(`Error creating Gun user from Nostr signing credential: ${error.message}`); throw error; } } /** * Get the Gun user public key for a signing credential */ getGunUserPubFromSigningCredential(address) { return this.assertSigner().getGunUserPub(address); } /** * Get the password (for consistency checking) */ getPassword(address) { return this.assertSigner().getPassword(address); } /** * Verify consistency between oneshot and normal approaches * This ensures both approaches create the same Gun user */ async verifyConsistency(address, expectedUserPub) { try { return await this.assertSigner().verifyConsistency(address, expectedUserPub); } catch (error) { console.error(`Error verifying Nostr consistency: ${error.message}`); return { consistent: false }; } } /** * Complete oneshot workflow that creates the SAME Gun user as normal approach * This is the recommended method for oneshot signing with full consistency */ async setupConsistentOneshotSigning(address) { try { // 1. Create signing credential (with consistent password generation) const credential = await this.createSigningCredential(address); // 2. Create authenticator const authenticator = this.createAuthenticator(address); // 3. Create Gun user (same as normal approach) const gunUser = await this.createGunUserFromSigningCredential(address); return { credential, authenticator, gunUser, username: credential.username, password: credential.password, }; } catch (error) { console.error(`Error setting up consistent Nostr oneshot signing: ${error.message}`); throw error; } } // === EXISTING METHODS === /** * Login with Bitcoin wallet * @param address - Bitcoin address * @returns {Promise<AuthResult>} Authentication result * @description Authenticates the user using Bitcoin wallet credentials after signature verification */ async login(address) { try { const core = this.assertInitialized(); if (!address) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.VALIDATION, "ADDRESS_REQUIRED", "Bitcoin address required for login"); } if (!this.isAvailable()) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.ENVIRONMENT, "BITCOIN_WALLET_UNAVAILABLE", "No Bitcoin wallet available in the browser"); } const message = nostrConnector_1.MESSAGE_TO_SIGN; const signature = await this.assertBitcoinConnector().requestSignature(address, message); const credentials = await this.generateCredentials(address, signature, message); if (!credentials?.username || !credentials?.key || !credentials.message || !credentials.signature) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.AUTHENTICATION, "CREDENTIAL_GENERATION_FAILED", "Bitcoin wallet credentials not generated correctly or signature missing"); } const isValid = await this.verifySignature(credentials.message, credentials.signature, address); if (!isValid) { console.error(`Signature verification failed for address: ${address}`); throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.SECURITY, "SIGNATURE_VERIFICATION_FAILED", "Bitcoin wallet signature verification failed"); } // Deriva le chiavi da address, signature, message const k = await (0, nostrConnector_1.deriveNostrKeys)(address, signature, message); // Set authentication method to nostr before login core.setAuthMethod("nostr"); // Usa le chiavi derivate per login const loginResult = await core.login(credentials.username, "", k); if (!loginResult.success) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.AUTHENTICATION, "BITCOIN_LOGIN_FAILED", loginResult.error || "Failed to log in with Bitcoin credentials"); } // Emit login event core.emit("auth:login", { userPub: loginResult.userPub || "", username: credentials.username, method: "bitcoin", }); return loginResult; } catch (error) { // Handle both ShogunError and generic errors const errorType = error?.type || errorHandler_1.ErrorType.AUTHENTICATION; const errorCode = error?.code || "BITCOIN_LOGIN_ERROR"; const errorMessage = error?.message || "Unknown error during Bitcoin wallet login"; const handledError = errorHandler_1.ErrorHandler.handle(errorType, errorCode, errorMessage, error); return { success: false, error: handledError.message, }; } } /** * Register new user with Nostr wallet * @param address - Nostr address * @returns {Promise<SignUpResult>} Registration result */ async signUp(address) { try { const core = this.assertInitialized(); if (!address) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.VALIDATION, "ADDRESS_REQUIRED", "Bitcoin address required for signup"); } if (!this.isAvailable()) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.ENVIRONMENT, "BITCOIN_WALLET_UNAVAILABLE", "No Bitcoin wallet available in the browser"); } const message = nostrConnector_1.MESSAGE_TO_SIGN; const signature = await this.assertBitcoinConnector().requestSignature(address, message); const credentials = await this.generateCredentials(address, signature, message); if (!credentials?.username || !credentials?.key || !credentials.message || !credentials.signature) { throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.AUTHENTICATION, "CREDENTIAL_GENERATION_FAILED", "Bitcoin wallet credentials not generated correctly or signature missing"); } // Verify signature const isValid = await this.verifySignature(credentials.message, credentials.signature, address); if (!isValid) { console.error(`Signature verification failed for address: ${address}`); throw (0, errorHandler_1.createError)(errorHandler_1.ErrorType.SECURITY, "SIGNATURE_VERIFICATION_FAILED", "Bitcoin wallet signature verification failed"); } // Deriva le chiavi da address, signature, message const k = await (0, nostrConnector_1.deriveNostrKeys)(address, signature, message); // Set authentication method to nostr before signup core.setAuthMethod("nostr"); // Usa le chiavi derivate per signup const signupResult = await core.signUp(credentials.username, "", "", k); if (signupResult.success) { // Dopo la creazione, autentica subito const authResult = await core.login(credentials.username, "", k); if (authResult.success) { console.log(`Bitcoin wallet registration and login completed for user: ${credentials.username}`); // Emetti eventi core.emit("auth:signup", { userPub: authResult.userPub || "", username: credentials.username, method: "bitcoin", }); return { ...authResult }; } else { return { ...signupResult, error: "User created but login failed" }; } } else { // Se l'errore è che l'utente esiste già, prova direttamente l'auth if (signupResult.error && signupResult.error.toLowerCase().includes("exist")) { const authResult = await core.login(credentials.username, "", k); return { ...authResult }; } return signupResult; } } catch (error) { // Handle both ShogunError and generic errors const errorType = error?.type || errorHandler_1.ErrorType.AUTHENTICATION; const errorCode = error?.code || "BITCOIN_SIGNUP_ERROR"; const errorMessage = error?.message || "Unknown error during Bitcoin wallet signup"; const handledError = errorHandler_1.ErrorHandler.handle(errorType, errorCode, errorMessage, error); return { success: false, error: handledError.message, }; } } /** * Convenience method that matches the interface pattern */ async loginWithBitcoinWallet(address) { return this.login(address); } /** * Convenience method that matches the interface pattern */ async signUpWithBitcoinWallet(address) { return this.signUp(address); } } exports.NostrConnectorPlugin = NostrConnectorPlugin;