@konemono/nostr-login 1.7.22 → 1.7.23
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.
- package/package.json +1 -1
- package/src/const/index.ts +1 -0
- package/src/iife-module.ts +7 -2
- package/src/index.ts +0 -7
- package/src/modules/AuthNostrService.ts +47 -204
- package/src/modules/BannerManager.ts +1 -9
- package/src/modules/ModalManager.ts +49 -56
- package/src/modules/Nip46.ts +82 -110
- package/src/modules/ProcessManager.ts +5 -47
- package/src/types.ts +2 -32
- package/src/utils/index.ts +15 -29
package/package.json
CHANGED
package/src/const/index.ts
CHANGED
package/src/iife-module.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { init } from './index';
|
|
2
2
|
import { NostrLoginOptions, StartScreens } from './types';
|
|
3
3
|
|
|
4
|
-
// wrap to hide local vars
|
|
5
4
|
(() => {
|
|
6
|
-
// currentScript only visible in global scope code, not event handlers
|
|
7
5
|
const cs = document.currentScript;
|
|
8
6
|
const start = async () => {
|
|
9
7
|
const options: NostrLoginOptions = {};
|
|
@@ -71,6 +69,13 @@ import { NostrLoginOptions, StartScreens } from './types';
|
|
|
71
69
|
const custom = cs.getAttribute('data-custom-nostr-connect') === 'true';
|
|
72
70
|
if (custom) options.customNostrConnect = custom;
|
|
73
71
|
|
|
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
|
+
|
|
74
79
|
console.log('nostr-login options', options);
|
|
75
80
|
}
|
|
76
81
|
|
package/src/index.ts
CHANGED
|
@@ -93,13 +93,6 @@ export class NostrLoginInitializer {
|
|
|
93
93
|
this.bannerManager.onUserInfo(info);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
this.authNostrService.on('timeout', () => {
|
|
97
|
-
console.log('nostr-login: timeout event received');
|
|
98
|
-
this.bannerManager.onCallTimeout();
|
|
99
|
-
// Don't cancel the connection on timeout - user may want to retry
|
|
100
|
-
// this.authNostrService.cancelNostrConnect();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
96
|
this.modalManager.on('onAuthUrlClick', url => {
|
|
104
97
|
this.openPopup(url);
|
|
105
98
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { localStorageAddAccount, bunkerUrlToInfo, isBunkerUrl, fetchProfile, getBunkerUrl, localStorageRemoveCurrentAccount, createProfile, getIcon } from '../utils';
|
|
2
|
-
import { ConnectionString, Info } from 'nostr-login-components';
|
|
2
|
+
import { ConnectionString, Info } from 'nostr-login-components/dist/types/types';
|
|
3
3
|
import { generatePrivateKey, getEventHash, getPublicKey, nip19 } from 'nostr-tools';
|
|
4
4
|
import { NostrLoginAuthOptions, Response } from '../types';
|
|
5
5
|
import NDK, { NDKEvent, NDKNip46Signer, NDKRpcResponse, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
@@ -11,7 +11,7 @@ import { IframeNostrRpc, Nip46Signer, ReadyListener } from './Nip46';
|
|
|
11
11
|
import { PrivateKeySigner } from './Signer';
|
|
12
12
|
|
|
13
13
|
const OUTBOX_RELAYS = ['wss://user.kindpag.es', 'wss://purplepag.es', 'wss://relay.nos.social'];
|
|
14
|
-
const
|
|
14
|
+
const DEFAULT_NOSTRCONNECT_RELAYS = ['wss://relay.nsec.app/', 'wss://ephemeral.snowflare.cc/'];
|
|
15
15
|
const NOSTRCONNECT_APPS: ConnectionString[] = [
|
|
16
16
|
{
|
|
17
17
|
name: 'Nsec.app',
|
|
@@ -106,8 +106,15 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
106
106
|
this.resetAuth();
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
private getDefaultRelays(customRelays?: string[]): string[] {
|
|
110
|
+
if (customRelays && customRelays.length > 0) {
|
|
111
|
+
return customRelays;
|
|
112
|
+
}
|
|
113
|
+
return [...DEFAULT_NOSTRCONNECT_RELAYS];
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
public async nostrConnect(
|
|
110
|
-
relay?: string,
|
|
117
|
+
relay?: string | string[],
|
|
111
118
|
{
|
|
112
119
|
domain = '',
|
|
113
120
|
link = '',
|
|
@@ -120,55 +127,34 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
120
127
|
iframeUrl?: string;
|
|
121
128
|
} = {},
|
|
122
129
|
) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const relays = relay
|
|
126
|
-
.split(',')
|
|
127
|
-
.map(r => r.replace(/['" \t\r\n]/g, '')) // Remove quotes, spaces, tabs, newlines
|
|
128
|
-
.filter(r => r)
|
|
129
|
-
.map(r => {
|
|
130
|
-
try {
|
|
131
|
-
const url = new URL(r);
|
|
132
|
-
if (url.protocol === 'wss:' || url.protocol === 'ws:') return url.href;
|
|
133
|
-
} catch {}
|
|
134
|
-
return null;
|
|
135
|
-
})
|
|
136
|
-
.filter(r => r !== null) as string[];
|
|
130
|
+
const relays = Array.isArray(relay) ? relay : relay ? [relay] : this.getDefaultRelays();
|
|
137
131
|
|
|
138
132
|
const info: Info = {
|
|
139
133
|
authMethod: 'connect',
|
|
140
|
-
pubkey: '',
|
|
141
|
-
signerPubkey: '',
|
|
134
|
+
pubkey: '',
|
|
135
|
+
signerPubkey: '',
|
|
142
136
|
sk: this.nostrConnectKey,
|
|
143
137
|
domain: domain,
|
|
144
|
-
relays,
|
|
138
|
+
relays: relays,
|
|
145
139
|
iframeUrl,
|
|
146
140
|
};
|
|
147
141
|
|
|
148
142
|
console.log('nostrconnect info', info, link);
|
|
149
143
|
|
|
150
|
-
// non-iframe flow
|
|
151
144
|
if (link && !iframeUrl) window.open(link, '_blank', 'width=400,height=700');
|
|
152
145
|
|
|
153
|
-
// init nip46 signer
|
|
154
146
|
await this.initSigner(info, { listen: true });
|
|
155
147
|
|
|
156
|
-
// signer learns the remote pubkey
|
|
157
148
|
if (!info.pubkey || !info.signerPubkey) throw new Error('Bad remote pubkey');
|
|
158
149
|
|
|
159
|
-
|
|
160
|
-
for (const r of relays) {
|
|
161
|
-
bunkerUrl += `relay=${r}&`;
|
|
162
|
-
}
|
|
163
|
-
info.bunkerUrl = bunkerUrl;
|
|
150
|
+
info.bunkerUrl = `bunker://${info.signerPubkey}?${relays.map(r => `relay=${r}`).join('&')}`;
|
|
164
151
|
|
|
165
|
-
// callback
|
|
166
152
|
if (!importConnect) this.onAuth('login', info);
|
|
167
153
|
|
|
168
154
|
return info;
|
|
169
155
|
}
|
|
170
156
|
|
|
171
|
-
public async createNostrConnect(relay?: string) {
|
|
157
|
+
public async createNostrConnect(relay?: string | string[]) {
|
|
172
158
|
this.nostrConnectKey = generatePrivateKey();
|
|
173
159
|
this.nostrConnectSecret = Math.random().toString(36).substring(7);
|
|
174
160
|
|
|
@@ -180,45 +166,40 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
180
166
|
perms: encodeURIComponent(this.params.optionsModal.perms || ''),
|
|
181
167
|
};
|
|
182
168
|
|
|
183
|
-
|
|
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}`;
|
|
184
173
|
}
|
|
185
174
|
|
|
186
175
|
public async getNostrConnectServices(): Promise<[string, ConnectionString[]]> {
|
|
187
|
-
const
|
|
176
|
+
const customRelays = this.params.optionsModal.connectRelays;
|
|
177
|
+
const nostrconnect = await this.createNostrConnect(customRelays);
|
|
188
178
|
|
|
189
|
-
// copy defaults
|
|
190
179
|
const apps = NOSTRCONNECT_APPS.map(a => ({ ...a }));
|
|
191
|
-
// if (this.params.optionsModal.dev) {
|
|
192
|
-
// apps.push({
|
|
193
|
-
// name: 'Dev.Nsec.app',
|
|
194
|
-
// domain: 'new.nsec.app',
|
|
195
|
-
// canImport: true,
|
|
196
|
-
// img: 'https://new.nsec.app/assets/favicon.ico',
|
|
197
|
-
// link: 'https://dev.nsec.app/<nostrconnect>',
|
|
198
|
-
// relay: 'wss://relay.nsec.app/',
|
|
199
|
-
// });
|
|
200
|
-
// }
|
|
201
180
|
|
|
202
181
|
for (const a of apps) {
|
|
203
|
-
let
|
|
182
|
+
let relays = customRelays && customRelays.length > 0 ? customRelays : this.getDefaultRelays();
|
|
183
|
+
|
|
204
184
|
if (a.link.startsWith('https://')) {
|
|
205
185
|
let domain = a.domain || new URL(a.link).hostname;
|
|
206
186
|
try {
|
|
207
187
|
const info = await (await fetch(`https://${domain}/.well-known/nostr.json`)).json();
|
|
208
188
|
const pubkey = info.names['_'];
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
189
|
+
const serviceRelays = info.nip46[pubkey] as string[];
|
|
190
|
+
if (serviceRelays && serviceRelays.length) {
|
|
191
|
+
relays = serviceRelays;
|
|
192
|
+
}
|
|
211
193
|
a.iframeUrl = info.nip46.iframe_url || '';
|
|
212
194
|
} catch (e) {
|
|
213
195
|
console.log('Bad app info', e, a);
|
|
214
196
|
}
|
|
215
197
|
}
|
|
216
|
-
|
|
198
|
+
|
|
199
|
+
const nc = nostrconnect + '&' + relays.map(r => `relay=${r}`).join('&');
|
|
217
200
|
if (a.iframeUrl) {
|
|
218
|
-
// pass plain nc url for iframe-based flow
|
|
219
201
|
a.link = nc;
|
|
220
202
|
} else {
|
|
221
|
-
// we will open popup ourselves
|
|
222
203
|
a.link = a.link.replace('<nostrconnect>', nc);
|
|
223
204
|
}
|
|
224
205
|
}
|
|
@@ -250,10 +231,8 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
250
231
|
}
|
|
251
232
|
|
|
252
233
|
public prepareImportUrl(url: string) {
|
|
253
|
-
// for OTP we choose interactive import
|
|
254
234
|
if (this.params.userInfo?.authMethod === 'otp') return url + '&import=true';
|
|
255
235
|
|
|
256
|
-
// for local we export our existing key
|
|
257
236
|
if (!this.localSigner || this.params.userInfo?.authMethod !== 'local') throw new Error('Most be local keys');
|
|
258
237
|
return url + '#import=' + nip19.nsecEncode(this.localSigner.privateKey!);
|
|
259
238
|
}
|
|
@@ -262,16 +241,13 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
262
241
|
const { relay, domain, link, iframeUrl } = cs;
|
|
263
242
|
if (!domain) throw new Error('Domain required');
|
|
264
243
|
|
|
265
|
-
const
|
|
244
|
+
const relays = relay ? [relay] : this.getDefaultRelays();
|
|
245
|
+
const info = await this.nostrConnect(relays, { domain, link, importConnect: true, iframeUrl });
|
|
266
246
|
|
|
267
|
-
|
|
268
|
-
// but keep the connect signer
|
|
269
|
-
await this.logout(/*keepSigner*/ true);
|
|
247
|
+
await this.logout(true);
|
|
270
248
|
|
|
271
|
-
// release local one
|
|
272
249
|
this.localSigner = null;
|
|
273
250
|
|
|
274
|
-
// notify app that we've switched to 'connect' keys
|
|
275
251
|
this.onAuth('login', info);
|
|
276
252
|
}
|
|
277
253
|
|
|
@@ -301,41 +277,29 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
301
277
|
public async createAccount(nip05: string) {
|
|
302
278
|
const [name, domain] = nip05.split('@');
|
|
303
279
|
|
|
304
|
-
// bunker's own url
|
|
305
280
|
const bunkerUrl = await getBunkerUrl(`_@${domain}`, this.params.optionsModal);
|
|
306
281
|
console.log("create account bunker's url", bunkerUrl);
|
|
307
282
|
|
|
308
|
-
// parse bunker url and generate local nsec
|
|
309
283
|
const info = bunkerUrlToInfo(bunkerUrl);
|
|
310
284
|
if (!info.signerPubkey) throw new Error('Bad bunker url');
|
|
311
285
|
|
|
312
286
|
const eventToAddAccount = Boolean(this.params.userInfo);
|
|
313
287
|
|
|
314
|
-
// init signer to talk to the bunker (not the user!)
|
|
315
288
|
await this.initSigner(info, { eventToAddAccount });
|
|
316
289
|
|
|
317
290
|
const userPubkey = await this.signer!.createAccount2({ bunkerPubkey: info.signerPubkey!, name, domain, perms: this.params.optionsModal.perms });
|
|
318
291
|
|
|
319
292
|
return {
|
|
320
293
|
bunkerUrl: `bunker://${userPubkey}?relay=${info.relays?.[0]}`,
|
|
321
|
-
sk: info.sk,
|
|
294
|
+
sk: info.sk,
|
|
322
295
|
};
|
|
323
296
|
}
|
|
324
297
|
|
|
325
298
|
private releaseSigner() {
|
|
326
|
-
// cleanup iframe ping interval if using IframeNostrRpc
|
|
327
|
-
if (this.signer && '_rpc' in this.signer) {
|
|
328
|
-
const rpc = (this.signer as any)._rpc;
|
|
329
|
-
if (rpc && typeof rpc.cleanup === 'function') {
|
|
330
|
-
rpc.cleanup();
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
299
|
this.signer = null;
|
|
335
300
|
this.signerErrCallback?.('cancelled');
|
|
336
301
|
this.localSigner = null;
|
|
337
302
|
|
|
338
|
-
// disconnect from signer relays
|
|
339
303
|
for (const r of this.ndk.pool.relays.keys()) {
|
|
340
304
|
this.ndk.pool.removeRelay(r);
|
|
341
305
|
}
|
|
@@ -344,10 +308,8 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
344
308
|
public async logout(keepSigner = false) {
|
|
345
309
|
if (!keepSigner) this.releaseSigner();
|
|
346
310
|
|
|
347
|
-
// move current to recent
|
|
348
311
|
localStorageRemoveCurrentAccount();
|
|
349
312
|
|
|
350
|
-
// notify everyone
|
|
351
313
|
this.onAuth('logout');
|
|
352
314
|
|
|
353
315
|
this.emit('updateAccounts');
|
|
@@ -372,17 +334,15 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
372
334
|
private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null) {
|
|
373
335
|
if (type !== 'logout' && !info) throw new Error('No user info in onAuth');
|
|
374
336
|
|
|
375
|
-
// make sure we emulate logout first
|
|
376
337
|
if (info && this.params.userInfo && (info.pubkey !== this.params.userInfo.pubkey || info.authMethod !== this.params.userInfo.authMethod)) {
|
|
377
338
|
const event = new CustomEvent('nlAuth', { detail: { type: 'logout' } });
|
|
378
339
|
console.log('nostr-login auth', event.detail);
|
|
379
|
-
document.dispatchEvent(event)
|
|
340
|
+
document.dispatchEvent(event);
|
|
380
341
|
}
|
|
381
342
|
|
|
382
343
|
this.setUserInfo(info);
|
|
383
344
|
|
|
384
345
|
if (info) {
|
|
385
|
-
// async profile fetch
|
|
386
346
|
fetchProfile(info, this.profileNdk).then(p => {
|
|
387
347
|
if (this.params.userInfo !== info) return;
|
|
388
348
|
|
|
@@ -390,10 +350,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
390
350
|
...this.params.userInfo,
|
|
391
351
|
picture: p?.image || p?.picture,
|
|
392
352
|
name: p?.name || p?.displayName || p?.nip05 || nip19.npubEncode(info.pubkey),
|
|
393
|
-
// NOTE: do not overwrite info.nip05 with the one from profile!
|
|
394
|
-
// info.nip05 refers to nip46 provider,
|
|
395
|
-
// profile.nip05 is just a fancy name that user has chosen
|
|
396
|
-
// nip05: p?.nip05
|
|
397
353
|
};
|
|
398
354
|
|
|
399
355
|
this.setUserInfo(userInfo);
|
|
@@ -408,7 +364,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
408
364
|
};
|
|
409
365
|
|
|
410
366
|
if (type === 'logout') {
|
|
411
|
-
// reset
|
|
412
367
|
if (this.iframe) this.iframe.remove();
|
|
413
368
|
this.iframe = undefined;
|
|
414
369
|
} else {
|
|
@@ -445,12 +400,10 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
445
400
|
private async createIframe(iframeUrl?: string) {
|
|
446
401
|
if (!iframeUrl) return undefined;
|
|
447
402
|
|
|
448
|
-
// ensure iframe
|
|
449
403
|
const url = new URL(iframeUrl);
|
|
450
404
|
const domain = url.hostname;
|
|
451
405
|
let iframe: HTMLIFrameElement | undefined;
|
|
452
406
|
|
|
453
|
-
// one iframe per domain
|
|
454
407
|
const did = domain.replaceAll('.', '-');
|
|
455
408
|
const id = '__nostr-login-worker-iframe-' + did;
|
|
456
409
|
iframe = document.querySelector(`#${id}`) as HTMLIFrameElement;
|
|
@@ -461,50 +414,25 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
461
414
|
iframe.setAttribute('height', '0');
|
|
462
415
|
iframe.setAttribute('border', '0');
|
|
463
416
|
iframe.style.display = 'none';
|
|
464
|
-
// iframe.setAttribute('sandbox', 'allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts');
|
|
465
417
|
iframe.id = id;
|
|
466
418
|
document.body.append(iframe);
|
|
467
419
|
}
|
|
468
420
|
|
|
469
|
-
// wait until loaded
|
|
470
421
|
iframe.setAttribute('src', iframeUrl);
|
|
471
422
|
|
|
472
|
-
// we start listening right now to avoid races
|
|
473
|
-
// with 'load' event below
|
|
474
423
|
const ready = new ReadyListener(['workerReady', 'workerError'], url.origin);
|
|
475
424
|
|
|
476
|
-
await new Promise(
|
|
477
|
-
|
|
478
|
-
iframe!.addEventListener('load', () => {
|
|
479
|
-
clearTimeout(timeoutId);
|
|
480
|
-
ok(undefined);
|
|
481
|
-
});
|
|
425
|
+
await new Promise(ok => {
|
|
426
|
+
iframe!.addEventListener('load', ok);
|
|
482
427
|
});
|
|
483
428
|
|
|
484
|
-
// now make sure the iframe is ready,
|
|
485
|
-
// timeout timer starts here
|
|
486
429
|
const r = await ready.wait();
|
|
487
430
|
|
|
488
|
-
// FIXME wait until the iframe is ready to accept requests,
|
|
489
|
-
// maybe it should send us some message?
|
|
490
|
-
|
|
491
431
|
console.log('nostr-login iframe ready', iframeUrl, r);
|
|
492
432
|
|
|
493
433
|
return { iframe, port: r[1] as MessagePort };
|
|
494
434
|
}
|
|
495
435
|
|
|
496
|
-
// private async getIframeUrl(domain?: string) {
|
|
497
|
-
// if (!domain) return '';
|
|
498
|
-
// try {
|
|
499
|
-
// const r = await fetch(`https://${domain}/.well-known/nostr.json`);
|
|
500
|
-
// const data = await r.json();
|
|
501
|
-
// return data.nip46?.iframe_url || '';
|
|
502
|
-
// } catch (e) {
|
|
503
|
-
// console.log('failed to fetch iframe url', e, domain);
|
|
504
|
-
// return '';
|
|
505
|
-
// }
|
|
506
|
-
// }
|
|
507
|
-
|
|
508
436
|
public async sendNeedAuth() {
|
|
509
437
|
const [nostrconnect] = await this.getNostrConnectServices();
|
|
510
438
|
const event = new CustomEvent('nlNeedAuth', { detail: { nostrconnect } });
|
|
@@ -517,22 +445,19 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
517
445
|
}
|
|
518
446
|
|
|
519
447
|
public async startAuth() {
|
|
520
|
-
console.log(
|
|
448
|
+
console.log('startAuth');
|
|
521
449
|
if (this.readyCallback) throw new Error('Already started');
|
|
522
450
|
|
|
523
|
-
// start the new promise
|
|
524
451
|
this.readyPromise = new Promise<void>(ok => (this.readyCallback = ok));
|
|
525
452
|
}
|
|
526
453
|
|
|
527
454
|
public async endAuth() {
|
|
528
455
|
console.log('endAuth', this.params.userInfo);
|
|
529
456
|
if (this.params.userInfo && this.params.userInfo.iframeUrl) {
|
|
530
|
-
// create iframe
|
|
531
457
|
const { iframe, port } = (await this.createIframe(this.params.userInfo.iframeUrl)) || {};
|
|
532
458
|
this.iframe = iframe;
|
|
533
459
|
if (!this.iframe || !port) return;
|
|
534
460
|
|
|
535
|
-
// assign iframe to RPC object
|
|
536
461
|
(this.signer!.rpc as IframeNostrRpc).setWorkerIframePort(port);
|
|
537
462
|
}
|
|
538
463
|
|
|
@@ -557,113 +482,58 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
557
482
|
}
|
|
558
483
|
|
|
559
484
|
public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {}) {
|
|
560
|
-
// mutex
|
|
561
485
|
if (this.signerPromise) {
|
|
562
486
|
try {
|
|
563
487
|
await this.signerPromise;
|
|
564
488
|
} catch {}
|
|
565
489
|
}
|
|
566
490
|
|
|
567
|
-
// we remove support for iframe from nip05 and bunker-url methods,
|
|
568
|
-
// only nostrconnect flow will use it.
|
|
569
|
-
// info.iframeUrl = info.iframeUrl || (await this.getIframeUrl(info.domain));
|
|
570
491
|
console.log('initSigner info', info);
|
|
571
492
|
|
|
572
|
-
// start listening for the ready signal
|
|
573
493
|
const iframeOrigin = info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined;
|
|
574
494
|
if (iframeOrigin) this.starterReady = new ReadyListener(['starterDone', 'starterError'], iframeOrigin);
|
|
575
495
|
|
|
576
|
-
// notify modals so they could show the starter iframe,
|
|
577
|
-
// FIXME shouldn't this come from nostrconnect service list?
|
|
578
496
|
this.emit('onIframeUrl', info.iframeUrl);
|
|
579
497
|
|
|
580
498
|
this.signerPromise = new Promise<void>(async (ok, err) => {
|
|
581
499
|
this.signerErrCallback = err;
|
|
582
500
|
try {
|
|
583
|
-
// pre-connect if we're creating the connection (listen|connect) or
|
|
584
|
-
// not iframe mode
|
|
585
501
|
if (info.relays && !info.iframeUrl) {
|
|
586
502
|
for (const r of info.relays) {
|
|
587
503
|
this.ndk.addExplicitRelay(r, undefined);
|
|
588
504
|
}
|
|
589
505
|
}
|
|
590
506
|
|
|
591
|
-
|
|
592
|
-
// signer won't start properly
|
|
593
|
-
await new Promise<void>((resolve, reject) => {
|
|
594
|
-
let finished = false
|
|
595
|
-
// 10 sec timeout for connection
|
|
596
|
-
setTimeout(() => {
|
|
597
|
-
if (!finished) {
|
|
598
|
-
finished = true
|
|
599
|
-
console.log('nostr-login: relay connection timeout, emitting timeout event');
|
|
600
|
-
this.emit('timeout');
|
|
601
|
-
reject('Connection timed out')
|
|
602
|
-
}
|
|
603
|
-
}, 10000)
|
|
604
|
-
|
|
605
|
-
this.ndk.connect().then(() => {
|
|
606
|
-
if (!finished) {
|
|
607
|
-
finished = true
|
|
608
|
-
resolve()
|
|
609
|
-
}
|
|
610
|
-
}).catch(e => {
|
|
611
|
-
if (!finished) {
|
|
612
|
-
finished = true
|
|
613
|
-
reject(e)
|
|
614
|
-
}
|
|
615
|
-
})
|
|
616
|
-
});
|
|
507
|
+
await this.ndk.connect();
|
|
617
508
|
|
|
618
|
-
// create and prepare the signer
|
|
619
509
|
const localSigner = new PrivateKeySigner(info.sk!);
|
|
620
510
|
this.signer = new Nip46Signer(this.ndk, localSigner, info.signerPubkey!, iframeOrigin);
|
|
621
511
|
|
|
622
|
-
// forward timeout events
|
|
623
|
-
this.signer.on('timeout', () => {
|
|
624
|
-
this.emit('timeout');
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
// we should notify the banner the same way as
|
|
628
|
-
// the onAuthUrl does
|
|
629
512
|
this.signer.on(`iframeRestart`, async () => {
|
|
630
513
|
const iframeUrl = info.iframeUrl + (info.iframeUrl!.includes('?') ? '&' : '?') + 'pubkey=' + info.pubkey + '&rebind=' + localSigner.pubkey;
|
|
631
514
|
this.emit('iframeRestart', { pubkey: info.pubkey, iframeUrl });
|
|
632
515
|
});
|
|
633
516
|
|
|
634
|
-
// OAuth flow
|
|
635
|
-
// if (!listen) {
|
|
636
517
|
this.signer.on('authUrl', (url: string) => {
|
|
637
518
|
console.log('nostr login auth url', url);
|
|
638
519
|
|
|
639
|
-
// notify our UI
|
|
640
520
|
this.emit('onAuthUrl', { url, iframeUrl: info.iframeUrl, eventToAddAccount });
|
|
641
521
|
});
|
|
642
|
-
// }
|
|
643
522
|
|
|
644
523
|
if (listen) {
|
|
645
|
-
// nostrconnect: flow
|
|
646
|
-
// wait for the incoming message from signer
|
|
647
524
|
await this.listen(info);
|
|
648
525
|
} else if (connect) {
|
|
649
|
-
// bunker: flow
|
|
650
|
-
// send 'connect' message to signer
|
|
651
526
|
await this.connect(info, this.params.optionsModal.perms);
|
|
652
527
|
} else {
|
|
653
|
-
// provide saved pubkey as a hint
|
|
654
528
|
await this.signer!.initUserPubkey(info.pubkey);
|
|
655
529
|
}
|
|
656
530
|
|
|
657
|
-
// ensure, we're using it in callbacks above
|
|
658
|
-
// and expect info to be valid after this call
|
|
659
531
|
info.pubkey = this.signer!.userPubkey;
|
|
660
|
-
// learned after nostrconnect flow
|
|
661
532
|
info.signerPubkey = this.signer!.remotePubkey;
|
|
662
533
|
|
|
663
534
|
ok();
|
|
664
535
|
} catch (e) {
|
|
665
536
|
console.log('initSigner failure', e);
|
|
666
|
-
// make sure signer isn't set
|
|
667
537
|
this.signer = null;
|
|
668
538
|
err(e);
|
|
669
539
|
}
|
|
@@ -686,7 +556,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
686
556
|
if (domain) info.domain = domain;
|
|
687
557
|
if (iframeUrl) info.iframeUrl = iframeUrl;
|
|
688
558
|
|
|
689
|
-
// console.log('nostr login auth info', info);
|
|
690
559
|
if (!info.signerPubkey || !info.sk || !info.relays?.[0]) {
|
|
691
560
|
throw new Error(`Bad bunker url ${bunkerUrl}`);
|
|
692
561
|
}
|
|
@@ -694,47 +563,27 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
694
563
|
const eventToAddAccount = Boolean(this.params.userInfo);
|
|
695
564
|
console.log('authNip46', type, info);
|
|
696
565
|
|
|
697
|
-
// updates the info
|
|
698
566
|
await this.initSigner(info, { connect: true, eventToAddAccount });
|
|
699
567
|
|
|
700
|
-
// callback
|
|
701
568
|
this.onAuth(type, info);
|
|
702
569
|
} catch (e) {
|
|
703
570
|
console.log('nostr login auth failed', e);
|
|
704
|
-
// make ure it's closed
|
|
705
|
-
// this.popupManager.closePopup();
|
|
706
571
|
throw e;
|
|
707
572
|
}
|
|
708
573
|
}
|
|
709
574
|
|
|
710
575
|
public async signEvent(event: any) {
|
|
711
|
-
console.log('AuthNostrService.signEvent called', {
|
|
712
|
-
hasLocalSigner: !!this.localSigner,
|
|
713
|
-
hasSigner: !!this.signer,
|
|
714
|
-
signerRemotePubkey: this.signer?.remotePubkey,
|
|
715
|
-
eventBefore: JSON.parse(JSON.stringify(event))
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
// Create a copy to avoid mutating the original event on timeout
|
|
719
|
-
const eventToSign = { ...event };
|
|
720
|
-
|
|
721
576
|
if (this.localSigner) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
577
|
+
event.pubkey = getPublicKey(this.localSigner.privateKey!);
|
|
578
|
+
event.id = getEventHash(event);
|
|
579
|
+
event.sig = await this.localSigner.sign(event);
|
|
725
580
|
} else {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
eventToSign.pubkey = this.signer.remotePubkey;
|
|
731
|
-
eventToSign.id = getEventHash(eventToSign);
|
|
732
|
-
console.log('Calling signer.sign with event:', eventToSign);
|
|
733
|
-
eventToSign.sig = await this.signer.sign(eventToSign);
|
|
734
|
-
console.log('Received signature:', eventToSign.sig);
|
|
581
|
+
event.pubkey = this.signer?.remotePubkey;
|
|
582
|
+
event.id = getEventHash(event);
|
|
583
|
+
event.sig = await this.signer?.sign(event);
|
|
735
584
|
}
|
|
736
|
-
console.log('signed
|
|
737
|
-
return
|
|
585
|
+
console.log('signed', { event });
|
|
586
|
+
return event;
|
|
738
587
|
}
|
|
739
588
|
|
|
740
589
|
private async codec_call(method: string, pubkey: string, param: string) {
|
|
@@ -761,10 +610,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
761
610
|
if (this.localSigner) {
|
|
762
611
|
return this.localSigner.decrypt(new NDKUser({ pubkey }), ciphertext);
|
|
763
612
|
} else {
|
|
764
|
-
// decrypt is broken in ndk v2.3.1, and latest
|
|
765
|
-
// ndk v2.8.1 doesn't allow to override connect easily,
|
|
766
|
-
// so we reimplement and fix decrypt here as a temporary fix
|
|
767
|
-
|
|
768
613
|
return this.codec_call('nip04_decrypt', pubkey, ciphertext);
|
|
769
614
|
}
|
|
770
615
|
}
|
|
@@ -773,7 +618,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
773
618
|
if (this.localSigner) {
|
|
774
619
|
return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
|
|
775
620
|
} else {
|
|
776
|
-
// no support of nip44 in ndk yet
|
|
777
621
|
return this.codec_call('nip44_encrypt', pubkey, plaintext);
|
|
778
622
|
}
|
|
779
623
|
}
|
|
@@ -782,7 +626,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
782
626
|
if (this.localSigner) {
|
|
783
627
|
return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
|
|
784
628
|
} else {
|
|
785
|
-
// no support of nip44 in ndk yet
|
|
786
629
|
return this.codec_call('nip44_decrypt', pubkey, ciphertext);
|
|
787
630
|
}
|
|
788
631
|
}
|
|
@@ -47,15 +47,10 @@ class BannerManager extends EventEmitter {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
public onCallTimeout() {
|
|
50
|
-
console.log('BannerManager.onCallTimeout called, banner:', this.banner);
|
|
51
50
|
if (this.banner) {
|
|
52
|
-
console.log('Setting banner isLoading=false and notify=timeout');
|
|
53
|
-
this.banner.isLoading = false;
|
|
54
51
|
this.banner.notify = {
|
|
55
52
|
mode: 'timeout',
|
|
56
53
|
};
|
|
57
|
-
} else {
|
|
58
|
-
console.log('Banner is null, cannot set timeout notification');
|
|
59
54
|
}
|
|
60
55
|
}
|
|
61
56
|
|
|
@@ -72,10 +67,7 @@ class BannerManager extends EventEmitter {
|
|
|
72
67
|
this.iframeReady = undefined;
|
|
73
68
|
}
|
|
74
69
|
this.banner.isLoading = false;
|
|
75
|
-
|
|
76
|
-
if (this.banner.notify?.mode !== 'timeout') {
|
|
77
|
-
this.banner.notify = { mode: '' };
|
|
78
|
-
}
|
|
70
|
+
this.banner.notify = { mode: '' };
|
|
79
71
|
}
|
|
80
72
|
}
|
|
81
73
|
|