@konemono/nostr-login 1.11.1 → 1.11.4
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/dist/index.esm.js +12 -23
- package/dist/index.esm.js.map +1 -1
- package/dist/modules/Nip46.d.ts +16 -25
- package/dist/unpkg.js +12 -23
- package/package.json +2 -1
- package/src/modules/AuthNostrService.ts +43 -24
- package/src/modules/ModalManager.ts +2 -3
- package/src/modules/Nip46.ts +121 -227
- package/src/modules/Signer.ts +1 -2
- package/src/utils/index.ts +4 -5
- package/src/utils/nip44.ts +1 -2
package/src/modules/Nip46.ts
CHANGED
|
@@ -1,104 +1,26 @@
|
|
|
1
|
-
import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
2
|
-
import { validateEvent } from 'nostr-tools';
|
|
1
|
+
import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
2
|
+
import { validateEvent, verifySignature } from 'nostr-tools';
|
|
3
3
|
import { PrivateKeySigner } from './Signer';
|
|
4
4
|
import { NIP46_REQUEST_TIMEOUT, NIP46_CONNECT_TIMEOUT } from '../const';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
5
6
|
|
|
6
7
|
// タイムアウト付きPromiseラッパー
|
|
7
8
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T> {
|
|
8
9
|
return Promise.race([promise, new Promise<T>((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs))]);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
-
* NDKのリレー接続を待機する
|
|
13
|
-
*/
|
|
14
|
-
export function waitForConnection(ndk: NDK, timeout: number): Promise<void> {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
const start = Date.now();
|
|
17
|
-
|
|
18
|
-
const check = () => {
|
|
19
|
-
// 接続済みリレーがあるか確認
|
|
20
|
-
const connected = Array.from(ndk.pool.relays.values()).some(relay => relay.status === 1); // 1 = CONNECTED
|
|
21
|
-
|
|
22
|
-
if (connected) {
|
|
23
|
-
resolve();
|
|
24
|
-
} else if (Date.now() - start > timeout) {
|
|
25
|
-
reject(new Error(`接続タイムアウト: ${timeout}ms`));
|
|
26
|
-
} else {
|
|
27
|
-
setTimeout(check, 100);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
check();
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* NDKの接続を強制的に確立する
|
|
37
|
-
*/
|
|
38
|
-
export async function ensureNDKConnection(ndk: NDK, timeout: number): Promise<void> {
|
|
39
|
-
// connect()は冪等なので安全
|
|
40
|
-
await ndk.connect();
|
|
41
|
-
await waitForConnection(ndk, timeout);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* NIP-46専用リレーも含めて接続を確立する
|
|
46
|
-
*/
|
|
47
|
-
export async function ensureNIP46Connection(ndk: NDK, timeout: number): Promise<void> {
|
|
48
|
-
try {
|
|
49
|
-
await ensureNDKConnection(ndk, timeout);
|
|
50
|
-
} catch (e) {
|
|
51
|
-
console.warn('Initial connection attempt failed, retrying with force disconnect...', e);
|
|
52
|
-
// 接続数0なら明示的に全切断して再試行
|
|
53
|
-
if (Array.from(ndk.pool.relays.values()).filter(r => r.status === 1).length === 0) {
|
|
54
|
-
ndk.pool.relays.forEach(relay => {
|
|
55
|
-
try {
|
|
56
|
-
relay.disconnect();
|
|
57
|
-
} catch (err) {
|
|
58
|
-
console.error('Error disconnecting relay:', err);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
// タイムアウトを延長して再試行
|
|
62
|
-
await ensureNDKConnection(ndk, timeout * 2);
|
|
63
|
-
} else {
|
|
64
|
-
throw e;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// NIP-46用リレーが個別に定義されている場合の接続確認
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
const signerRelays = ndk.signer?.relayUrls;
|
|
71
|
-
if (signerRelays && Array.isArray(signerRelays)) {
|
|
72
|
-
for (const url of signerRelays) {
|
|
73
|
-
const relay = ndk.pool.getRelay(url);
|
|
74
|
-
if (relay && relay.status !== 1) {
|
|
75
|
-
await relay.connect();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 最終チェック
|
|
81
|
-
const connectedRelays = Array.from(ndk.pool.relays.values()).filter(r => r.status === 1);
|
|
82
|
-
if (connectedRelays.length === 0) {
|
|
83
|
-
throw new Error('リレー接続に失敗しました');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
12
|
class NostrRpc extends NDKNostrRpc {
|
|
88
13
|
protected _ndk: NDK;
|
|
89
14
|
protected _signer: PrivateKeySigner;
|
|
90
15
|
protected requests: Set<string> = new Set();
|
|
91
16
|
private sub?: NDKSubscription;
|
|
92
17
|
protected _useNip44: boolean = false;
|
|
93
|
-
private
|
|
94
|
-
private maxReconnectAttempts: number = 3;
|
|
95
|
-
private reconnectDelay: number = 2000;
|
|
18
|
+
private eventEmitter: EventEmitter = new EventEmitter();
|
|
96
19
|
|
|
97
20
|
public constructor(ndk: NDK, signer: PrivateKeySigner) {
|
|
98
21
|
super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
|
|
99
22
|
this._ndk = ndk;
|
|
100
23
|
this._signer = signer;
|
|
101
|
-
this.setupConnectionMonitoring();
|
|
102
24
|
}
|
|
103
25
|
|
|
104
26
|
public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
|
|
@@ -213,97 +135,22 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
213
135
|
return withTimeout(this.connect(pubkey, token, perms), timeoutMs, `Connection timeout after ${timeoutMs}ms`);
|
|
214
136
|
}
|
|
215
137
|
|
|
216
|
-
//
|
|
217
|
-
public async
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return withTimeout(
|
|
225
|
-
new Promise<NDKRpcResponse>((resolve, reject) => {
|
|
226
|
-
this.sendRequest(remotePubkey, method, params, kind, response => {
|
|
227
|
-
if (response.error) {
|
|
228
|
-
reject(new Error(response.error));
|
|
229
|
-
} else {
|
|
230
|
-
resolve(response);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
}),
|
|
234
|
-
timeoutMs,
|
|
235
|
-
`Request timeout after ${timeoutMs}ms for method: ${method}`,
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// 接続監視のセットアップ
|
|
240
|
-
private setupConnectionMonitoring() {
|
|
241
|
-
// アプリがフォアグラウンドに戻ったときの処理
|
|
242
|
-
if (typeof document !== 'undefined') {
|
|
243
|
-
document.addEventListener('visibilitychange', async () => {
|
|
244
|
-
if (document.visibilityState === 'visible') {
|
|
245
|
-
console.log('App visible, checking relay connections...');
|
|
246
|
-
await this.ensureConnected();
|
|
138
|
+
// ping実装
|
|
139
|
+
public async ping(remotePubkey: string): Promise<void> {
|
|
140
|
+
return new Promise<void>((ok, err) => {
|
|
141
|
+
this.sendRequest(remotePubkey, 'ping', [], 24133, (response: NDKRpcResponse) => {
|
|
142
|
+
if (response.result === 'pong') {
|
|
143
|
+
ok();
|
|
144
|
+
} else {
|
|
145
|
+
err(new Error(response.error || 'ping failed'));
|
|
247
146
|
}
|
|
248
147
|
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// オンライン/オフライン検知
|
|
252
|
-
if (typeof window !== 'undefined') {
|
|
253
|
-
window.addEventListener('online', async () => {
|
|
254
|
-
console.log('Network online, reconnecting relays...');
|
|
255
|
-
await this.reconnect();
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// 接続を確認して必要なら再接続
|
|
261
|
-
private async ensureConnected(): Promise<void> {
|
|
262
|
-
const connectedRelays = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1); // 1 = CONNECTED
|
|
263
|
-
|
|
264
|
-
if (connectedRelays.length === 0) {
|
|
265
|
-
console.log('No connected relays, attempting reconnection...');
|
|
266
|
-
await this.reconnect();
|
|
267
|
-
}
|
|
148
|
+
});
|
|
268
149
|
}
|
|
269
150
|
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
console.error('Max reconnection attempts reached');
|
|
274
|
-
this.emit('reconnectFailed');
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
this.reconnectAttempts++;
|
|
279
|
-
console.log(`Reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
// サブスクリプションを停止
|
|
283
|
-
if (this.sub) {
|
|
284
|
-
this.sub.stop();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 少し待ってから再接続
|
|
288
|
-
await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
|
|
289
|
-
|
|
290
|
-
// 再接続
|
|
291
|
-
await this._ndk.connect();
|
|
292
|
-
|
|
293
|
-
// サブスクリプションを再開
|
|
294
|
-
if (this.sub) {
|
|
295
|
-
await this.sub.start();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
this.reconnectAttempts = 0;
|
|
299
|
-
this.emit('reconnected');
|
|
300
|
-
console.log('Successfully reconnected to relays');
|
|
301
|
-
} catch (e) {
|
|
302
|
-
console.error('Reconnection failed:', e);
|
|
303
|
-
|
|
304
|
-
// リトライ
|
|
305
|
-
setTimeout(() => this.reconnect(), this.reconnectDelay * this.reconnectAttempts);
|
|
306
|
-
}
|
|
151
|
+
// タイムアウト対応のping
|
|
152
|
+
public async pingWithTimeout(remotePubkey: string, timeoutMs: number = 10000): Promise<void> {
|
|
153
|
+
return withTimeout(this.ping(remotePubkey), timeoutMs, `Ping timeout after ${timeoutMs}ms`);
|
|
307
154
|
}
|
|
308
155
|
|
|
309
156
|
protected getId(): string {
|
|
@@ -311,9 +158,6 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
311
158
|
}
|
|
312
159
|
|
|
313
160
|
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
314
|
-
// リクエスト送信前に接続を確認
|
|
315
|
-
await ensureNIP46Connection(this._ndk, 5000);
|
|
316
|
-
|
|
317
161
|
const id = this.getId();
|
|
318
162
|
|
|
319
163
|
// response handler will deduplicate auth urls and responses
|
|
@@ -341,10 +185,10 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
341
185
|
return new Promise<NDKRpcResponse>(() => {
|
|
342
186
|
const responseHandler = (response: NDKRpcResponse) => {
|
|
343
187
|
if (response.result === 'auth_url') {
|
|
344
|
-
this.once(`response-${id}`, responseHandler);
|
|
188
|
+
this.eventEmitter.once(`response-${id}`, responseHandler);
|
|
345
189
|
if (!authUrlSent) {
|
|
346
190
|
authUrlSent = true;
|
|
347
|
-
this.emit('authUrl', response.error);
|
|
191
|
+
this.eventEmitter.emit('authUrl', response.error);
|
|
348
192
|
}
|
|
349
193
|
} else if (cb) {
|
|
350
194
|
if (this.requests.has(id)) {
|
|
@@ -355,7 +199,7 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
355
199
|
}
|
|
356
200
|
};
|
|
357
201
|
|
|
358
|
-
this.once(`response-${id}`, responseHandler);
|
|
202
|
+
this.eventEmitter.once(`response-${id}`, responseHandler);
|
|
359
203
|
});
|
|
360
204
|
}
|
|
361
205
|
|
|
@@ -379,15 +223,28 @@ class NostrRpc extends NDKNostrRpc {
|
|
|
379
223
|
|
|
380
224
|
return event;
|
|
381
225
|
}
|
|
226
|
+
|
|
227
|
+
// EventEmitter互換メソッド
|
|
228
|
+
public override on = <EventKey extends string | symbol = string>(
|
|
229
|
+
event: EventKey,
|
|
230
|
+
listener: (...args: any[]) => void
|
|
231
|
+
): this => {
|
|
232
|
+
this.eventEmitter.on(event as string, listener);
|
|
233
|
+
return this;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
public override emit = <EventKey extends string | symbol = string>(
|
|
237
|
+
event: EventKey,
|
|
238
|
+
...args: any[]
|
|
239
|
+
): boolean => {
|
|
240
|
+
return this.eventEmitter.emit(event as string, ...args);
|
|
241
|
+
}
|
|
382
242
|
}
|
|
383
243
|
|
|
384
244
|
export class IframeNostrRpc extends NostrRpc {
|
|
385
245
|
private peerOrigin?: string;
|
|
386
246
|
private iframePort?: MessagePort;
|
|
387
247
|
private iframeRequests = new Map<string, { id: string; pubkey: string }>();
|
|
388
|
-
private heartbeatInterval?: number;
|
|
389
|
-
private lastResponseTime: number = Date.now();
|
|
390
|
-
private heartbeatTimeoutMs: number = 30000; // 30秒応答がなければ再接続
|
|
391
248
|
|
|
392
249
|
public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
|
|
393
250
|
super(ndk, localSigner);
|
|
@@ -408,32 +265,10 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
408
265
|
);
|
|
409
266
|
}
|
|
410
267
|
|
|
411
|
-
// ハートビート開始
|
|
412
|
-
private startHeartbeat() {
|
|
413
|
-
this.stopHeartbeat();
|
|
414
|
-
|
|
415
|
-
this.heartbeatInterval = window.setInterval(async () => {
|
|
416
|
-
const timeSinceLastResponse = Date.now() - this.lastResponseTime;
|
|
417
|
-
|
|
418
|
-
if (timeSinceLastResponse > this.heartbeatTimeoutMs) {
|
|
419
|
-
console.warn('No response from relay for too long, reconnecting...');
|
|
420
|
-
await this.reconnect();
|
|
421
|
-
}
|
|
422
|
-
}, 10000); // 10秒ごとにチェック
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
private stopHeartbeat() {
|
|
426
|
-
if (this.heartbeatInterval) {
|
|
427
|
-
clearInterval(this.heartbeatInterval);
|
|
428
|
-
this.heartbeatInterval = undefined;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
268
|
public setWorkerIframePort(port: MessagePort) {
|
|
433
269
|
if (!this.peerOrigin) throw new Error('Unexpected iframe port');
|
|
434
270
|
|
|
435
271
|
this.iframePort = port;
|
|
436
|
-
this.startHeartbeat();
|
|
437
272
|
|
|
438
273
|
// to make sure Chrome doesn't terminate the channel
|
|
439
274
|
setInterval(() => {
|
|
@@ -455,10 +290,9 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
455
290
|
const event = ev.data;
|
|
456
291
|
|
|
457
292
|
if (!validateEvent(event)) throw new Error('Invalid event from iframe');
|
|
293
|
+
if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
|
|
458
294
|
const nevent = new NDKEvent(this._ndk, event);
|
|
459
295
|
const parsedEvent = await this.parseEvent(nevent);
|
|
460
|
-
// レスポンス受信時にタイムスタンプを更新
|
|
461
|
-
this.lastResponseTime = Date.now();
|
|
462
296
|
// we're only implementing client-side rpc
|
|
463
297
|
if (!(parsedEvent as NDKRpcRequest).method) {
|
|
464
298
|
console.log('parsed response', parsedEvent);
|
|
@@ -471,11 +305,6 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
471
305
|
}
|
|
472
306
|
|
|
473
307
|
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
474
|
-
// リクエスト送信前に接続を確認 (relayを使用する場合に備えて)
|
|
475
|
-
if (!this.iframePort) {
|
|
476
|
-
await ensureNIP46Connection(this._ndk, 5000);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
308
|
const id = this.getId();
|
|
480
309
|
|
|
481
310
|
// create and sign request event
|
|
@@ -539,17 +368,6 @@ export class ReadyListener {
|
|
|
539
368
|
async wait(): Promise<any> {
|
|
540
369
|
console.log(new Date(), 'waiting for', this.messages);
|
|
541
370
|
const r = await this.promise;
|
|
542
|
-
// NOTE: timer here doesn't help bcs it must be activated when
|
|
543
|
-
// user "confirms", but that's happening on a different
|
|
544
|
-
// origin and we can't really know.
|
|
545
|
-
// await new Promise<any>((ok, err) => {
|
|
546
|
-
// // 10 sec should be more than enough
|
|
547
|
-
// setTimeout(() => err(new Date() + ' timeout for ' + this.message), 10000);
|
|
548
|
-
|
|
549
|
-
// // if promise already resolved or will resolve in the future
|
|
550
|
-
// this.promise.then(ok);
|
|
551
|
-
// });
|
|
552
|
-
|
|
553
371
|
console.log(new Date(), 'finished waiting for', this.messages, r);
|
|
554
372
|
return r;
|
|
555
373
|
}
|
|
@@ -558,29 +376,72 @@ export class ReadyListener {
|
|
|
558
376
|
export class Nip46Signer extends NDKNip46Signer {
|
|
559
377
|
private _userPubkey: string = '';
|
|
560
378
|
private _rpc: IframeNostrRpc;
|
|
379
|
+
private lastPingTime: number = 0;
|
|
380
|
+
private pingCacheDuration: number = 30000; // 30秒
|
|
381
|
+
private _remotePubkey?: string;
|
|
561
382
|
|
|
562
383
|
constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
|
|
563
384
|
super(ndk, signerPubkey, localSigner);
|
|
564
385
|
|
|
565
386
|
// override with our own rpc implementation
|
|
566
387
|
this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
|
|
567
|
-
this._rpc.setUseNip44(true);
|
|
388
|
+
this._rpc.setUseNip44(true);
|
|
568
389
|
this._rpc.on('authUrl', (url: string) => {
|
|
569
390
|
this.emit('authUrl', url);
|
|
570
391
|
});
|
|
571
392
|
|
|
572
393
|
this.rpc = this._rpc;
|
|
394
|
+
this._remotePubkey = signerPubkey;
|
|
573
395
|
}
|
|
574
396
|
|
|
575
397
|
get userPubkey() {
|
|
576
398
|
return this._userPubkey;
|
|
577
399
|
}
|
|
578
400
|
|
|
401
|
+
// Use a different name to avoid conflict with base class property
|
|
402
|
+
get remotePubkeyAccessor() {
|
|
403
|
+
return this._remotePubkey;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
set remotePubkeyAccessor(value: string | undefined) {
|
|
407
|
+
this._remotePubkey = value;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 接続確認(必要時のみping)
|
|
411
|
+
private async ensureConnection(retries: number = 2): Promise<void> {
|
|
412
|
+
if (!this._remotePubkey) return;
|
|
413
|
+
|
|
414
|
+
const now = Date.now();
|
|
415
|
+
|
|
416
|
+
// 最近ping成功していればスキップ
|
|
417
|
+
if (now - this.lastPingTime < this.pingCacheDuration) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
for (let i = 0; i <= retries; i++) {
|
|
422
|
+
try {
|
|
423
|
+
await this._rpc.pingWithTimeout(this._remotePubkey, 10000);
|
|
424
|
+
this.lastPingTime = now;
|
|
425
|
+
console.log('Connection check OK');
|
|
426
|
+
return;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (i === retries) {
|
|
429
|
+
console.error('Connection check failed after retries', error);
|
|
430
|
+
throw new Error('NIP-46 connection lost');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const delay = Math.min(1000 * Math.pow(2, i), 5000);
|
|
434
|
+
console.log(`Ping failed, retrying in ${delay}ms...`);
|
|
435
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
579
440
|
private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
|
|
580
441
|
console.log('setSignerPubkey', signerPubkey);
|
|
581
442
|
|
|
582
443
|
// ensure it's set
|
|
583
|
-
this.
|
|
444
|
+
this._remotePubkey = signerPubkey;
|
|
584
445
|
|
|
585
446
|
// when we're sure it's known
|
|
586
447
|
this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
|
|
@@ -601,10 +462,10 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
601
462
|
|
|
602
463
|
this._userPubkey = await withTimeout(
|
|
603
464
|
new Promise<string>((ok, err) => {
|
|
604
|
-
if (!this.
|
|
465
|
+
if (!this._remotePubkey) throw new Error('Signer pubkey not set');
|
|
605
466
|
|
|
606
|
-
console.log('get_public_key', this.
|
|
607
|
-
this._rpc.sendRequest(this.
|
|
467
|
+
console.log('get_public_key', this._remotePubkey);
|
|
468
|
+
this._rpc.sendRequest(this._remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
|
|
608
469
|
if (response.error) {
|
|
609
470
|
err(new Error(response.error));
|
|
610
471
|
} else {
|
|
@@ -620,17 +481,42 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
620
481
|
public async listen(nostrConnectSecret: string) {
|
|
621
482
|
const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
|
|
622
483
|
await this.setSignerPubkey(signerPubkey);
|
|
484
|
+
|
|
485
|
+
// ログイン完了後に接続確認
|
|
486
|
+
await this.ensureConnection();
|
|
623
487
|
}
|
|
624
488
|
|
|
625
489
|
public async connect(token?: string, perms?: string) {
|
|
626
|
-
if (!this.
|
|
627
|
-
await
|
|
628
|
-
await this.setSignerPubkey(this.
|
|
490
|
+
if (!this._remotePubkey) throw new Error('No signer pubkey');
|
|
491
|
+
await this._rpc.connectWithTimeout(this._remotePubkey, token, perms, NIP46_CONNECT_TIMEOUT);
|
|
492
|
+
await this.setSignerPubkey(this._remotePubkey);
|
|
493
|
+
|
|
494
|
+
// ログイン完了後に接続確認
|
|
495
|
+
await this.ensureConnection();
|
|
629
496
|
}
|
|
630
497
|
|
|
631
498
|
public async setListenReply(reply: any, nostrConnectSecret: string) {
|
|
632
499
|
const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
|
|
633
500
|
await this.setSignerPubkey(signerPubkey, true);
|
|
501
|
+
|
|
502
|
+
// ログイン完了後に接続確認
|
|
503
|
+
await this.ensureConnection();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// 署名メソッドのオーバーライド - 署名前に接続確認
|
|
507
|
+
async sign(event: NostrEvent): Promise<string> {
|
|
508
|
+
await this.ensureConnection();
|
|
509
|
+
return super.sign(event);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async encrypt(recipient: NDKUser, value: string): Promise<string> {
|
|
513
|
+
await this.ensureConnection();
|
|
514
|
+
return super.encrypt(recipient, value);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async decrypt(sender: NDKUser, value: string): Promise<string> {
|
|
518
|
+
await this.ensureConnection();
|
|
519
|
+
return super.decrypt(sender, value);
|
|
634
520
|
}
|
|
635
521
|
|
|
636
522
|
public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
|
|
@@ -652,4 +538,12 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
652
538
|
|
|
653
539
|
return r.result;
|
|
654
540
|
}
|
|
655
|
-
|
|
541
|
+
|
|
542
|
+
// EventEmitter互換メソッド
|
|
543
|
+
public override emit = <EventKey extends string | symbol = string>(
|
|
544
|
+
event: EventKey,
|
|
545
|
+
...args: any[]
|
|
546
|
+
): boolean => {
|
|
547
|
+
return this._rpc.emit(event as string, ...args);
|
|
548
|
+
}
|
|
549
|
+
}
|
package/src/modules/Signer.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk';
|
|
2
2
|
import { Nip44 } from '../utils/nip44';
|
|
3
3
|
import { getPublicKey } from 'nostr-tools';
|
|
4
|
-
import { hexToBytes } from 'nostr-tools/lib/types/utils';
|
|
5
4
|
|
|
6
5
|
export class PrivateKeySigner extends NDKPrivateKeySigner {
|
|
7
6
|
private nip44: Nip44 = new Nip44();
|
|
@@ -9,7 +8,7 @@ export class PrivateKeySigner extends NDKPrivateKeySigner {
|
|
|
9
8
|
|
|
10
9
|
constructor(privateKey: string) {
|
|
11
10
|
super(privateKey);
|
|
12
|
-
this._pubkey = getPublicKey(
|
|
11
|
+
this._pubkey = getPublicKey(privateKey);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
get pubkey() {
|
package/src/utils/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Info, RecentType } from 'nostr-login-components/dist/types/types';
|
|
2
2
|
import NDK, { NDKEvent, NDKRelaySet, NDKSigner, NDKUser } from '@nostr-dev-kit/ndk';
|
|
3
|
-
import {
|
|
3
|
+
import { generatePrivateKey } from 'nostr-tools';
|
|
4
4
|
import { NostrLoginOptions } from '../types';
|
|
5
|
-
import { bytesToHex } from 'nostr-tools/lib/types/utils';
|
|
6
5
|
|
|
7
6
|
const LOCAL_STORE_KEY = '__nostrlogin_nip46';
|
|
8
7
|
const LOGGED_IN_ACCOUNTS = '__nostrlogin_accounts';
|
|
@@ -20,7 +19,7 @@ export const localStorageGetItem = (key: string) => {
|
|
|
20
19
|
if (value) {
|
|
21
20
|
try {
|
|
22
21
|
return JSON.parse(value);
|
|
23
|
-
} catch {
|
|
22
|
+
} catch {}
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
return null;
|
|
@@ -93,7 +92,7 @@ export const bunkerUrlToInfo = (bunkerUrl: string, sk = ''): Info => {
|
|
|
93
92
|
return {
|
|
94
93
|
pubkey: '',
|
|
95
94
|
signerPubkey: url.hostname || url.pathname.split('//')[1],
|
|
96
|
-
sk: sk ||
|
|
95
|
+
sk: sk || generatePrivateKey(),
|
|
97
96
|
relays: url.searchParams.getAll('relay'),
|
|
98
97
|
token: url.searchParams.get('secret') || '',
|
|
99
98
|
authMethod: 'connect',
|
|
@@ -166,7 +165,7 @@ export const checkNip05 = async (nip05: string) => {
|
|
|
166
165
|
pubkey = d.names[name];
|
|
167
166
|
return;
|
|
168
167
|
}
|
|
169
|
-
} catch {
|
|
168
|
+
} catch {}
|
|
170
169
|
|
|
171
170
|
available = true;
|
|
172
171
|
})();
|
package/src/utils/nip44.ts
CHANGED
|
@@ -10,7 +10,6 @@ import { sha256 } from "@noble/hashes/sha256"
|
|
|
10
10
|
import { hmac } from "@noble/hashes/hmac";
|
|
11
11
|
import { base64 } from "@scure/base";
|
|
12
12
|
import { getPublicKey } from 'nostr-tools'
|
|
13
|
-
import { hexToBytes } from "nostr-tools/lib/types/utils";
|
|
14
13
|
|
|
15
14
|
// from https://github.com/nbd-wtf/nostr-tools
|
|
16
15
|
|
|
@@ -165,7 +164,7 @@ export class Nip44 {
|
|
|
165
164
|
}
|
|
166
165
|
|
|
167
166
|
private getKey(privkey: string, pubkey: string, extractable?: boolean) {
|
|
168
|
-
const id = getPublicKey(
|
|
167
|
+
const id = getPublicKey(privkey) + pubkey
|
|
169
168
|
let cryptoKey = this.cache.get(id)
|
|
170
169
|
if (cryptoKey) return cryptoKey
|
|
171
170
|
|