From 4f8e43ed874869f37d79c2d063e282ea18ae3dfc Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Thu, 23 Oct 2025 21:59:35 +0200 Subject: [PATCH] fix: implement proper WebAuthn user interaction and fix WebAssembly serialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations :** - WebAuthn requires user gesture (click) to work properly - Fix WebAssembly serialization error 'invalid type: sequence, expected a map' - Provide clear UI for user to trigger WebAuthn authentication - Ensure proper error handling for authentication failures **Modifications :** - Added authentication button in home.ts that requires user click for WebAuthn - Fixed WebAssembly members parameter to pass object map instead of array - Added CSS styles for authentication button with hover effects - Improved error handling and user feedback for authentication process - Maintained user interaction requirement for WebAuthn security **Pages affectΓ©es :** - src/pages/home/home.ts: Added user interaction button for WebAuthn - src/services/service.ts: Fixed WebAssembly serialization to use object map - src/4nk.css: Added authentication button styles with responsive design --- src/4nk.css | 45 ++++++++++++++++ src/pages/home/home.ts | 97 ++++++++++++++++++++++------------- src/services/service.ts | 13 ++--- src/utils/sp-address.utils.ts | 2 +- 4 files changed, 114 insertions(+), 43 deletions(-) diff --git a/src/4nk.css b/src/4nk.css index 84240e6..be56350 100644 --- a/src/4nk.css +++ b/src/4nk.css @@ -1230,6 +1230,45 @@ select[data-multi-select-plugin] { box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3); } +/* Authentication Button Styles */ +.auth-container { + text-align: center; + padding: 20px; +} + +.auth-button { + background: linear-gradient(135deg, #007bff, #0056b3); + color: white; + border: none; + padding: 16px 32px; + border-radius: 12px; + cursor: pointer; + font-size: 16px; + font-weight: 600; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3); + margin: 20px 0; + min-width: 250px; +} + +.auth-button:hover { + background: linear-gradient(135deg, #0056b3, #004085); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 123, 255, 0.4); +} + +.auth-button:active { + transform: translateY(0); + box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3); +} + +.auth-hint { + color: #6c757d; + font-size: 14px; + margin-top: 10px; + font-style: italic; +} + /* Responsive Design for Mode Selection */ @media (max-width: 768px) { .mode-buttons { @@ -1244,4 +1283,10 @@ select[data-multi-select-plugin] { position: static; margin-top: 10px; } + + .auth-button { + min-width: 200px; + padding: 14px 28px; + font-size: 14px; + } } diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index 9d9be1f..567184e 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -547,51 +547,76 @@ async function handleMainPairing(): Promise { const mainStatus = container.querySelector('#main-status') as HTMLElement; try { - // Update UI + // Show authentication button that requires user interaction if (mainStatus) { - mainStatus.innerHTML = '
Authenticating with browser...'; + mainStatus.innerHTML = ` +
+

πŸ” Secure authentication required

+ +

Click the button above to authenticate with your browser

+
+ `; } - // Always trigger WebAuthn flow for authentication - console.log('πŸ” Triggering WebAuthn authentication...'); - if (mainStatus) { - mainStatus.innerHTML = '
Authenticating with browser...'; - } - - // Import and trigger WebAuthn directly - const { secureCredentialsService } = await import('../../services/secure-credentials.service'); - - // Check if we have existing credentials - const hasCredentials = await secureCredentialsService.hasCredentials(); - - if (hasCredentials) { - console.log('πŸ”“ Existing credentials found, decrypting...'); - if (mainStatus) { - mainStatus.innerHTML = '
Decrypting existing credentials...'; + // Wait for user to click the authentication button + await new Promise((resolve, reject) => { + const authButton = document.getElementById('authButton') as HTMLButtonElement; + if (!authButton) { + reject(new Error('Authentication button not found')); + return; } - // This will trigger WebAuthn for decryption - await secureCredentialsService.retrieveCredentials(''); + authButton.addEventListener('click', async () => { + try { + // Update UI to show authentication in progress + if (mainStatus) { + mainStatus.innerHTML = '
Authenticating with browser...'; + } - if (mainStatus) { - mainStatus.innerHTML = 'βœ… Credentials decrypted successfully'; - } - } else { - console.log('πŸ” No existing credentials, creating new ones...'); - if (mainStatus) { - mainStatus.innerHTML = '
Creating new credentials...'; - } + // Import and trigger WebAuthn directly + const { secureCredentialsService } = await import('../../services/secure-credentials.service'); - // This will trigger WebAuthn for creation - await secureCredentialsService.generateSecureCredentials(''); + // Check if we have existing credentials + const hasCredentials = await secureCredentialsService.hasCredentials(); - if (mainStatus) { - mainStatus.innerHTML = 'βœ… New credentials created successfully'; - } - } + if (hasCredentials) { + console.log('πŸ”“ Existing credentials found, decrypting...'); + if (mainStatus) { + mainStatus.innerHTML = '
Decrypting existing credentials...'; + } - // Now proceed with pairing process - await prepareAndSendPairingTx(); + // This will trigger WebAuthn for decryption + await secureCredentialsService.retrieveCredentials(''); + + if (mainStatus) { + mainStatus.innerHTML = 'βœ… Credentials decrypted successfully'; + } + } else { + console.log('πŸ” No existing credentials, creating new ones...'); + if (mainStatus) { + mainStatus.innerHTML = '
Creating new credentials...'; + } + + // This will trigger WebAuthn for creation + await secureCredentialsService.generateSecureCredentials(''); + + if (mainStatus) { + mainStatus.innerHTML = 'βœ… New credentials created successfully'; + } + } + + // Now proceed with pairing process + await prepareAndSendPairingTx(); + resolve(); + } catch (error) { + console.error('Authentication failed:', error); + if (mainStatus) { + mainStatus.innerHTML = '❌ Authentication failed. Please try again.'; + } + reject(error); + } + }); + }); } catch (error) { console.error('Pairing failed:', error); diff --git a/src/services/service.ts b/src/services/service.ts index 05ff366..d4fddcd 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -903,12 +903,13 @@ export default class Services { 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); + // Convert to map format for WebAssembly (keep original structure) + const members = membersObj; + console.log('πŸ” DEBUG: Members map keys:', Object.keys(members)); + console.log('πŸ” DEBUG: Members map sample:', Object.keys(members).slice(0, 3).reduce((acc, key) => { + acc[key] = members[key]; + return acc; + }, {} as any)); const result = this.sdkClient.create_new_process( encodedPrivateData, diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index 86870b4..3d19edb 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -2546,7 +2546,7 @@ async function onCreateButtonClick() { // Auto-trigger WebAuthn authentication console.log('πŸ” DEBUG: Auto-triggering WebAuthn authentication...'); - + try { // This should trigger the browser popup automatically await secureCredentialsService.generateSecureCredentials('4nk-pairing-password');