From 0f26bae9a6cc442624a9a50494290f0d8a52cf33 Mon Sep 17 00:00:00 2001 From: BT Date: Sun, 3 May 2026 23:12:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20**Nostr=20Login:**=20Improved=20err?= =?UTF-8?q?or=20handling=20and=20compatibility=20for=20Nostr=20login=20flo?= =?UTF-8?q?w.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🛡️ Added validation for missing or invalid `challenge`. - ⚠️ Added error handling for absent or non-functional Nostr signer. - 🔄 Ensured plain serialization of signed events for Livewire compatibility. - 🪲 Improved error messages for better user feedback. --- resources/js/nostrLogin.js | 41 ++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/resources/js/nostrLogin.js b/resources/js/nostrLogin.js index 98cda94..b9c2980 100644 --- a/resources/js/nostrLogin.js +++ b/resources/js/nostrLogin.js @@ -12,20 +12,53 @@ export default () => ({ async openNostrLogin() { const livewireComponent = this.$el.closest('[wire\\:id]')?.__livewire; - const challenge = livewireComponent?.$wire?.nostrChallenge; + const rawChallenge = livewireComponent?.$wire?.nostrChallenge; + + // Livewire's $wire proxy returns a function (server-action fallback) when + // a property is missing from the snapshot. Only accept a non-empty string. + const challenge = typeof rawChallenge === 'string' && rawChallenge !== '' + ? rawChallenge + : null; + if (!challenge) { this.showAuthError('Login challenge missing. Please reload and try again.'); return; } - const signedEvent = await window.nostr.signEvent({ + if (!window.nostr || typeof window.nostr.signEvent !== 'function') { + this.showAuthError('No Nostr signer found. Please install a Nostr browser extension.'); + return; + } + + const event = { kind: 22242, created_at: Math.floor(Date.now() / 1000), tags: [['challenge', challenge]], content: '', - }); + }; - this.$dispatch('nostrLoggedIn', {signedEvent}); + let signedEvent; + try { + signedEvent = await window.nostr.signEvent(event); + } catch (error) { + console.error('Nostr signEvent failed:', error); + this.showAuthError('Could not sign Nostr login event. Please try again.'); + return; + } + + // Some Nostr extensions return objects wrapped in extension-specific + // proxies (e.g. cloneInto results) that Livewire cannot serialize. + // Round-trip through JSON to guarantee a plain, cloneable object. + let plainSignedEvent; + try { + plainSignedEvent = JSON.parse(JSON.stringify(signedEvent)); + } catch (error) { + console.error('Nostr signedEvent serialization failed:', error); + this.showAuthError('Wallet returned an incompatible signature. Please try a different wallet.'); + return; + } + + this.$dispatch('nostrLoggedIn', {signedEvent: plainSignedEvent}); }, initErrorPolling() {