@konemono/nostr-login 1.7.22 → 1.7.24
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 +115 -109
- package/src/modules/ProcessManager.ts +5 -47
- package/src/types.ts +2 -32
- package/src/utils/index.ts +15 -29
package/src/modules/Nip46.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
2
2
|
import { validateEvent, verifySignature } from 'nostr-tools';
|
|
3
3
|
import { PrivateKeySigner } from './Signer';
|
|
4
|
+
import { NIP46_TIMEOUT } from '../const';
|
|
4
5
|
|
|
5
6
|
class NostrRpc extends NDKNostrRpc {
|
|
6
7
|
protected _ndk: NDK;
|
|
7
8
|
protected _signer: PrivateKeySigner;
|
|
8
9
|
protected requests: Set<string> = new Set();
|
|
10
|
+
protected requestTimeouts: Map<string, NodeJS.Timeout> = new Map();
|
|
9
11
|
private sub?: NDKSubscription;
|
|
10
12
|
protected _useNip44: boolean = false;
|
|
11
13
|
|
|
@@ -16,19 +18,34 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
|
|
19
|
-
// NOTE: fixing ndk
|
|
20
21
|
filter.kinds = filter.kinds?.filter(k => k === 24133);
|
|
21
22
|
this.sub = await super.subscribe(filter);
|
|
22
23
|
return this.sub;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
public stop() {
|
|
27
|
+
this.clearAllTimeouts();
|
|
26
28
|
if (this.sub) {
|
|
27
29
|
this.sub.stop();
|
|
28
30
|
this.sub = undefined;
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
private clearAllTimeouts() {
|
|
35
|
+
for (const timeout of this.requestTimeouts.values()) {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
}
|
|
38
|
+
this.requestTimeouts.clear();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private clearTimeout(id: string) {
|
|
42
|
+
const timeout = this.requestTimeouts.get(id);
|
|
43
|
+
if (timeout) {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
this.requestTimeouts.delete(id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
public setUseNip44(useNip44: boolean) {
|
|
33
50
|
this._useNip44 = useNip44;
|
|
34
51
|
}
|
|
@@ -39,7 +56,6 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
39
56
|
return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
|
|
40
57
|
}
|
|
41
58
|
|
|
42
|
-
// override to auto-decrypt nip04/nip44
|
|
43
59
|
public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
|
|
44
60
|
const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
|
|
45
61
|
remoteUser.ndk = this._ndk;
|
|
@@ -68,9 +84,6 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
68
84
|
}
|
|
69
85
|
}
|
|
70
86
|
|
|
71
|
-
// ndk doesn't support nostrconnect:
|
|
72
|
-
// we just listed to an unsolicited reply to
|
|
73
|
-
// our pubkey and if it's ack/secret - we're fine
|
|
74
87
|
public async listen(nostrConnectSecret: string): Promise<string> {
|
|
75
88
|
const pubkey = this._signer.pubkey;
|
|
76
89
|
console.log('nostr-login listening for conn to', pubkey);
|
|
@@ -78,61 +91,46 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
78
91
|
'kinds': [24133],
|
|
79
92
|
'#p': [pubkey],
|
|
80
93
|
});
|
|
81
|
-
|
|
82
94
|
return new Promise<string>((ok, err) => {
|
|
83
|
-
let finished = false;
|
|
84
|
-
|
|
85
|
-
// 30 sec timeout
|
|
86
95
|
const timeoutId = setTimeout(() => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.emit('timeout');
|
|
92
|
-
err('Listen timed out');
|
|
93
|
-
}
|
|
94
|
-
}, 30000);
|
|
95
|
-
|
|
96
|
+
this.stop();
|
|
97
|
+
err(new Error('NIP46 listen timeout'));
|
|
98
|
+
}, NIP46_TIMEOUT);
|
|
99
|
+
|
|
96
100
|
sub.on('event', async (event: NDKEvent) => {
|
|
97
|
-
if (finished) return;
|
|
98
|
-
|
|
99
101
|
try {
|
|
100
102
|
const parsedEvent = await this.parseEvent(event);
|
|
101
|
-
// console.log('ack parsedEvent', parsedEvent);
|
|
102
103
|
if (!(parsedEvent as NDKRpcRequest).method) {
|
|
103
104
|
const response = parsedEvent as NDKRpcResponse;
|
|
104
105
|
|
|
105
|
-
// ignore
|
|
106
106
|
if (response.result === 'auth_url') return;
|
|
107
107
|
|
|
108
|
-
// FIXME for now accept 'ack' replies, later on only
|
|
109
|
-
// accept secrets
|
|
110
108
|
if (response.result === 'ack' || response.result === nostrConnectSecret) {
|
|
111
|
-
finished = true;
|
|
112
109
|
clearTimeout(timeoutId);
|
|
113
|
-
this.stop();
|
|
114
110
|
ok(event.pubkey);
|
|
115
111
|
} else {
|
|
116
|
-
finished = true;
|
|
117
112
|
clearTimeout(timeoutId);
|
|
118
|
-
this.stop();
|
|
119
113
|
err(response.error);
|
|
120
114
|
}
|
|
121
115
|
}
|
|
122
116
|
} catch (e) {
|
|
123
117
|
console.log('error parsing event', e, event.rawEvent());
|
|
124
118
|
}
|
|
119
|
+
this.stop();
|
|
125
120
|
});
|
|
126
121
|
});
|
|
127
122
|
}
|
|
128
123
|
|
|
129
|
-
// since ndk doesn't yet support perms param
|
|
130
|
-
// we reimplement the 'connect' call here
|
|
131
|
-
// instead of await signer.blockUntilReady();
|
|
132
124
|
public async connect(pubkey: string, token?: string, perms?: string) {
|
|
133
125
|
return new Promise<void>((ok, err) => {
|
|
134
126
|
const connectParams = [pubkey!, token || '', perms || ''];
|
|
127
|
+
|
|
128
|
+
const timeoutId = setTimeout(() => {
|
|
129
|
+
err(new Error('NIP46 connect timeout'));
|
|
130
|
+
}, NIP46_TIMEOUT);
|
|
131
|
+
|
|
135
132
|
this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
|
|
133
|
+
clearTimeout(timeoutId);
|
|
136
134
|
if (response.result === 'ack') {
|
|
137
135
|
ok();
|
|
138
136
|
} else {
|
|
@@ -146,25 +144,45 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
146
144
|
return Math.random().toString(36).substring(7);
|
|
147
145
|
}
|
|
148
146
|
|
|
147
|
+
protected async ensureConnected(): Promise<void> {
|
|
148
|
+
const connectedRelays = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1);
|
|
149
|
+
|
|
150
|
+
if (connectedRelays.length === 0) {
|
|
151
|
+
console.log('No connected relays, reconnecting...');
|
|
152
|
+
await this._ndk.connect();
|
|
153
|
+
|
|
154
|
+
await new Promise<void>((resolve, reject) => {
|
|
155
|
+
const timeout = setTimeout(() => {
|
|
156
|
+
reject(new Error('Failed to reconnect to relays'));
|
|
157
|
+
}, 5000);
|
|
158
|
+
|
|
159
|
+
const checkConnection = () => {
|
|
160
|
+
const connected = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1);
|
|
161
|
+
if (connected.length > 0) {
|
|
162
|
+
clearTimeout(timeout);
|
|
163
|
+
console.log('Reconnected to', connected.length, 'relays');
|
|
164
|
+
resolve();
|
|
165
|
+
} else {
|
|
166
|
+
setTimeout(checkConnection, 100);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
checkConnection();
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
149
174
|
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
175
|
+
await this.ensureConnected();
|
|
176
|
+
|
|
150
177
|
const id = this.getId();
|
|
151
178
|
|
|
152
|
-
// response handler will deduplicate auth urls and responses
|
|
153
179
|
this.setResponseHandler(id, cb);
|
|
154
180
|
|
|
155
|
-
// create and sign request
|
|
156
181
|
const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
|
|
157
|
-
console.log(
|
|
182
|
+
console.log('sendRequest', { event, method, remotePubkey, params });
|
|
158
183
|
|
|
159
|
-
|
|
160
|
-
const relays = await event.publish();
|
|
161
|
-
if (relays.size === 0) throw new Error('Failed to publish to relays');
|
|
184
|
+
await event.publish();
|
|
162
185
|
|
|
163
|
-
// NOTE: ndk returns a promise that never resolves and
|
|
164
|
-
// in fact REQUIRES cb to be provided (otherwise no way
|
|
165
|
-
// to consume the result), we've already stepped on the bug
|
|
166
|
-
// of waiting for this unresolvable result, so now we return
|
|
167
|
-
// undefined to make sure waiters fail, not hang.
|
|
168
186
|
// @ts-ignore
|
|
169
187
|
return undefined as NDKRpcResponse;
|
|
170
188
|
}
|
|
@@ -172,7 +190,26 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
172
190
|
protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
|
|
173
191
|
let authUrlSent = false;
|
|
174
192
|
const now = Date.now();
|
|
175
|
-
|
|
193
|
+
|
|
194
|
+
const timeoutId = setTimeout(() => {
|
|
195
|
+
if (this.requests.has(id)) {
|
|
196
|
+
this.requests.delete(id);
|
|
197
|
+
this.requestTimeouts.delete(id);
|
|
198
|
+
console.log('NIP46 request timeout for', id);
|
|
199
|
+
if (cb) {
|
|
200
|
+
cb({
|
|
201
|
+
id,
|
|
202
|
+
result: '',
|
|
203
|
+
error: 'NIP46 request timeout',
|
|
204
|
+
event: undefined as any,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}, NIP46_TIMEOUT);
|
|
209
|
+
|
|
210
|
+
this.requestTimeouts.set(id, timeoutId);
|
|
211
|
+
|
|
212
|
+
return new Promise<NDKRpcResponse>(() => {
|
|
176
213
|
const responseHandler = (response: NDKRpcResponse) => {
|
|
177
214
|
if (response.result === 'auth_url') {
|
|
178
215
|
this.once(`response-${id}`, responseHandler);
|
|
@@ -182,30 +219,18 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
182
219
|
}
|
|
183
220
|
} else if (cb) {
|
|
184
221
|
if (this.requests.has(id)) {
|
|
222
|
+
this.clearTimeout(id);
|
|
185
223
|
this.requests.delete(id);
|
|
186
224
|
console.log('nostr-login processed nip46 request in', Date.now() - now, 'ms');
|
|
187
225
|
cb(response);
|
|
188
|
-
resolve(response);
|
|
189
226
|
}
|
|
190
227
|
}
|
|
191
228
|
};
|
|
192
229
|
|
|
193
230
|
this.once(`response-${id}`, responseHandler);
|
|
194
|
-
|
|
195
|
-
// timeout
|
|
196
|
-
setTimeout(() => {
|
|
197
|
-
if (this.requests.has(id)) {
|
|
198
|
-
this.requests.delete(id);
|
|
199
|
-
this.removeListener(`response-${id}`, responseHandler);
|
|
200
|
-
console.log('nostr-login: NIP-46 request timeout, emitting timeout event');
|
|
201
|
-
this.emit('timeout');
|
|
202
|
-
reject('[Nip46] Request timed out');
|
|
203
|
-
}
|
|
204
|
-
}, 30000);
|
|
205
231
|
});
|
|
206
232
|
}
|
|
207
233
|
|
|
208
|
-
|
|
209
234
|
protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
|
|
210
235
|
this.requests.add(id);
|
|
211
236
|
const localUser = await this._signer.user();
|
|
@@ -232,7 +257,6 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
232
257
|
private peerOrigin?: string;
|
|
233
258
|
private iframePort?: MessagePort;
|
|
234
259
|
private iframeRequests = new Map<string, { id: string; pubkey: string }>();
|
|
235
|
-
private iframePingInterval?: NodeJS.Timeout;
|
|
236
260
|
|
|
237
261
|
public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
|
|
238
262
|
super(ndk, localSigner);
|
|
@@ -246,7 +270,6 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
246
270
|
this._ndk,
|
|
247
271
|
{},
|
|
248
272
|
{
|
|
249
|
-
// don't send to relay
|
|
250
273
|
closeOnEose: true,
|
|
251
274
|
cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
|
|
252
275
|
},
|
|
@@ -258,8 +281,7 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
258
281
|
|
|
259
282
|
this.iframePort = port;
|
|
260
283
|
|
|
261
|
-
|
|
262
|
-
this.iframePingInterval = setInterval(() => {
|
|
284
|
+
setInterval(() => {
|
|
263
285
|
console.log('iframe-nip46 ping');
|
|
264
286
|
this.iframePort!.postMessage('ping');
|
|
265
287
|
}, 5000);
|
|
@@ -273,7 +295,6 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
273
295
|
return;
|
|
274
296
|
}
|
|
275
297
|
|
|
276
|
-
// a copy-paste from rpc.subscribe
|
|
277
298
|
try {
|
|
278
299
|
const event = ev.data;
|
|
279
300
|
|
|
@@ -281,7 +302,6 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
281
302
|
if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
|
|
282
303
|
const nevent = new NDKEvent(this._ndk, event);
|
|
283
304
|
const parsedEvent = await this.parseEvent(nevent);
|
|
284
|
-
// we're only implementing client-side rpc
|
|
285
305
|
if (!(parsedEvent as NDKRpcRequest).method) {
|
|
286
306
|
console.log('parsed response', parsedEvent);
|
|
287
307
|
this.emit(`response-${parsedEvent.id}`, parsedEvent);
|
|
@@ -292,39 +312,26 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
292
312
|
};
|
|
293
313
|
}
|
|
294
314
|
|
|
295
|
-
public
|
|
296
|
-
if (this.
|
|
297
|
-
|
|
298
|
-
this.iframePingInterval = undefined;
|
|
315
|
+
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
316
|
+
if (!this.iframePort) {
|
|
317
|
+
await this.ensureConnected();
|
|
299
318
|
}
|
|
300
|
-
}
|
|
301
319
|
|
|
302
|
-
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
303
320
|
const id = this.getId();
|
|
304
321
|
|
|
305
|
-
// create and sign request event
|
|
306
322
|
const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
|
|
307
323
|
|
|
308
|
-
// set response handler, it will dedup auth urls,
|
|
309
|
-
// and also dedup response handlers - we're sending
|
|
310
|
-
// to relays and to iframe
|
|
311
324
|
this.setResponseHandler(id, cb);
|
|
312
325
|
|
|
313
326
|
if (this.iframePort) {
|
|
314
|
-
// map request event id to request id, if iframe
|
|
315
|
-
// has no key it will reply with error:event_id (it can't
|
|
316
|
-
// decrypt the request id without keys)
|
|
317
327
|
this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
|
|
318
328
|
|
|
319
|
-
// send to iframe
|
|
320
329
|
console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
|
|
321
330
|
this.iframePort.postMessage(event.rawEvent());
|
|
322
331
|
} else {
|
|
323
|
-
// send to relays
|
|
324
332
|
await event.publish();
|
|
325
333
|
}
|
|
326
334
|
|
|
327
|
-
// see notes in 'super'
|
|
328
335
|
// @ts-ignore
|
|
329
336
|
return undefined as NDKRpcResponse;
|
|
330
337
|
}
|
|
@@ -341,14 +348,11 @@ export class ReadyListener {
|
|
|
341
348
|
this.promise = new Promise<any>(ok => {
|
|
342
349
|
console.log(new Date(), 'started listener for', this.messages);
|
|
343
350
|
|
|
344
|
-
// ready message handler
|
|
345
351
|
const onReady = async (e: MessageEvent) => {
|
|
346
352
|
const originHostname = new URL(origin!).hostname;
|
|
347
353
|
const messageHostname = new URL(e.origin).hostname;
|
|
348
|
-
// same host or subdomain
|
|
349
354
|
const validHost = messageHostname === originHostname || messageHostname.endsWith('.' + originHostname);
|
|
350
355
|
if (!validHost || !Array.isArray(e.data) || !e.data.length || !this.messages.includes(e.data[0])) {
|
|
351
|
-
// console.log(new Date(), 'got invalid ready message', e.origin, e.data);
|
|
352
356
|
return;
|
|
353
357
|
}
|
|
354
358
|
|
|
@@ -362,14 +366,7 @@ export class ReadyListener {
|
|
|
362
366
|
|
|
363
367
|
async wait(): Promise<any> {
|
|
364
368
|
console.log(new Date(), 'waiting for', this.messages);
|
|
365
|
-
|
|
366
|
-
const r = await new Promise<any>((ok, err) => {
|
|
367
|
-
// 30 sec timeout for iframe ready
|
|
368
|
-
setTimeout(() => err(new Date() + ' timeout for ' + this.messages), 30000);
|
|
369
|
-
|
|
370
|
-
this.promise.then(ok).catch(err);
|
|
371
|
-
});
|
|
372
|
-
|
|
369
|
+
const r = await this.promise;
|
|
373
370
|
console.log(new Date(), 'finished waiting for', this.messages, r);
|
|
374
371
|
return r;
|
|
375
372
|
}
|
|
@@ -382,15 +379,11 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
382
379
|
constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
|
|
383
380
|
super(ndk, signerPubkey, localSigner);
|
|
384
381
|
|
|
385
|
-
// override with our own rpc implementation
|
|
386
382
|
this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
|
|
387
|
-
this._rpc.setUseNip44(true);
|
|
383
|
+
this._rpc.setUseNip44(true);
|
|
388
384
|
this._rpc.on('authUrl', (url: string) => {
|
|
389
385
|
this.emit('authUrl', url);
|
|
390
386
|
});
|
|
391
|
-
this._rpc.on('timeout', () => {
|
|
392
|
-
this.emit('timeout');
|
|
393
|
-
});
|
|
394
387
|
|
|
395
388
|
this.rpc = this._rpc;
|
|
396
389
|
}
|
|
@@ -400,17 +393,14 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
400
393
|
}
|
|
401
394
|
|
|
402
395
|
private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
|
|
403
|
-
console.log(
|
|
396
|
+
console.log('setSignerPubkey', signerPubkey);
|
|
404
397
|
|
|
405
|
-
// ensure it's set
|
|
406
398
|
this.remotePubkey = signerPubkey;
|
|
407
399
|
|
|
408
|
-
// when we're sure it's known
|
|
409
400
|
this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
|
|
410
401
|
this.emit('iframeRestart');
|
|
411
402
|
});
|
|
412
403
|
|
|
413
|
-
// now call getPublicKey and swap remotePubkey w/ that
|
|
414
404
|
await this.initUserPubkey(sameAsUser ? signerPubkey : '');
|
|
415
405
|
}
|
|
416
406
|
|
|
@@ -425,9 +415,19 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
425
415
|
this._userPubkey = await new Promise<string>((ok, err) => {
|
|
426
416
|
if (!this.remotePubkey) throw new Error('Signer pubkey not set');
|
|
427
417
|
|
|
428
|
-
console.log(
|
|
418
|
+
console.log('get_public_key', this.remotePubkey);
|
|
419
|
+
|
|
420
|
+
const timeoutId = setTimeout(() => {
|
|
421
|
+
err(new Error('NIP46 get_public_key timeout'));
|
|
422
|
+
}, NIP46_TIMEOUT);
|
|
423
|
+
|
|
429
424
|
this._rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
|
|
430
|
-
|
|
425
|
+
clearTimeout(timeoutId);
|
|
426
|
+
if (response.error) {
|
|
427
|
+
err(new Error(response.error));
|
|
428
|
+
} else {
|
|
429
|
+
ok(response.result);
|
|
430
|
+
}
|
|
431
431
|
});
|
|
432
432
|
});
|
|
433
433
|
}
|
|
@@ -449,15 +449,21 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
|
|
452
|
-
const params = [
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
452
|
+
const params = [name, domain, '', perms];
|
|
453
|
+
|
|
454
|
+
const r = await new Promise<NDKRpcResponse>((ok, err) => {
|
|
455
|
+
const timeoutId = setTimeout(() => {
|
|
456
|
+
err(new Error('NIP46 create_account timeout'));
|
|
457
|
+
}, NIP46_TIMEOUT);
|
|
458
|
+
|
|
459
|
+
this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, response => {
|
|
460
|
+
clearTimeout(timeoutId);
|
|
461
|
+
if (response.error) {
|
|
462
|
+
err(new Error(response.error));
|
|
463
|
+
} else {
|
|
464
|
+
ok(response);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
461
467
|
});
|
|
462
468
|
|
|
463
469
|
console.log('create_account pubkey', r);
|
|
@@ -23,29 +23,9 @@ class ProcessManager extends EventEmitter {
|
|
|
23
23
|
|
|
24
24
|
public async wait<T>(cb: () => Promise<T>): Promise<T> {
|
|
25
25
|
// FIXME only allow 1 parallel req
|
|
26
|
-
console.log('ProcessManager.wait called, callTimer exists:', !!this.callTimer, 'callCount:', this.callCount);
|
|
27
|
-
|
|
28
|
-
let timeoutReject: ((reason?: any) => void) | undefined;
|
|
29
|
-
let isTimedOut = false;
|
|
30
|
-
let localTimer: NodeJS.Timeout | undefined;
|
|
31
|
-
|
|
32
|
-
const timeoutPromise = new Promise<T>((_, reject) => {
|
|
33
|
-
timeoutReject = reject;
|
|
34
|
-
});
|
|
35
26
|
|
|
36
27
|
if (!this.callTimer) {
|
|
37
|
-
|
|
38
|
-
localTimer = setTimeout(() => {
|
|
39
|
-
console.log('ProcessManager: timeout reached, emitting onCallTimeout');
|
|
40
|
-
isTimedOut = true;
|
|
41
|
-
this.emit('onCallTimeout');
|
|
42
|
-
if (timeoutReject) {
|
|
43
|
-
timeoutReject(new Error('[ProcessManager] Request timed out'));
|
|
44
|
-
}
|
|
45
|
-
}, CALL_TIMEOUT);
|
|
46
|
-
this.callTimer = localTimer;
|
|
47
|
-
} else {
|
|
48
|
-
console.log('Timer already exists, not setting up new one');
|
|
28
|
+
this.callTimer = setTimeout(() => this.emit('onCallTimeout'), CALL_TIMEOUT);
|
|
49
29
|
}
|
|
50
30
|
|
|
51
31
|
if (!this.callCount) {
|
|
@@ -56,45 +36,23 @@ class ProcessManager extends EventEmitter {
|
|
|
56
36
|
|
|
57
37
|
let error;
|
|
58
38
|
let result;
|
|
59
|
-
let raceFinished = false;
|
|
60
39
|
|
|
61
40
|
try {
|
|
62
|
-
result = await
|
|
63
|
-
cb().then(r => {
|
|
64
|
-
if (!raceFinished) {
|
|
65
|
-
raceFinished = true;
|
|
66
|
-
return r;
|
|
67
|
-
}
|
|
68
|
-
// Race already finished (timed out), ignore this result
|
|
69
|
-
console.log('ProcessManager: ignoring late result after timeout');
|
|
70
|
-
throw new Error('Already timed out');
|
|
71
|
-
}),
|
|
72
|
-
timeoutPromise.then(
|
|
73
|
-
() => {
|
|
74
|
-
raceFinished = true;
|
|
75
|
-
throw new Error('Should not resolve');
|
|
76
|
-
},
|
|
77
|
-
(err) => {
|
|
78
|
-
raceFinished = true;
|
|
79
|
-
throw err;
|
|
80
|
-
}
|
|
81
|
-
)
|
|
82
|
-
]);
|
|
41
|
+
result = await cb();
|
|
83
42
|
} catch (e) {
|
|
84
43
|
error = e;
|
|
85
44
|
}
|
|
86
45
|
|
|
87
46
|
this.callCount--;
|
|
88
47
|
|
|
89
|
-
// Always emit onCallEnd for cleanup
|
|
90
48
|
this.emit('onCallEnd');
|
|
91
49
|
|
|
92
|
-
|
|
93
|
-
if (this.callTimer === localTimer) {
|
|
50
|
+
if (this.callTimer) {
|
|
94
51
|
clearTimeout(this.callTimer);
|
|
95
|
-
this.callTimer = undefined;
|
|
96
52
|
}
|
|
97
53
|
|
|
54
|
+
this.callTimer = undefined;
|
|
55
|
+
|
|
98
56
|
if (error) {
|
|
99
57
|
throw error;
|
|
100
58
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Info, AuthMethod, ConnectionString, RecentType, BannerNotify } from 'nostr-login-components';
|
|
1
|
+
import { Info, AuthMethod, ConnectionString, RecentType, BannerNotify } from 'nostr-login-components/dist/types/types';
|
|
2
2
|
|
|
3
3
|
export interface NostrLoginAuthOptions {
|
|
4
4
|
localNsec?: string;
|
|
@@ -10,7 +10,6 @@ export interface NostrLoginAuthOptions {
|
|
|
10
10
|
name?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// NOTE: must be a subset of CURRENT_MODULE enum
|
|
14
13
|
export type StartScreens =
|
|
15
14
|
| 'welcome'
|
|
16
15
|
| 'welcome-login'
|
|
@@ -27,56 +26,27 @@ export type StartScreens =
|
|
|
27
26
|
| 'import';
|
|
28
27
|
|
|
29
28
|
export interface NostrLoginOptions {
|
|
30
|
-
// optional
|
|
31
29
|
theme?: string;
|
|
32
30
|
startScreen?: StartScreens;
|
|
33
31
|
bunkers?: string;
|
|
34
32
|
onAuth?: (npub: string, options: NostrLoginAuthOptions) => void;
|
|
35
33
|
perms?: string;
|
|
36
34
|
darkMode?: boolean;
|
|
37
|
-
|
|
38
|
-
// do not show the banner, modals must be `launch`-ed
|
|
39
35
|
noBanner?: boolean;
|
|
40
|
-
|
|
41
|
-
// forward reqs to this bunker origin for testing
|
|
42
36
|
devOverrideBunkerOrigin?: string;
|
|
43
|
-
|
|
44
|
-
// deprecated, use methods=['local']
|
|
45
|
-
// use local signup instead of nostr connect
|
|
46
37
|
localSignup?: boolean;
|
|
47
|
-
|
|
48
|
-
// allowed auth methods
|
|
49
38
|
methods?: AuthMethod[];
|
|
50
|
-
|
|
51
|
-
// otp endpoints
|
|
52
39
|
otpRequestUrl?: string;
|
|
53
40
|
otpReplyUrl?: string;
|
|
54
|
-
|
|
55
|
-
// welcome screen's title/desc
|
|
56
41
|
title?: string;
|
|
57
42
|
description?: string;
|
|
58
|
-
|
|
59
|
-
// comma-separated list of relays added
|
|
60
|
-
// to relay list of new profiles created with local signup
|
|
61
43
|
signupRelays?: string;
|
|
62
|
-
|
|
63
|
-
// relay list to override hardcoded `OUTBOX_RELAYS` constant
|
|
64
44
|
outboxRelays?: string[];
|
|
65
|
-
|
|
66
|
-
// dev mode
|
|
67
45
|
dev?: boolean;
|
|
68
|
-
|
|
69
|
-
// use start.njump.me instead of local signup
|
|
70
46
|
signupNstart?: boolean;
|
|
71
|
-
|
|
72
|
-
// list of npubs to auto/suggest-follow on signup
|
|
73
47
|
followNpubs?: string;
|
|
74
|
-
|
|
75
|
-
// when method call auth needed, instead of showing
|
|
76
|
-
// the modal, we start waiting for incoming nip46
|
|
77
|
-
// connection and send the nostrconnect string using
|
|
78
|
-
// nlNeedAuth event
|
|
79
48
|
customNostrConnect?: boolean;
|
|
49
|
+
connectRelays?: string[];
|
|
80
50
|
}
|
|
81
51
|
|
|
82
52
|
export interface IBanner {
|
package/src/utils/index.ts
CHANGED
|
@@ -115,36 +115,22 @@ export const getBunkerUrl = async (value: string, optionsModal: NostrLoginOption
|
|
|
115
115
|
const origin = optionsModal.devOverrideBunkerOrigin || `https://${domain}`;
|
|
116
116
|
const bunkerUrl = `${origin}/.well-known/nostr.json?name=_`;
|
|
117
117
|
const userUrl = `${origin}/.well-known/nostr.json?name=${name}`;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// console.log({
|
|
132
|
-
// bunkerData, userData, bunkerPubkey, bunkerRelays, userPubkey,
|
|
133
|
-
// name, domain, origin
|
|
134
|
-
// })
|
|
135
|
-
if (!bunkerRelays.length) {
|
|
136
|
-
throw new Error('Bunker relay not provided');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return `bunker://${userPubkey}?relay=${bunkerRelays[0]}`;
|
|
140
|
-
} catch (e: any) {
|
|
141
|
-
if (e.name === 'AbortError') {
|
|
142
|
-
throw new Error('[getBunkerUrl] Request timed out');
|
|
143
|
-
}
|
|
144
|
-
throw e;
|
|
145
|
-
} finally {
|
|
146
|
-
clearTimeout(timeoutId);
|
|
118
|
+
const bunker = await fetch(bunkerUrl);
|
|
119
|
+
const bunkerData = await bunker.json();
|
|
120
|
+
const bunkerPubkey = bunkerData.names['_'];
|
|
121
|
+
const bunkerRelays = bunkerData.nip46[bunkerPubkey];
|
|
122
|
+
const user = await fetch(userUrl);
|
|
123
|
+
const userData = await user.json();
|
|
124
|
+
const userPubkey = userData.names[name];
|
|
125
|
+
// console.log({
|
|
126
|
+
// bunkerData, userData, bunkerPubkey, bunkerRelays, userPubkey,
|
|
127
|
+
// name, domain, origin
|
|
128
|
+
// })
|
|
129
|
+
if (!bunkerRelays.length) {
|
|
130
|
+
throw new Error('Bunker relay not provided');
|
|
147
131
|
}
|
|
132
|
+
|
|
133
|
+
return `bunker://${userPubkey}?relay=${bunkerRelays[0]}`;
|
|
148
134
|
}
|
|
149
135
|
|
|
150
136
|
throw new Error('Invalid user name or bunker url');
|