@konemono/nostr-login 1.7.36 → 1.7.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { Info, RecentType } from 'nostr-login-components';
1
+ import { Info, RecentType } from 'nostr-login-components/dist/types/types';
2
2
  import NDK, { NDKSigner } from '@nostr-dev-kit/ndk';
3
3
  import { NostrLoginOptions } from '../types';
4
4
  export declare const localStorageSetItem: (key: string, value: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konemono/nostr-login",
3
- "version": "1.7.36",
3
+ "version": "1.7.37",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,2 +1 @@
1
- export const CALL_TIMEOUT = 10000;
2
- export const NIP46_TIMEOUT = 30000;
1
+ export const CALL_TIMEOUT = 5000;
@@ -1,7 +1,9 @@
1
1
  import { init } from './index';
2
2
  import { NostrLoginOptions, StartScreens } from './types';
3
3
 
4
+ // wrap to hide local vars
4
5
  (() => {
6
+ // currentScript only visible in global scope code, not event handlers
5
7
  const cs = document.currentScript;
6
8
  const start = async () => {
7
9
  const options: NostrLoginOptions = {};
@@ -69,13 +71,6 @@ import { NostrLoginOptions, StartScreens } from './types';
69
71
  const custom = cs.getAttribute('data-custom-nostr-connect') === 'true';
70
72
  if (custom) options.customNostrConnect = custom;
71
73
 
72
- const connectRelays = cs.getAttribute('data-connect-relays');
73
- if (connectRelays)
74
- options.connectRelays = connectRelays
75
- .split(',')
76
- .map(r => r.trim())
77
- .filter(r => !!r);
78
-
79
74
  console.log('nostr-login options', options);
80
75
  }
81
76
 
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@ import 'nostr-login-components';
2
2
  import { AuthNostrService, NostrExtensionService, Popup, NostrParams, Nostr, ProcessManager, BannerManager, ModalManager } from './modules';
3
3
  import { NostrLoginAuthOptions, NostrLoginOptions, StartScreens } from './types';
4
4
  import { localStorageGetAccounts, localStorageGetCurrent, localStorageGetRecents, localStorageSetItem } from './utils';
5
- import { Info } from 'nostr-login-components';
5
+ import { Info } from 'nostr-login-components/dist/types/types';
6
6
  import { NostrObjectParams } from './modules/Nostr';
7
7
 
8
8
  export class NostrLoginInitializer {
@@ -93,11 +93,6 @@ export class NostrLoginInitializer {
93
93
  this.bannerManager.onUserInfo(info);
94
94
  });
95
95
 
96
- this.authNostrService.on('reconnecting', () => {
97
- // リレー再接続中はProcessManagerのタイマーをリセット
98
- this.processManager.onIframeUrl();
99
- });
100
-
101
96
  this.modalManager.on('onAuthUrlClick', url => {
102
97
  this.openPopup(url);
103
98
  });
@@ -327,7 +322,7 @@ export class NostrLoginInitializer {
327
322
  };
328
323
 
329
324
  public cancelNeedAuth = () => {
330
- console.log('cancelNeedAuth');
325
+ console.log("cancelNeedAuth");
331
326
  this.fulfillCustomLaunchPromise();
332
327
  this.authNostrService.cancelNostrConnect();
333
328
  };
@@ -12,6 +12,7 @@ import { PrivateKeySigner } from './Signer';
12
12
 
13
13
  const OUTBOX_RELAYS = ['wss://user.kindpag.es', 'wss://purplepag.es', 'wss://relay.nos.social'];
14
14
  const DEFAULT_NOSTRCONNECT_RELAYS = ['wss://relay.nsec.app/', 'wss://ephemeral.snowflare.cc/'];
15
+ const CONNECT_TIMEOUT = 5000;
15
16
  const NOSTRCONNECT_APPS: ConnectionString[] = [
16
17
  {
17
18
  name: 'Nsec.app',
@@ -19,19 +20,19 @@ const NOSTRCONNECT_APPS: ConnectionString[] = [
19
20
  canImport: true,
20
21
  img: 'https://nsec.app/assets/favicon.ico',
21
22
  link: 'https://use.nsec.app/<nostrconnect>',
22
- relay: 'wss://relay.nsec.app/',
23
+ relays: DEFAULT_NOSTRCONNECT_RELAYS,
23
24
  },
24
25
  {
25
26
  name: 'Amber',
26
27
  img: 'https://raw.githubusercontent.com/greenart7c3/Amber/refs/heads/master/assets/android-icon.svg',
27
28
  link: '<nostrconnect>',
28
- relay: 'wss://relay.nsec.app/',
29
+ relays: DEFAULT_NOSTRCONNECT_RELAYS,
29
30
  },
30
31
  {
31
32
  name: 'Other key stores',
32
33
  img: '',
33
34
  link: '<nostrconnect>',
34
- relay: 'wss://relay.nsec.app/',
35
+ relays: DEFAULT_NOSTRCONNECT_RELAYS,
35
36
  },
36
37
  ];
37
38
 
@@ -71,7 +72,7 @@ class AuthNostrService extends EventEmitter implements Signer {
71
72
  enableOutboxModel: true,
72
73
  explicitRelayUrls: OUTBOX_RELAYS,
73
74
  });
74
- this.profileNdk.connect();
75
+ this.profileNdk.connect(CONNECT_TIMEOUT);
75
76
 
76
77
  this.nip04 = {
77
78
  encrypt: this.encrypt04.bind(this),
@@ -91,13 +92,13 @@ class AuthNostrService extends EventEmitter implements Signer {
91
92
  if (this.signerPromise) {
92
93
  try {
93
94
  await this.signerPromise;
94
- } catch {}
95
+ } catch { }
95
96
  }
96
97
 
97
98
  if (this.readyPromise) {
98
99
  try {
99
100
  await this.readyPromise;
100
- } catch {}
101
+ } catch { }
101
102
  }
102
103
  }
103
104
 
@@ -106,15 +107,8 @@ class AuthNostrService extends EventEmitter implements Signer {
106
107
  this.resetAuth();
107
108
  }
108
109
 
109
- private getDefaultRelays(customRelays?: string[]): string[] {
110
- if (customRelays && customRelays.length > 0) {
111
- return customRelays;
112
- }
113
- return [...DEFAULT_NOSTRCONNECT_RELAYS];
114
- }
115
-
116
110
  public async nostrConnect(
117
- relay?: string | string[],
111
+ relays?: string[],
118
112
  {
119
113
  domain = '',
120
114
  link = '',
@@ -127,12 +121,13 @@ class AuthNostrService extends EventEmitter implements Signer {
127
121
  iframeUrl?: string;
128
122
  } = {},
129
123
  ) {
130
- const relays = Array.isArray(relay) ? relay : relay ? [relay] : this.getDefaultRelays();
124
+ relays = relays && relays.length > 0 ? relays : DEFAULT_NOSTRCONNECT_RELAYS;
125
+
131
126
 
132
127
  const info: Info = {
133
128
  authMethod: 'connect',
134
- pubkey: '',
135
- signerPubkey: '',
129
+ pubkey: '', // unknown yet!
130
+ signerPubkey: '', // unknown too!
136
131
  sk: this.nostrConnectKey,
137
132
  domain: domain,
138
133
  relays: relays,
@@ -141,20 +136,31 @@ class AuthNostrService extends EventEmitter implements Signer {
141
136
 
142
137
  console.log('nostrconnect info', info, link);
143
138
 
139
+ // non-iframe flow
144
140
  if (link && !iframeUrl) window.open(link, '_blank', 'width=400,height=700');
145
141
 
142
+ // init nip46 signer
146
143
  await this.initSigner(info, { listen: true });
147
144
 
145
+ // signer learns the remote pubkey
148
146
  if (!info.pubkey || !info.signerPubkey) throw new Error('Bad remote pubkey');
149
147
 
150
- info.bunkerUrl = `bunker://${info.signerPubkey}?${relays.map(r => `relay=${r}`).join('&')}`;
148
+ info.bunkerUrl = `bunker://${info.signerPubkey}?${relays.map((r, i) => `${i !== 0 ? '&' : ''}relay=${r}`)}`;
151
149
 
150
+ // callback
152
151
  if (!importConnect) this.onAuth('login', info);
153
152
 
154
153
  return info;
155
154
  }
156
155
 
157
- public async createNostrConnect(relay?: string | string[]) {
156
+ public async createNostrConnect(relays?: string[]) {
157
+ /*const relayList = relays
158
+ ? relays
159
+ .split(",")
160
+ .map(r => r.trim().replace(/['"]/g, ""))
161
+ .filter(r => r.length > 0)
162
+ : [];*/
163
+
158
164
  this.nostrConnectKey = generatePrivateKey();
159
165
  this.nostrConnectSecret = Math.random().toString(36).substring(7);
160
166
 
@@ -166,37 +172,52 @@ class AuthNostrService extends EventEmitter implements Signer {
166
172
  perms: encodeURIComponent(this.params.optionsModal.perms || ''),
167
173
  };
168
174
 
169
- const relays = Array.isArray(relay) ? relay : relay ? [relay] : this.getDefaultRelays();
170
-
171
- const relayParams = relays.map(r => `relay=${r}`).join('&');
172
- return `nostrconnect://${pubkey}?image=${meta.icon}&url=${meta.url}&name=${meta.name}&perms=${meta.perms}&secret=${this.nostrConnectSecret}&${relayParams}`;
175
+ return `nostrconnect://${pubkey}?image=${meta.icon}&url=${meta.url}&name=${meta.name}&perms=${meta.perms}&secret=${this.nostrConnectSecret}${(relays || []).length > 0 ? (relays || []).map((r, i) => `&relay=${r}`) : ""}`;
173
176
  }
174
177
 
175
178
  public async getNostrConnectServices(): Promise<[string, ConnectionString[]]> {
176
- const customRelays = this.params.optionsModal.connectRelays;
177
- const nostrconnect = await this.createNostrConnect(customRelays);
179
+ const nostrconnect = await this.createNostrConnect();
178
180
 
181
+ // copy defaults
179
182
  const apps = NOSTRCONNECT_APPS.map(a => ({ ...a }));
183
+ // if (this.params.optionsModal.dev) {
184
+ // apps.push({
185
+ // name: 'Dev.Nsec.app',
186
+ // domain: 'new.nsec.app',
187
+ // canImport: true,
188
+ // img: 'https://new.nsec.app/assets/favicon.ico',
189
+ // link: 'https://dev.nsec.app/<nostrconnect>',
190
+ // relay: 'wss://relay.nsec.app/',
191
+ // });
192
+ // }
180
193
 
181
194
  for (const a of apps) {
182
- let relays = customRelays && customRelays.length > 0 ? customRelays : this.getDefaultRelays();
195
+ let relays: string[] = [...DEFAULT_NOSTRCONNECT_RELAYS];
183
196
 
184
197
  if (a.link.startsWith('https://')) {
185
- let domain = a.domain || new URL(a.link).hostname;
198
+ const domain = a.domain || new URL(a.link).hostname;
186
199
  try {
187
200
  const info = await (await fetch(`https://${domain}/.well-known/nostr.json`)).json();
188
201
  const pubkey = info.names['_'];
189
- const serviceRelays = info.nip46[pubkey] as string[];
190
- if (serviceRelays && serviceRelays.length) {
191
- relays = serviceRelays;
202
+ const appRelays = info.nip46?.[pubkey] as string[] | undefined;
203
+
204
+ if (Array.isArray(appRelays) && appRelays.length > 0) {
205
+ relays = appRelays;
192
206
  }
193
- a.iframeUrl = info.nip46.iframe_url || '';
207
+
208
+ a.iframeUrl = info.nip46?.iframe_url || '';
194
209
  } catch (e) {
195
210
  console.log('Bad app info', e, a);
196
211
  }
197
212
  }
198
213
 
199
- const nc = nostrconnect + '&' + relays.map(r => `relay=${r}`).join('&');
214
+ const relayParams = relays
215
+ .map(r => r.replace(/['"]/g, ''))
216
+ .map(r => `&relay=${encodeURIComponent(r)}`)
217
+ .join('');
218
+
219
+ const nc = nostrconnect + relayParams;
220
+
200
221
  if (a.iframeUrl) {
201
222
  a.link = nc;
202
223
  } else {
@@ -231,23 +252,27 @@ class AuthNostrService extends EventEmitter implements Signer {
231
252
  }
232
253
 
233
254
  public prepareImportUrl(url: string) {
255
+ // for OTP we choose interactive import
234
256
  if (this.params.userInfo?.authMethod === 'otp') return url + '&import=true';
235
257
 
258
+ // for local we export our existing key
236
259
  if (!this.localSigner || this.params.userInfo?.authMethod !== 'local') throw new Error('Most be local keys');
237
260
  return url + '#import=' + nip19.nsecEncode(this.localSigner.privateKey!);
238
261
  }
239
262
 
240
263
  public async importAndConnect(cs: ConnectionString) {
241
- const { relay, domain, link, iframeUrl } = cs;
264
+ const { relays, domain, link, iframeUrl } = cs;
242
265
  if (!domain) throw new Error('Domain required');
243
266
 
244
- const relays = relay ? [relay] : this.getDefaultRelays();
245
- const info = await this.nostrConnect(relays, { domain, link, importConnect: true, iframeUrl });
267
+ const info = await this.nostrConnect(relays, {
268
+ domain,
269
+ link,
270
+ importConnect: true,
271
+ iframeUrl,
272
+ });
246
273
 
247
274
  await this.logout(true);
248
-
249
275
  this.localSigner = null;
250
-
251
276
  this.onAuth('login', info);
252
277
  }
253
278
 
@@ -277,20 +302,25 @@ class AuthNostrService extends EventEmitter implements Signer {
277
302
  public async createAccount(nip05: string) {
278
303
  const [name, domain] = nip05.split('@');
279
304
 
305
+ // bunker's own url
280
306
  const bunkerUrl = await getBunkerUrl(`_@${domain}`, this.params.optionsModal);
281
307
  console.log("create account bunker's url", bunkerUrl);
282
308
 
309
+ // parse bunker url and generate local nsec
283
310
  const info = bunkerUrlToInfo(bunkerUrl);
284
311
  if (!info.signerPubkey) throw new Error('Bad bunker url');
285
312
 
286
313
  const eventToAddAccount = Boolean(this.params.userInfo);
287
314
 
315
+ // init signer to talk to the bunker (not the user!)
288
316
  await this.initSigner(info, { eventToAddAccount });
289
317
 
290
318
  const userPubkey = await this.signer!.createAccount2({ bunkerPubkey: info.signerPubkey!, name, domain, perms: this.params.optionsModal.perms });
291
319
 
292
320
  return {
293
- bunkerUrl: `bunker://${userPubkey}?relay=${info.relays?.[0]}`,
321
+ bunkerUrl:
322
+ `bunker://${userPubkey}?` +
323
+ (info.relays ?? []).map((r: string) => `relay=${encodeURIComponent(r)}`).join('&'),
294
324
  sk: info.sk,
295
325
  };
296
326
  }
@@ -300,6 +330,7 @@ class AuthNostrService extends EventEmitter implements Signer {
300
330
  this.signerErrCallback?.('cancelled');
301
331
  this.localSigner = null;
302
332
 
333
+ // disconnect from signer relays
303
334
  for (const r of this.ndk.pool.relays.keys()) {
304
335
  this.ndk.pool.removeRelay(r);
305
336
  }
@@ -308,8 +339,10 @@ class AuthNostrService extends EventEmitter implements Signer {
308
339
  public async logout(keepSigner = false) {
309
340
  if (!keepSigner) this.releaseSigner();
310
341
 
342
+ // move current to recent
311
343
  localStorageRemoveCurrentAccount();
312
344
 
345
+ // notify everyone
313
346
  this.onAuth('logout');
314
347
 
315
348
  this.emit('updateAccounts');
@@ -334,15 +367,17 @@ class AuthNostrService extends EventEmitter implements Signer {
334
367
  private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null) {
335
368
  if (type !== 'logout' && !info) throw new Error('No user info in onAuth');
336
369
 
370
+ // make sure we emulate logout first
337
371
  if (info && this.params.userInfo && (info.pubkey !== this.params.userInfo.pubkey || info.authMethod !== this.params.userInfo.authMethod)) {
338
372
  const event = new CustomEvent('nlAuth', { detail: { type: 'logout' } });
339
373
  console.log('nostr-login auth', event.detail);
340
- document.dispatchEvent(event);
374
+ document.dispatchEvent(event)
341
375
  }
342
376
 
343
377
  this.setUserInfo(info);
344
378
 
345
379
  if (info) {
380
+ // async profile fetch
346
381
  fetchProfile(info, this.profileNdk).then(p => {
347
382
  if (this.params.userInfo !== info) return;
348
383
 
@@ -350,6 +385,10 @@ class AuthNostrService extends EventEmitter implements Signer {
350
385
  ...this.params.userInfo,
351
386
  picture: p?.image || p?.picture,
352
387
  name: p?.name || p?.displayName || p?.nip05 || nip19.npubEncode(info.pubkey),
388
+ // NOTE: do not overwrite info.nip05 with the one from profile!
389
+ // info.nip05 refers to nip46 provider,
390
+ // profile.nip05 is just a fancy name that user has chosen
391
+ // nip05: p?.nip05
353
392
  };
354
393
 
355
394
  this.setUserInfo(userInfo);
@@ -364,6 +403,7 @@ class AuthNostrService extends EventEmitter implements Signer {
364
403
  };
365
404
 
366
405
  if (type === 'logout') {
406
+ // reset
367
407
  if (this.iframe) this.iframe.remove();
368
408
  this.iframe = undefined;
369
409
  } else {
@@ -400,10 +440,12 @@ class AuthNostrService extends EventEmitter implements Signer {
400
440
  private async createIframe(iframeUrl?: string) {
401
441
  if (!iframeUrl) return undefined;
402
442
 
443
+ // ensure iframe
403
444
  const url = new URL(iframeUrl);
404
445
  const domain = url.hostname;
405
446
  let iframe: HTMLIFrameElement | undefined;
406
447
 
448
+ // one iframe per domain
407
449
  const did = domain.replaceAll('.', '-');
408
450
  const id = '__nostr-login-worker-iframe-' + did;
409
451
  iframe = document.querySelector(`#${id}`) as HTMLIFrameElement;
@@ -414,25 +456,46 @@ class AuthNostrService extends EventEmitter implements Signer {
414
456
  iframe.setAttribute('height', '0');
415
457
  iframe.setAttribute('border', '0');
416
458
  iframe.style.display = 'none';
459
+ // iframe.setAttribute('sandbox', 'allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts');
417
460
  iframe.id = id;
418
461
  document.body.append(iframe);
419
462
  }
420
463
 
464
+ // wait until loaded
421
465
  iframe.setAttribute('src', iframeUrl);
422
466
 
467
+ // we start listening right now to avoid races
468
+ // with 'load' event below
423
469
  const ready = new ReadyListener(['workerReady', 'workerError'], url.origin);
424
470
 
425
471
  await new Promise(ok => {
426
472
  iframe!.addEventListener('load', ok);
427
473
  });
428
474
 
475
+ // now make sure the iframe is ready,
476
+ // timeout timer starts here
429
477
  const r = await ready.wait();
430
478
 
479
+ // FIXME wait until the iframe is ready to accept requests,
480
+ // maybe it should send us some message?
481
+
431
482
  console.log('nostr-login iframe ready', iframeUrl, r);
432
483
 
433
484
  return { iframe, port: r[1] as MessagePort };
434
485
  }
435
486
 
487
+ // private async getIframeUrl(domain?: string) {
488
+ // if (!domain) return '';
489
+ // try {
490
+ // const r = await fetch(`https://${domain}/.well-known/nostr.json`);
491
+ // const data = await r.json();
492
+ // return data.nip46?.iframe_url || '';
493
+ // } catch (e) {
494
+ // console.log('failed to fetch iframe url', e, domain);
495
+ // return '';
496
+ // }
497
+ // }
498
+
436
499
  public async sendNeedAuth() {
437
500
  const [nostrconnect] = await this.getNostrConnectServices();
438
501
  const event = new CustomEvent('nlNeedAuth', { detail: { nostrconnect } });
@@ -445,19 +508,22 @@ class AuthNostrService extends EventEmitter implements Signer {
445
508
  }
446
509
 
447
510
  public async startAuth() {
448
- console.log('startAuth');
511
+ console.log("startAuth");
449
512
  if (this.readyCallback) throw new Error('Already started');
450
513
 
514
+ // start the new promise
451
515
  this.readyPromise = new Promise<void>(ok => (this.readyCallback = ok));
452
516
  }
453
517
 
454
518
  public async endAuth() {
455
519
  console.log('endAuth', this.params.userInfo);
456
520
  if (this.params.userInfo && this.params.userInfo.iframeUrl) {
521
+ // create iframe
457
522
  const { iframe, port } = (await this.createIframe(this.params.userInfo.iframeUrl)) || {};
458
523
  this.iframe = iframe;
459
524
  if (!this.iframe || !port) return;
460
525
 
526
+ // assign iframe to RPC object
461
527
  (this.signer!.rpc as IframeNostrRpc).setWorkerIframePort(port);
462
528
  }
463
529
 
@@ -482,58 +548,85 @@ class AuthNostrService extends EventEmitter implements Signer {
482
548
  }
483
549
 
484
550
  public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {}) {
551
+ // mutex
485
552
  if (this.signerPromise) {
486
553
  try {
487
554
  await this.signerPromise;
488
- } catch {}
555
+ } catch { }
489
556
  }
490
557
 
558
+ // we remove support for iframe from nip05 and bunker-url methods,
559
+ // only nostrconnect flow will use it.
560
+ // info.iframeUrl = info.iframeUrl || (await this.getIframeUrl(info.domain));
491
561
  console.log('initSigner info', info);
492
562
 
563
+ // start listening for the ready signal
493
564
  const iframeOrigin = info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined;
494
565
  if (iframeOrigin) this.starterReady = new ReadyListener(['starterDone', 'starterError'], iframeOrigin);
495
566
 
567
+ // notify modals so they could show the starter iframe,
568
+ // FIXME shouldn't this come from nostrconnect service list?
496
569
  this.emit('onIframeUrl', info.iframeUrl);
497
570
 
498
571
  this.signerPromise = new Promise<void>(async (ok, err) => {
499
572
  this.signerErrCallback = err;
500
573
  try {
574
+ // pre-connect if we're creating the connection (listen|connect) or
575
+ // not iframe mode
501
576
  if (info.relays && !info.iframeUrl) {
502
577
  for (const r of info.relays) {
503
578
  this.ndk.addExplicitRelay(r, undefined);
504
579
  }
505
580
  }
506
581
 
507
- await this.ndk.connect();
582
+ // wait until we connect, otherwise
583
+ // signer won't start properly
584
+ await this.ndk.connect(CONNECT_TIMEOUT);
508
585
 
586
+ // create and prepare the signer
509
587
  const localSigner = new PrivateKeySigner(info.sk!);
510
588
  this.signer = new Nip46Signer(this.ndk, localSigner, info.signerPubkey!, iframeOrigin);
511
589
 
590
+ // we should notify the banner the same way as
591
+ // the onAuthUrl does
512
592
  this.signer.on(`iframeRestart`, async () => {
513
593
  const iframeUrl = info.iframeUrl + (info.iframeUrl!.includes('?') ? '&' : '?') + 'pubkey=' + info.pubkey + '&rebind=' + localSigner.pubkey;
514
594
  this.emit('iframeRestart', { pubkey: info.pubkey, iframeUrl });
515
595
  });
516
596
 
597
+ // OAuth flow
598
+ // if (!listen) {
517
599
  this.signer.on('authUrl', (url: string) => {
518
600
  console.log('nostr login auth url', url);
519
601
 
602
+ // notify our UI
520
603
  this.emit('onAuthUrl', { url, iframeUrl: info.iframeUrl, eventToAddAccount });
521
604
  });
605
+ // }
522
606
 
523
607
  if (listen) {
608
+ // nostrconnect: flow
609
+ // wait for the incoming message from signer
524
610
  await this.listen(info);
525
611
  } else if (connect) {
612
+ // bunker: flow
613
+ // send 'connect' message to signer
526
614
  await this.connect(info, this.params.optionsModal.perms);
527
615
  } else {
616
+ // provide saved pubkey as a hint
528
617
  await this.signer!.initUserPubkey(info.pubkey);
529
618
  }
530
619
 
620
+ // ensure, we're using it in callbacks above
621
+ // and expect info to be valid after this call
531
622
  info.pubkey = this.signer!.userPubkey;
623
+ // learned after nostrconnect flow
532
624
  info.signerPubkey = this.signer!.remotePubkey;
533
625
 
534
626
  ok();
535
627
  } catch (e) {
536
628
  console.log('initSigner failure', e);
629
+ // make sure signer isn't set
537
630
  this.signer = null;
538
631
  err(e);
539
632
  }
@@ -556,35 +649,50 @@ class AuthNostrService extends EventEmitter implements Signer {
556
649
  if (domain) info.domain = domain;
557
650
  if (iframeUrl) info.iframeUrl = iframeUrl;
558
651
 
559
- if (!info.signerPubkey || !info.sk || !info.relays?.[0]) {
652
+ // console.log('nostr login auth info', info);
653
+ if (!info.signerPubkey || !info.sk || !info.relays || info.relays.length === 0) {
560
654
  throw new Error(`Bad bunker url ${bunkerUrl}`);
561
655
  }
562
656
 
563
657
  const eventToAddAccount = Boolean(this.params.userInfo);
564
658
  console.log('authNip46', type, info);
565
659
 
660
+ // updates the info
566
661
  await this.initSigner(info, { connect: true, eventToAddAccount });
567
662
 
663
+ // callback
568
664
  this.onAuth(type, info);
569
665
  } catch (e) {
570
666
  console.log('nostr login auth failed', e);
667
+ // make ure it's closed
668
+ // this.popupManager.closePopup();
571
669
  throw e;
572
670
  }
573
671
  }
574
672
 
575
- public async signEvent(ev: any) {
576
- const event = { ...ev };
577
- if (this.localSigner) {
578
- event.pubkey = getPublicKey(this.localSigner.privateKey!);
579
- event.id = getEventHash(event);
580
- event.sig = await this.localSigner.sign(event);
581
- } else {
582
- event.pubkey = this.signer?.remotePubkey;
583
- event.id = getEventHash(event);
584
- event.sig = await this.signer?.sign(event);
585
- }
586
- console.log('signed', { event });
587
- return event;
673
+ public async signEvent(event: any) {
674
+ const timeoutMs = 20000;
675
+
676
+ const signPromise = (async () => {
677
+ if (this.localSigner) {
678
+ event.pubkey = getPublicKey(this.localSigner.privateKey!);
679
+ event.id = getEventHash(event);
680
+ event.sig = await this.localSigner.sign(event);
681
+ } else {
682
+ event.pubkey = this.signer?.remotePubkey;
683
+ event.id = getEventHash(event);
684
+ event.sig = await this.signer?.sign(event);
685
+ }
686
+ return event;
687
+ })();
688
+
689
+ const timeoutPromise = new Promise((_, reject) => {
690
+ setTimeout(() => reject(new Error('Sign timeout')), timeoutMs);
691
+ });
692
+
693
+ const result = await Promise.race([signPromise, timeoutPromise]);
694
+ console.log('signed', { event: result });
695
+ return result;
588
696
  }
589
697
 
590
698
  private async codec_call(method: string, pubkey: string, param: string) {
@@ -611,6 +719,10 @@ class AuthNostrService extends EventEmitter implements Signer {
611
719
  if (this.localSigner) {
612
720
  return this.localSigner.decrypt(new NDKUser({ pubkey }), ciphertext);
613
721
  } else {
722
+ // decrypt is broken in ndk v2.3.1, and latest
723
+ // ndk v2.8.1 doesn't allow to override connect easily,
724
+ // so we reimplement and fix decrypt here as a temporary fix
725
+
614
726
  return this.codec_call('nip04_decrypt', pubkey, ciphertext);
615
727
  }
616
728
  }
@@ -619,6 +731,7 @@ class AuthNostrService extends EventEmitter implements Signer {
619
731
  if (this.localSigner) {
620
732
  return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
621
733
  } else {
734
+ // no support of nip44 in ndk yet
622
735
  return this.codec_call('nip44_encrypt', pubkey, plaintext);
623
736
  }
624
737
  }
@@ -627,6 +740,7 @@ class AuthNostrService extends EventEmitter implements Signer {
627
740
  if (this.localSigner) {
628
741
  return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
629
742
  } else {
743
+ // no support of nip44 in ndk yet
630
744
  return this.codec_call('nip44_decrypt', pubkey, ciphertext);
631
745
  }
632
746
  }
@@ -1,6 +1,6 @@
1
1
  import { NostrLoginOptions, TypeBanner } from '../types';
2
2
  import { NostrParams } from '.';
3
- import { Info } from 'nostr-login-components';
3
+ import { Info } from 'nostr-login-components/dist/types/types';
4
4
  import { EventEmitter } from 'tseep';
5
5
  import { getDarkMode } from '../utils';
6
6
  import { ReadyListener } from './Nip46';