@@ -15,7 +15,8 @@ import { bytesToHex } from './lib/crypto/utils.ts';
1515import { ncryptsecEncode , ncryptsecDecode } from './lib/crypto/nip49.ts' ;
1616import { signEvent } from './lib/crypto/nip01.ts' ;
1717import { getDefaultsForDomain } from './src/shared/adapterDefaults.ts' ;
18- import { Nip46Client } from './lib/nip46.ts' ;
18+ import { BunkerSigner , createNostrConnectURI } from 'nostr-tools/nip46' ;
19+ import { generateSecretKey , getPublicKey as ntGetPublicKey } from 'nostr-tools/pure' ;
1920import type { Account , SignedEvent , ScoringConfig , UnsignedEvent , VaultPayload } from './lib/types.ts' ;
2021
2122// Sanitize user-provided CSS to prevent data exfiltration via url(), @import, etc.
@@ -32,13 +33,13 @@ const DEFAULT_RELAYS = ['wss://relay.damus.io', 'wss://nos.lol', 'wss://nostr-01
3233
3334// In-memory sessions for pending nostrconnect:// QR flows
3435interface NostrConnectSession {
35- client : Nip46Client ;
36- relay : string ;
37- localPrivkey : string ;
36+ signerPromise : Promise < BunkerSigner > ;
37+ signer : BunkerSigner | null ;
38+ secretKey : Uint8Array ;
3839 localPubkey : string ;
39- signerPubkey : string | null ;
40- connected : boolean ;
41- expired : boolean ;
40+ relays : string [ ] ;
41+ error : Error | null ;
42+ abortController : AbortController ;
4243}
4344const _nostrConnectSessions = new Map < string , NostrConnectSession > ( ) ;
4445
@@ -67,7 +68,7 @@ const PRIVILEGED_METHODS = new Set([
6768 'signer_clearPermissions' , 'signer_savePermission' ,
6869 'signer_getPermissionsRaw' , 'signer_getPermissionsForDomainRaw' ,
6970 'signer_copyPermissions' , 'signer_getUseGlobalDefaults' , 'signer_setUseGlobalDefaults' ,
70- 'signer_getPending' , 'signer_resolve' , 'signer_resolveBatch' ,
71+ 'signer_getPending' , 'signer_resolve' , 'signer_resolveBatch' , 'signer_cancelNip46' ,
7172 'onboarding_validateNsec' , 'onboarding_validateNcryptsec' , 'onboarding_validateMnemonic' , 'onboarding_validateNpub' , 'onboarding_connectNip46' ,
7273 'onboarding_generateAccount' , 'onboarding_checkExistingSeed' , 'onboarding_generateSubAccount' ,
7374 'onboarding_exportNcryptsec' , 'onboarding_saveReadOnly' , 'onboarding_createVault' , 'onboarding_addToVault' ,
@@ -1389,6 +1390,10 @@ async function handleRequest({ method, params }: { method: string; params: Recor
13891390 await signer . resolveBatch ( params . origin as string , params . method as string , params . decision as unknown as import ( './lib/types.ts' ) . RequestDecision , params . eventKind as number | undefined ) ;
13901391 return { ok : true } ;
13911392
1393+ case 'signer_cancelNip46' :
1394+ await signer . cancelNip46InFlight ( params . id as string ) ;
1395+ return { ok : true } ;
1396+
13921397 // === Onboarding methods ===
13931398
13941399 case 'onboarding_validateNsec' : {
@@ -1493,50 +1498,80 @@ async function handleRequest({ method, params }: { method: string; params: Recor
14931498
14941499 case 'onboarding_connectNip46' : {
14951500 const acct = accounts . connectNip46 ( params . bunkerUrl as string ) ;
1496- return { account : acct } ;
1501+ await setPendingOnboardingAccount ( acct ) ;
1502+ const { nip46Config : _n46 , privkey : _pk , mnemonic : _mn , ...safeNip46 } = acct ;
1503+ return { account : safeNip46 } ;
14971504 }
14981505
14991506 case 'onboarding_initNostrConnect' : {
1500- const relay = DEFAULT_RELAYS [ 0 ] ;
1507+ // Clean up any existing sessions before creating a new one
1508+ for ( const [ oldId , oldSession ] of _nostrConnectSessions ) {
1509+ oldSession . abortController . abort ( ) ;
1510+ if ( oldSession . signer ) oldSession . signer . close ( ) . catch ( ( ) => { } ) ;
1511+ _nostrConnectSessions . delete ( oldId ) ;
1512+ }
1513+
1514+ // Known NIP-46 signer relays (nsec.app, etc.) + general relays
1515+ const NIP46_RELAYS = [ 'wss://relay.nsec.app' , ...DEFAULT_RELAYS ] ;
15011516 const connectSecret = Array . from ( crypto . getRandomValues ( new Uint8Array ( 16 ) ) , b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
1502- const client = new Nip46Client ( { pubkey : null , relay, secret : null , connectSecret } ) ;
1503- await client . init ( ) ;
1504- const { privkey : localPrivkey , pubkey : localPubkey } = client . getLocalKeyPair ( ) ;
1505- const nostrconnectUri = Nip46Client . buildConnectUri ( localPubkey , relay , {
1506- name : 'Nostr WoT Extension'
1507- } ) + `&secret=${ connectSecret } ` ;
1508- await client . connect ( ) ;
1517+ const ncSecretKey = generateSecretKey ( ) ;
1518+ const ncLocalPubkey = ntGetPublicKey ( ncSecretKey ) ;
1519+
1520+ const nostrconnectUri = createNostrConnectURI ( {
1521+ clientPubkey : ncLocalPubkey ,
1522+ relays : NIP46_RELAYS ,
1523+ secret : connectSecret ,
1524+ name : 'Nostr WoT' ,
1525+ url : 'https://nostr-wot.com' ,
1526+ image : 'https://nostr-wot.com/icon-512.png'
1527+ } ) ;
15091528
1529+ const abortController = new AbortController ( ) ;
15101530 const sessionId = Array . from ( crypto . getRandomValues ( new Uint8Array ( 8 ) ) , b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
1511- const session : NostrConnectSession = { client, relay, localPrivkey, localPubkey, signerPubkey : null , connected : false , expired : false } ;
1512- _nostrConnectSessions . set ( sessionId , session ) ;
1531+ const session : NostrConnectSession = {
1532+ signerPromise : null ! , // set below
1533+ signer : null ,
1534+ secretKey : ncSecretKey ,
1535+ localPubkey : ncLocalPubkey ,
1536+ relays : NIP46_RELAYS ,
1537+ error : null ,
1538+ abortController,
1539+ } ;
15131540
1514- // Start listening in the background
1515- client . listenForConnect ( 120000 ) . then ( signerPubkey => {
1516- session . signerPubkey = signerPubkey ;
1517- session . connected = true ;
1518- } ) . catch ( ( ) => {
1519- session . expired = true ;
1520- } ) ;
1541+ // BunkerSigner.fromURI waits for the remote signer to connect
1542+ session . signerPromise = BunkerSigner . fromURI (
1543+ ncSecretKey ,
1544+ nostrconnectUri ,
1545+ { onauth ( url : string ) { browser . tabs . create ( { url } ) ; } } ,
1546+ abortController . signal
1547+ ) ;
1548+ session . signerPromise
1549+ . then ( signer => { session . signer = signer ; } )
1550+ . catch ( err => { session . error = err ; } ) ;
15211551
1552+ _nostrConnectSessions . set ( sessionId , session ) ;
15221553 return { nostrconnectUri, sessionId } ;
15231554 }
15241555
15251556 case 'onboarding_pollNostrConnect' : {
15261557 const session = _nostrConnectSessions . get ( params . sessionId as string ) ;
15271558 if ( ! session ) return { expired : true } ;
1528- if ( session . connected ) {
1559+
1560+ if ( session . signer ) {
1561+ const signerPk = session . signer . bp . pubkey ;
1562+ const primaryRelay = session . relays [ 0 ] ;
1563+ const localPrivkeyHex = bytesToHex ( session . secretKey ) ;
15291564 const acct = accounts . connectNostrConnect (
1530- session . signerPubkey ! , session . relay ,
1531- session . localPrivkey , session . localPubkey
1565+ signerPk , primaryRelay ,
1566+ localPrivkeyHex , session . localPubkey
15321567 ) ;
15331568 _nostrConnectSessions . delete ( params . sessionId as string ) ;
1534- session . client . close ( ) ;
1535- return { connected : true , account : acct } ;
1569+ await setPendingOnboardingAccount ( acct ) ;
1570+ const { nip46Config : _n46 , privkey : _pk , mnemonic : _mn , ...safeNc } = acct ;
1571+ return { connected : true , account : safeNc } ;
15361572 }
1537- if ( session . expired ) {
1573+ if ( session . error ) {
15381574 _nostrConnectSessions . delete ( params . sessionId as string ) ;
1539- session . client . close ( ) ;
15401575 return { expired : true } ;
15411576 }
15421577 return { connected : false } ;
@@ -1545,7 +1580,8 @@ async function handleRequest({ method, params }: { method: string; params: Recor
15451580 case 'onboarding_cancelNostrConnect' : {
15461581 const session2 = _nostrConnectSessions . get ( params . sessionId as string ) ;
15471582 if ( session2 ) {
1548- session2 . client . close ( ) ;
1583+ session2 . abortController . abort ( ) ;
1584+ if ( session2 . signer ) session2 . signer . close ( ) . catch ( ( ) => { } ) ;
15491585 _nostrConnectSessions . delete ( params . sessionId as string ) ;
15501586 }
15511587 return { ok : true } ;
@@ -1842,7 +1878,7 @@ async function handleRequest({ method, params }: { method: string; params: Recor
18421878 const clientConnected = signer . isNip46Connected ( nip46Acct . id ) ;
18431879
18441880 return {
1845- bunkerPubkey : nip46Config . bunkerUrl ? new URL ( 'nostr://' + nip46Config . bunkerUrl . replace ( 'bunker://' , '' ) ) . pathname . slice ( 2 , 66 ) : null ,
1881+ bunkerPubkey : nip46Acct . pubkey ,
18461882 relay : nip46Config . relay ,
18471883 connected : clientConnected ,
18481884 accountId : nip46Acct . id ,
0 commit comments