diff --git a/package.json b/package.json index 78d08b7..fba59e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nostr_quantum_preparation", - "version": "0.0.34", + "version": "0.0.35", "description": "A migration strategy for bringing post-quantum security to Nostr without breaking the social graph, without requiring consensus on a single post-quantum algorithm, and without forcing existing users to abandon their identities.", "main": "index.js", "scripts": { diff --git a/www/js/verify-app.mjs b/www/js/verify-app.mjs index 3b22c9d..5b12c60 100644 --- a/www/js/verify-app.mjs +++ b/www/js/verify-app.mjs @@ -22,19 +22,26 @@ ================================================================ */ const tabRelay = document.getElementById('pqTabRelay'); const tabPaste = document.getElementById('pqTabPaste'); + const tabSignIn = document.getElementById('pqTabSignIn'); const panelRelay = document.getElementById('pqPanelRelay'); const panelPaste = document.getElementById('pqPanelPaste'); + const panelSignIn = document.getElementById('pqPanelSignIn'); function switchTab(which) { const isRelay = which === 'relay'; + const isPaste = which === 'paste'; + const isSignIn = which === 'signin'; tabRelay.classList.toggle('pq-tab-active', isRelay); - tabPaste.classList.toggle('pq-tab-active', !isRelay); + tabPaste.classList.toggle('pq-tab-active', isPaste); + tabSignIn.classList.toggle('pq-tab-active', isSignIn); panelRelay.classList.toggle('pq-tab-panel-active', isRelay); - panelPaste.classList.toggle('pq-tab-panel-active', !isRelay); + panelPaste.classList.toggle('pq-tab-panel-active', isPaste); + panelSignIn.classList.toggle('pq-tab-panel-active', isSignIn); } tabRelay.addEventListener('click', () => switchTab('relay')); tabPaste.addEventListener('click', () => switchTab('paste')); + tabSignIn.addEventListener('click', () => switchTab('signin')); /* ================================================================ SHARED DOM + STATE @@ -607,6 +614,141 @@ } }); + /* ================================================================ + AUTHENTICATION (nostr-login-lite / NIP-07) + ================================================================ */ + async function initNostrLoginLite() { + if (!window.NOSTR_LOGIN_LITE) return false; + if (!window.NOSTR_LOGIN_LITE._initialized) { + await window.NOSTR_LOGIN_LITE.init({ + methods: { extension: true, local: true, nip46: true, seedphrase: true, nsigner: true } + }); + } + return true; + } + + // Fill the pubkey field and auto-query relays for the signed-in user's + // kind 9999 event. Switches to the Query Relay tab so the user sees the + // results. Returns the pubkey hex on success, null otherwise. + async function queryAsSignedInUser() { + if (!window.nostr || !window.nostr.getPublicKey) { + throw new Error('No Nostr signer available. Install a NIP-07 browser extension (nos2x, Alby) or use nostr-login-lite.'); + } + const pubkeyHex = await window.nostr.getPublicKey(); + if (!pubkeyHex) throw new Error('No pubkey returned by signer.'); + + console.log('[verify] Signed-in pubkey:', pubkeyHex); + const pubkeyInput = document.getElementById('pqPubkeyInput'); + const npub = hexToNpub(pubkeyHex); + pubkeyInput.value = npub || pubkeyHex; + + // Show the query tab so the user sees the results + switchTab('relay'); + queryBtn.click(); + return pubkeyHex; + } + + /* ================================================================ + TAB 3: SIGN IN + ================================================================ */ + const signInBtn = document.getElementById('pqSignInBtn'); + const signInError = document.getElementById('pqSignInError'); + const signInStatus = document.getElementById('pqSignInStatus'); + + signInBtn.addEventListener('click', async () => { + signInBtn.disabled = true; + signInError.innerHTML = ''; + setStatus(signInStatus, 'info', 'Signing in...'); + try { + if (window.NOSTR_LOGIN_LITE) { + await initNostrLoginLite(); + // Try a restored session first + if (window.nostr) { + try { + const pk = await window.nostr.getPublicKey(); + if (pk) { + await queryAsSignedInUser(); + setStatus(signInStatus, 'success', 'Signed in. Querying your event...'); + return; + } + } catch (e) { + console.log('[verify] Not yet authenticated, launching login modal...'); + } + } + // Launch the login modal and wait for auth events + const pubkeyHex = await new Promise((resolve, reject) => { + let settled = false; + const timeout = setTimeout(() => { + if (!settled) { settled = true; cleanup(); reject(new Error('Login timed out')); } + }, 120000); + + async function tryGetPubkey() { + if (settled) return; + if (window.nostr) { + try { + const pk = await window.nostr.getPublicKey(); + if (pk && !settled) { settled = true; cleanup(); resolve(pk); } + } catch (e) { console.log('[verify] getPublicKey not ready:', e.message); } + } + } + + function authHandler(e) { + if (e.type === 'nlMethodSelected' && e.detail && e.detail.pubkey) { + if (!settled) { settled = true; cleanup(); resolve(e.detail.pubkey); } + return; + } + tryGetPubkey(); + setTimeout(tryGetPubkey, 200); + setTimeout(tryGetPubkey, 500); + setTimeout(tryGetPubkey, 1000); + setTimeout(tryGetPubkey, 2000); + } + + function cleanup() { + window.removeEventListener('nlAuth', authHandler); + window.removeEventListener('nlAuthRestored', authHandler); + window.removeEventListener('nlMethodSelected', authHandler); + window.removeEventListener('nlAuthComplete', authHandler); + clearTimeout(timeout); + } + + window.addEventListener('nlAuth', authHandler); + window.addEventListener('nlAuthRestored', authHandler); + window.addEventListener('nlMethodSelected', authHandler); + window.addEventListener('nlAuthComplete', authHandler); + + try { + const result = window.NOSTR_LOGIN_LITE.launch(); + if (result && typeof result.then === 'function') { + result.then(() => { + tryGetPubkey(); setTimeout(tryGetPubkey, 200); setTimeout(tryGetPubkey, 500); + }).catch(e => { if (!settled) { settled = true; cleanup(); reject(e); } }); + } + } catch (e) { + if (!settled) { settled = true; cleanup(); reject(e); } + } + }); + // Fill the pubkey field with the launched-session pubkey and query + const pubkeyInput = document.getElementById('pqPubkeyInput'); + const npub = hexToNpub(pubkeyHex); + pubkeyInput.value = npub || pubkeyHex; + switchTab('relay'); + queryBtn.click(); + setStatus(signInStatus, 'success', 'Signed in. Querying your event...'); + } else { + // No nostr-login-lite — fall back to a plain NIP-07 extension + await queryAsSignedInUser(); + setStatus(signInStatus, 'success', 'Signed in. Querying your event...'); + } + } catch (error) { + console.error('[verify] Sign-in failed:', error); + setStatus(signInError, 'error', error.message); + setStatus(signInStatus, 'error', 'Sign-in failed.'); + } finally { + signInBtn.disabled = false; + } + }); + /* ================================================================ AUTO-DETECT SIGNED-IN USER On page load, check for a NIP-07 signer / nostr-login-lite. If the @@ -614,14 +756,9 @@ relays for their kind 9999 event. ================================================================ */ async function initAutoDetect() { - // Initialize nostr-login-lite if available (shares session with main page) if (window.NOSTR_LOGIN_LITE) { try { - if (!window.NOSTR_LOGIN_LITE._initialized) { - await window.NOSTR_LOGIN_LITE.init({ - methods: { extension: true, local: true, nip46: true, seedphrase: true, nsigner: true } - }); - } + await initNostrLoginLite(); } catch (e) { console.log('[verify] nostr-login-lite init failed:', e.message); } diff --git a/www/js/version.json b/www/js/version.json index 415f758..f6ea5d0 100644 --- a/www/js/version.json +++ b/www/js/version.json @@ -1,5 +1,5 @@ { - "VERSION": "v0.0.34", - "VERSION_NUMBER": "0.0.34", - "BUILD_DATE": "2026-07-27T16:53:32.439Z" + "VERSION": "v0.0.35", + "VERSION_NUMBER": "0.0.35", + "BUILD_DATE": "2026-07-27T17:03:35.968Z" } diff --git a/www/verify.html b/www/verify.html index 9ce0fd0..6d45493 100644 --- a/www/verify.html +++ b/www/verify.html @@ -258,6 +258,7 @@
Query Relay
Paste Event JSON
+
Sign In
@@ -302,6 +303,17 @@ + +
+
+ Sign in with your Nostr identity (NIP-07 extension or nostr-login-lite) to look up your + own kind 9999 event automatically. +
+
+ +
+
+