From e393a4f615fd72432578ce3b747b18b303846b1a Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Thu, 23 Oct 2025 21:49:20 +0200 Subject: [PATCH] fix: resolve multiple critical issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations :** - Fix WebAuthn authentication regression (button reappeared instead of auto-trigger) - Resolve infinite loop of 'process.states is not an array' logs - Fix WebAssembly serialization error 'invalid type: map, expected a sequence' - Improve WebAuthn error handling and timeout management **Modifications :** - Restored automatic WebAuthn triggering in home.ts initHomePage() - Fixed handshake deduplication logic in service.ts using content-based keys - Added membersList validation before WebAssembly calls to prevent empty object errors - Enhanced WebAuthn error handling with specific error messages and increased timeout to 2 minutes - Improved error messages for NotAllowedError, NotSupportedError, and SecurityError **Pages affectées :** - src/pages/home/home.ts: Restored auto WebAuthn trigger, removed manual button - src/services/service.ts: Fixed handshake deduplication and added membersList validation - src/services/secure-credentials.service.ts: Enhanced WebAuthn error handling and timeout --- src/pages/home/home.ts | 16 +++++- src/services/secure-credentials.service.ts | 64 ++++++++++++++++------ src/services/service.ts | 47 ++++++++++++---- 3 files changed, 99 insertions(+), 28 deletions(-) diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index b75b50e..dde82cb 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -524,9 +524,21 @@ export function setupIframePairingButtons() { } } -// Main Pairing Interface - Auto-triggered, no button needed +// Main Pairing Interface - Automatic WebAuthn trigger export function setupMainPairing(): void { - // No button setup needed since authentication is automatic + const container = getCorrectDOM('login-4nk-component') as HTMLElement; + const mainStatus = container.querySelector('#main-status') as HTMLElement; + + if (mainStatus) { + mainStatus.innerHTML = ` +
+

🔐 Secure authentication required

+
+ Initializing secure authentication... +
+ `; + } + console.log('🔐 Main pairing setup - authentication will be automatic'); } diff --git a/src/services/secure-credentials.service.ts b/src/services/secure-credentials.service.ts index 8ee9507..eb109ea 100644 --- a/src/services/secure-credentials.service.ts +++ b/src/services/secure-credentials.service.ts @@ -99,16 +99,32 @@ export class SecureCredentialsService { authenticatorAttachment: "platform", // Force l'authentificateur intégré userVerification: "required" }, - timeout: 60000, + timeout: 120000, // 2 minutes timeout attestation: "direct" }; console.log('🔐 Requesting WebAuthn credential creation for encryption key...'); - // Créer le credential WebAuthn - const credential = await navigator.credentials.create({ - publicKey: publicKeyCredentialCreationOptions - }) as PublicKeyCredential; + // Créer le credential WebAuthn avec gestion d'erreur robuste + let credential: PublicKeyCredential; + try { + credential = await navigator.credentials.create({ + publicKey: publicKeyCredentialCreationOptions + }) as PublicKeyCredential; + } catch (error) { + if (error instanceof Error) { + if (error.name === 'NotAllowedError') { + throw new Error('WebAuthn authentication was cancelled or timed out. Please try again and complete the authentication when prompted.'); + } else if (error.name === 'NotSupportedError') { + throw new Error('WebAuthn is not supported in this browser. Please use a modern browser with WebAuthn support.'); + } else if (error.name === 'SecurityError') { + throw new Error('WebAuthn security error. Please ensure you are using HTTPS and try again.'); + } else { + throw new Error(`WebAuthn error: ${error.message}`); + } + } + throw error; + } if (!credential) { throw new Error('WebAuthn credential creation failed'); @@ -283,18 +299,34 @@ export class SecureCredentialsService { throw new Error('WebAuthn not supported for decryption'); } - // Demander l'authentification WebAuthn - const credential = await navigator.credentials.get({ - publicKey: { - challenge: crypto.getRandomValues(new Uint8Array(32)), - allowCredentials: [{ - id: new TextEncoder().encode(credentialId), - type: 'public-key' - }], - userVerification: 'required', - timeout: 60000 + // Demander l'authentification WebAuthn avec gestion d'erreur robuste + let credential: PublicKeyCredential; + try { + credential = await navigator.credentials.get({ + publicKey: { + challenge: crypto.getRandomValues(new Uint8Array(32)), + allowCredentials: [{ + id: new TextEncoder().encode(credentialId), + type: 'public-key' + }], + userVerification: 'required', + timeout: 120000 // 2 minutes timeout + } + }) as PublicKeyCredential; + } catch (error) { + if (error instanceof Error) { + if (error.name === 'NotAllowedError') { + throw new Error('WebAuthn authentication was cancelled or timed out. Please try again and complete the authentication when prompted.'); + } else if (error.name === 'NotSupportedError') { + throw new Error('WebAuthn is not supported in this browser. Please use a modern browser with WebAuthn support.'); + } else if (error.name === 'SecurityError') { + throw new Error('WebAuthn security error. Please ensure you are using HTTPS and try again.'); + } else { + throw new Error(`WebAuthn decryption error: ${error.message}`); + } } - }) as PublicKeyCredential; + throw error; + } if (!credential) { throw new Error('WebAuthn authentication failed'); diff --git a/src/services/service.ts b/src/services/service.ts index 64bc44d..05ff366 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -897,9 +897,18 @@ export default class Services { console.log('🔍 DEBUG: Members type:', typeof membersObj); console.log('🔍 DEBUG: Members keys:', Object.keys(membersObj)); - // Convert object to array for WebAssembly - const members = Object.values(membersObj); + // Check if membersList is empty + if (!membersObj || Object.keys(membersObj).length === 0) { + console.warn('⚠️ No members available for create_new_process, waiting for handshake...'); + throw new Error('No members available - handshake not completed yet'); + } + + // Convert to simple array of SP addresses for WebAssembly + const members = Object.values(membersObj).map(member => ({ + sp_addresses: member.sp_addresses + })); console.log('🔍 DEBUG: Members array length:', members.length); + console.log('🔍 DEBUG: Members simplified:', members); const result = this.sdkClient.create_new_process( encodedPrivateData, @@ -943,12 +952,15 @@ export default class Services { ...this.sdkClient.encode_binary(publicSplitData.binaryData), }; try { + const members = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); const result = this.sdkClient.update_process( process, encodedPrivateData, roles, encodedPublicData, - Object.values(this.getAllMembers()) + members ); if (result.updated_process) { await this.checkConnections(result.updated_process.current_process); @@ -969,7 +981,10 @@ export default class Services { await this.checkConnections(process); } try { - return this.sdkClient.create_update_message(process, stateId, Object.values(this.getAllMembers())); + const members = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); + return this.sdkClient.create_update_message(process, stateId, members); } catch (e) { throw new Error(`Failed to create prd update: ${e}`); } @@ -981,7 +996,10 @@ export default class Services { throw new Error('Unknown process'); } try { - return this.sdkClient.create_response_prd(process, stateId, Object.values(this.getAllMembers())); + const members = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); + return this.sdkClient.create_response_prd(process, stateId, members); } catch (e) { throw new Error(`Failed to create response prd: ${e}`); } @@ -993,7 +1011,10 @@ export default class Services { throw new Error('Failed to get process from db'); } try { - const result = this.sdkClient.validate_state(process, stateId, Object.values(this.getAllMembers())); + const members = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); + const result = this.sdkClient.validate_state(process, stateId, members); if (result.updated_process) { await this.checkConnections(result.updated_process.current_process); return result; @@ -1049,7 +1070,9 @@ export default class Services { } async parseCipher(message: string) { - const membersList = Object.values(this.getAllMembers()); + const membersList = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); const processes = await this.getProcesses(); try { // console.log('parsing new cipher'); @@ -1080,7 +1103,9 @@ export default class Services { return; } - const membersList = Object.values(this.getAllMembers()); + const membersList = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); try { // Does the transaction spend the tip of a process? const prevouts = this.sdkClient.get_prevouts(parsedMsg.transaction); @@ -2178,7 +2203,7 @@ export default class Services { } // Add a flag to prevent processing the same handshake multiple times - const handshakeKey = `${url}_${Date.now()}`; + const handshakeKey = `${url}_${JSON.stringify(handshakeMsg.processes_list)}`; if (this.processedHandshakes && this.processedHandshakes.has(handshakeKey)) { console.debug('Handshake already processed for', url); return; @@ -2479,7 +2504,9 @@ export default class Services { roles: Record[] ) { console.log('Requesting data from peers'); - const membersList = Object.values(this.getAllMembers()); + const membersList = Object.values(this.getAllMembers()).map(member => ({ + sp_addresses: member.sp_addresses + })); try { // Convert objects to strings for WASM compatibility const rolesString = JSON.stringify(roles);