@konemono/nostr-login 1.11.0 → 1.11.1
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 +25 -19
- package/dist/index.esm.js.map +1 -1
- package/dist/modules/AuthNostrService.d.ts +4 -14
- package/dist/modules/Nip46.d.ts +23 -52
- package/dist/modules/Signer.d.ts +6 -12
- package/dist/unpkg.js +25 -19
- package/dist/utils/index.d.ts +3 -6
- package/dist/utils/nip44.d.ts +3 -3
- package/package.json +8 -8
- package/src/modules/AuthNostrService.ts +103 -223
- package/src/modules/ModalManager.ts +2 -1
- package/src/modules/Nip46.ts +171 -270
- package/src/modules/NostrExtensionService.ts +0 -2
- package/src/modules/Signer.ts +13 -35
- package/src/utils/index.ts +22 -71
- package/src/utils/nip44.ts +8 -12
- package/test-relay-management.html +0 -407
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { Info, RecentType } from 'nostr-login-components/dist/types/types';
|
|
2
|
-
import {
|
|
2
|
+
import NDK, { NDKSigner } from '@nostr-dev-kit/ndk';
|
|
3
3
|
import { NostrLoginOptions } from '../types';
|
|
4
|
-
import { PrivateKeySigner } from '../modules/Signer';
|
|
5
|
-
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
6
|
-
export declare function hexToBytes(hex: string): Uint8Array;
|
|
7
4
|
export declare const localStorageSetItem: (key: string, value: string) => void;
|
|
8
5
|
export declare const localStorageGetItem: (key: string) => any;
|
|
9
6
|
export declare const localStorageRemoveItem: (key: string) => void;
|
|
10
|
-
export declare const fetchProfile: (info: Info,
|
|
7
|
+
export declare const fetchProfile: (info: Info, profileNdk: NDK) => Promise<import("@nostr-dev-kit/ndk").NDKUserProfile | null>;
|
|
11
8
|
export declare const prepareSignupRelays: (signupRelays?: string) => string[];
|
|
12
|
-
export declare const createProfile: (info: Info,
|
|
9
|
+
export declare const createProfile: (info: Info, profileNdk: NDK, signer: NDKSigner, signupRelays?: string, outboxRelays?: string[]) => Promise<void>;
|
|
13
10
|
export declare const bunkerUrlToInfo: (bunkerUrl: string, sk?: string) => Info;
|
|
14
11
|
export declare const isBunkerUrl: (value: string) => boolean;
|
|
15
12
|
export declare const getBunkerUrl: (value: string, optionsModal: NostrLoginOptions) => Promise<string>;
|
package/dist/utils/nip44.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export declare function encryptNip44(plaintext: string, conversationKey: Uint8Ar
|
|
|
2
2
|
export declare function decryptNip44(payload: string, conversationKey: Uint8Array): string;
|
|
3
3
|
export declare class Nip44 {
|
|
4
4
|
private cache;
|
|
5
|
-
createKey(privkey: string
|
|
5
|
+
createKey(privkey: string, pubkey: string): Uint8Array;
|
|
6
6
|
private getKey;
|
|
7
|
-
encrypt(privkey: string
|
|
8
|
-
decrypt(privkey: string
|
|
7
|
+
encrypt(privkey: string, pubkey: string, text: string): string;
|
|
8
|
+
decrypt(privkey: string, pubkey: string, data: string): string;
|
|
9
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konemono/nostr-login",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.esm.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
},
|
|
12
12
|
"author": "a-fralou",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"nostr-
|
|
15
|
-
"
|
|
16
|
-
"tseep": "^1.
|
|
14
|
+
"@nostr-dev-kit/ndk": "^2.3.1",
|
|
15
|
+
"nostr-tools": "^1.17.0",
|
|
16
|
+
"tseep": "^1.2.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@rollup/plugin-commonjs": "^
|
|
20
|
-
"@rollup/plugin-node-resolve": "^
|
|
19
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
20
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
21
21
|
"@rollup/plugin-terser": "^0.4.4",
|
|
22
22
|
"nostr-login-components": "^1.0.3",
|
|
23
|
-
"prettier": "^3.
|
|
24
|
-
"rollup": "^4.
|
|
23
|
+
"prettier": "^3.2.2",
|
|
24
|
+
"rollup": "^4.9.6",
|
|
25
25
|
"rollup-plugin-typescript2": "^0.36.0"
|
|
26
26
|
},
|
|
27
27
|
"license": "MIT"
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
import { localStorageAddAccount, bunkerUrlToInfo, isBunkerUrl, fetchProfile, getBunkerUrl, localStorageRemoveCurrentAccount, createProfile, getIcon
|
|
1
|
+
import { localStorageAddAccount, bunkerUrlToInfo, isBunkerUrl, fetchProfile, getBunkerUrl, localStorageRemoveCurrentAccount, createProfile, getIcon } from '../utils';
|
|
2
2
|
import { ConnectionString, Info } from 'nostr-login-components/dist/types/types';
|
|
3
|
-
import { createRxNostr, RxNostr } from 'rx-nostr';
|
|
4
3
|
import { generateSecretKey, getEventHash, getPublicKey, nip19 } from 'nostr-tools';
|
|
5
4
|
import { NostrLoginAuthOptions, Response } from '../types';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Placeholder types for SimplePool transition
|
|
10
|
-
// private pool: SimplePool;
|
|
11
|
-
// private outboxPool: SimplePool;
|
|
5
|
+
import NDK, { NDKEvent, NDKNip46Signer, NDKRpcResponse, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
12
6
|
import { NostrParams } from './';
|
|
13
7
|
import { EventEmitter } from 'tseep';
|
|
14
8
|
import { Signer } from './Nostr';
|
|
15
9
|
import { Nip44 } from '../utils/nip44';
|
|
16
|
-
import { IframeNostrRpc, Nip46Signer, ReadyListener } from './Nip46';
|
|
10
|
+
import { IframeNostrRpc, Nip46Signer, ReadyListener, ensureNIP46Connection } from './Nip46';
|
|
17
11
|
import { PrivateKeySigner } from './Signer';
|
|
18
12
|
import { DEFAULT_NIP46_RELAYS } from '../const';
|
|
13
|
+
import { bytesToHex, hexToBytes } from 'nostr-tools/lib/types/utils';
|
|
19
14
|
|
|
20
15
|
const OUTBOX_RELAYS = ['wss://user.kindpag.es', 'wss://purplepag.es', 'wss://relay.nos.social'];
|
|
21
16
|
const NOSTRCONNECT_APPS: ConnectionString[] = [
|
|
@@ -39,7 +34,8 @@ const NOSTRCONNECT_APPS: ConnectionString[] = [
|
|
|
39
34
|
];
|
|
40
35
|
|
|
41
36
|
class AuthNostrService extends EventEmitter implements Signer {
|
|
42
|
-
private
|
|
37
|
+
private ndk: NDK;
|
|
38
|
+
private profileNdk: NDK;
|
|
43
39
|
private signer: Nip46Signer | null = null;
|
|
44
40
|
private localSigner: PrivateKeySigner | null = null;
|
|
45
41
|
private params: NostrParams;
|
|
@@ -66,11 +62,17 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
66
62
|
constructor(params: NostrParams) {
|
|
67
63
|
super();
|
|
68
64
|
this.params = params;
|
|
65
|
+
this.ndk = new NDK({
|
|
66
|
+
enableOutboxModel: false,
|
|
67
|
+
autoConnectUserRelays: false,
|
|
68
|
+
autoFetchUserMutelist: false,
|
|
69
|
+
});
|
|
69
70
|
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
this.profileNdk = new NDK({
|
|
72
|
+
enableOutboxModel: true,
|
|
73
|
+
explicitRelayUrls: OUTBOX_RELAYS,
|
|
74
|
+
});
|
|
75
|
+
this.profileNdk.connect();
|
|
74
76
|
|
|
75
77
|
this.nip04 = {
|
|
76
78
|
encrypt: this.encrypt04.bind(this),
|
|
@@ -82,11 +84,11 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
public isIframe()
|
|
87
|
+
public isIframe() {
|
|
86
88
|
return !!this.iframe;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
public async waitReady()
|
|
91
|
+
public async waitReady() {
|
|
90
92
|
if (this.signerPromise) {
|
|
91
93
|
try {
|
|
92
94
|
await this.signerPromise;
|
|
@@ -100,12 +102,12 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
public cancelNostrConnect()
|
|
105
|
+
public cancelNostrConnect() {
|
|
104
106
|
this.releaseSigner();
|
|
105
107
|
this.resetAuth();
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
public cancelSignerInit()
|
|
110
|
+
public cancelSignerInit() {
|
|
109
111
|
if (this.signerAbortController) {
|
|
110
112
|
this.signerAbortController.abort();
|
|
111
113
|
this.signerAbortController = undefined;
|
|
@@ -134,7 +136,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
134
136
|
iframeUrl?: string;
|
|
135
137
|
customRelays?: string[];
|
|
136
138
|
} = {},
|
|
137
|
-
)
|
|
139
|
+
) {
|
|
138
140
|
// カスタムリレーが指定されていれば使用、そうでなければ単一リレーまたはデフォルト
|
|
139
141
|
const relays = customRelays && customRelays.length > 0 ? customRelays : relay ? [relay] : DEFAULT_NIP46_RELAYS;
|
|
140
142
|
|
|
@@ -169,13 +171,11 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
169
171
|
return info;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
|
-
public async createNostrConnect()
|
|
173
|
-
|
|
174
|
-
// @ts-ignore
|
|
175
|
-
this.nostrConnectKey = bytesToHex(skBytes);
|
|
174
|
+
public async createNostrConnect() {
|
|
175
|
+
this.nostrConnectKey = bytesToHex(generateSecretKey());
|
|
176
176
|
this.nostrConnectSecret = Math.random().toString(36).substring(7);
|
|
177
177
|
|
|
178
|
-
const pubkey = getPublicKey(
|
|
178
|
+
const pubkey = getPublicKey(hexToBytes(this.nostrConnectKey));
|
|
179
179
|
const meta = {
|
|
180
180
|
name: encodeURIComponent(document.location.host),
|
|
181
181
|
url: encodeURIComponent(document.location.origin),
|
|
@@ -232,14 +232,10 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
232
232
|
return [nostrconnect, apps];
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
public async localSignup(name: string, sk?: string)
|
|
235
|
+
public async localSignup(name: string, sk?: string) {
|
|
236
236
|
const signup = !sk;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
sk = bytesToHex(skBytes);
|
|
240
|
-
}
|
|
241
|
-
// @ts-ignore
|
|
242
|
-
const pubkey = getPublicKey(hexToBytes(sk!));
|
|
237
|
+
sk = sk || bytesToHex(generateSecretKey());
|
|
238
|
+
const pubkey = getPublicKey(hexToBytes(sk));
|
|
243
239
|
const info: Info = {
|
|
244
240
|
pubkey,
|
|
245
241
|
sk,
|
|
@@ -250,118 +246,25 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
250
246
|
await this.setLocal(info, signup);
|
|
251
247
|
}
|
|
252
248
|
|
|
253
|
-
public async setLocal(info: Info, signup?: boolean)
|
|
249
|
+
public async setLocal(info: Info, signup?: boolean) {
|
|
254
250
|
this.releaseSigner();
|
|
255
|
-
this.localSigner = new PrivateKeySigner(
|
|
251
|
+
this.localSigner = new PrivateKeySigner(info.sk!);
|
|
256
252
|
|
|
257
|
-
|
|
258
|
-
if (signup) {
|
|
259
|
-
await createProfile(info, this.rxNostr, this.localSigner, this.params.optionsModal.signupRelays, this.params.optionsModal.outboxRelays);
|
|
260
|
-
}
|
|
253
|
+
if (signup) await createProfile(info, this.profileNdk, this.localSigner, this.params.optionsModal.signupRelays, this.params.optionsModal.outboxRelays);
|
|
261
254
|
|
|
262
255
|
this.onAuth(signup ? 'signup' : 'login', info);
|
|
263
256
|
}
|
|
264
257
|
|
|
265
|
-
public prepareImportUrl(url: string)
|
|
258
|
+
public prepareImportUrl(url: string) {
|
|
266
259
|
// for OTP we choose interactive import
|
|
267
260
|
if (this.params.userInfo?.authMethod === 'otp') return url + '&import=true';
|
|
268
261
|
|
|
269
262
|
// for local we export our existing key
|
|
270
263
|
if (!this.localSigner || this.params.userInfo?.authMethod !== 'local') throw new Error('Most be local keys');
|
|
271
|
-
|
|
272
|
-
const skBytes = this.localSigner.privateKey!;
|
|
273
|
-
return url + '#import=' + nip19.nsecEncode(skBytes);
|
|
264
|
+
return url + '#import=' + nip19.nsecEncode(hexToBytes(this.localSigner.privateKey!));
|
|
274
265
|
}
|
|
275
266
|
|
|
276
|
-
|
|
277
|
-
public getRelayStatus(): Record<string, string> {
|
|
278
|
-
const status = this.rxNostr.getAllRelayStatus();
|
|
279
|
-
const result: Record<string, string> = {};
|
|
280
|
-
for (const [url, state] of Object.entries(status)) {
|
|
281
|
-
result[url] = String(state);
|
|
282
|
-
}
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
public getRelayStats(): {
|
|
287
|
-
total: number;
|
|
288
|
-
connected: number;
|
|
289
|
-
connecting: number;
|
|
290
|
-
disconnected: number;
|
|
291
|
-
error: number;
|
|
292
|
-
} {
|
|
293
|
-
const status = this.rxNostr.getAllRelayStatus();
|
|
294
|
-
const stats = {
|
|
295
|
-
total: 0,
|
|
296
|
-
connected: 0,
|
|
297
|
-
connecting: 0,
|
|
298
|
-
disconnected: 0,
|
|
299
|
-
error: 0,
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
for (const state of Object.values(status)) {
|
|
303
|
-
stats.total++;
|
|
304
|
-
const stateStr = String(state).toLowerCase();
|
|
305
|
-
if (stateStr === 'connected') stats.connected++;
|
|
306
|
-
else if (stateStr === 'connecting') stats.connecting++;
|
|
307
|
-
else if (stateStr === 'error') stats.error++;
|
|
308
|
-
else stats.disconnected++;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return stats;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
public async connectToRelay(relayUrl: string): Promise<void> {
|
|
315
|
-
try {
|
|
316
|
-
this.rxNostr.reconnect(relayUrl);
|
|
317
|
-
console.log(`Connecting to relay: ${relayUrl}`);
|
|
318
|
-
} catch (error) {
|
|
319
|
-
console.error(`Failed to connect to relay ${relayUrl}:`, error);
|
|
320
|
-
throw error;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
public async disconnectFromRelay(relayUrl: string): Promise<void> {
|
|
325
|
-
try {
|
|
326
|
-
// rx-nostrにはdisconnect APIがないため、リレーリストから除外する方法を使用
|
|
327
|
-
const currentRelays = Object.keys(this.rxNostr.getAllRelayStatus());
|
|
328
|
-
const newRelays = currentRelays.filter(url => url !== relayUrl);
|
|
329
|
-
if (newRelays.length > 0) {
|
|
330
|
-
// @ts-ignore
|
|
331
|
-
this.rxNostr.switchRelays(newRelays);
|
|
332
|
-
}
|
|
333
|
-
console.log(`Disconnected from relay: ${relayUrl}`);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
console.error(`Failed to disconnect from relay ${relayUrl}:`, error);
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
public async reconnectAllRelays(): Promise<void> {
|
|
341
|
-
try {
|
|
342
|
-
const relays = Object.keys(this.rxNostr.getAllRelayStatus());
|
|
343
|
-
console.log(`Reconnecting to ${relays.length} relays...`);
|
|
344
|
-
|
|
345
|
-
for (const relay of relays) {
|
|
346
|
-
try {
|
|
347
|
-
this.rxNostr.reconnect(relay);
|
|
348
|
-
} catch (error) {
|
|
349
|
-
console.warn(`Failed to reconnect to ${relay}:`, error);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error('Failed to reconnect all relays:', error);
|
|
354
|
-
throw error;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
public async checkRelayHealth(): Promise<boolean> {
|
|
359
|
-
const stats = this.getRelayStats();
|
|
360
|
-
// 少なくとも1つのリレーが接続されていればOK
|
|
361
|
-
return stats.connected > 0;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
public async importAndConnect(cs: ConnectionString): Promise<void> {
|
|
267
|
+
public async importAndConnect(cs: ConnectionString) {
|
|
365
268
|
const { relay, domain, link, iframeUrl } = cs;
|
|
366
269
|
if (!domain) throw new Error('Domain required');
|
|
367
270
|
|
|
@@ -378,22 +281,22 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
378
281
|
this.onAuth('login', info);
|
|
379
282
|
}
|
|
380
283
|
|
|
381
|
-
public setReadOnly(pubkey: string)
|
|
284
|
+
public setReadOnly(pubkey: string) {
|
|
382
285
|
const info: Info = { pubkey, authMethod: 'readOnly' };
|
|
383
286
|
this.onAuth('login', info);
|
|
384
287
|
}
|
|
385
288
|
|
|
386
|
-
public setExtension(pubkey: string)
|
|
289
|
+
public setExtension(pubkey: string) {
|
|
387
290
|
const info: Info = { pubkey, authMethod: 'extension' };
|
|
388
291
|
this.onAuth('login', info);
|
|
389
292
|
}
|
|
390
293
|
|
|
391
|
-
public setOTP(pubkey: string, data: string)
|
|
294
|
+
public setOTP(pubkey: string, data: string) {
|
|
392
295
|
const info: Info = { pubkey, authMethod: 'otp', otpData: data };
|
|
393
296
|
this.onAuth('login', info);
|
|
394
297
|
}
|
|
395
298
|
|
|
396
|
-
public async setConnect(info: Info)
|
|
299
|
+
public async setConnect(info: Info) {
|
|
397
300
|
this.releaseSigner();
|
|
398
301
|
await this.startAuth();
|
|
399
302
|
await this.initSigner(info);
|
|
@@ -401,7 +304,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
401
304
|
await this.endAuth();
|
|
402
305
|
}
|
|
403
306
|
|
|
404
|
-
public async createAccount(nip05: string)
|
|
307
|
+
public async createAccount(nip05: string) {
|
|
405
308
|
const [name, domain] = nip05.split('@');
|
|
406
309
|
|
|
407
310
|
// bunker's own url
|
|
@@ -425,19 +328,18 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
425
328
|
};
|
|
426
329
|
}
|
|
427
330
|
|
|
428
|
-
private releaseSigner()
|
|
331
|
+
private releaseSigner() {
|
|
429
332
|
this.signer = null;
|
|
430
333
|
this.signerErrCallback?.('cancelled');
|
|
431
334
|
this.localSigner = null;
|
|
432
335
|
|
|
433
336
|
// disconnect from signer relays
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// this.rxNostr.switchRelays([]); // Should we disconnect? Maybe better to keep connection for next signer?
|
|
337
|
+
for (const r of this.ndk.pool.relays.keys()) {
|
|
338
|
+
this.ndk.pool.removeRelay(r);
|
|
339
|
+
}
|
|
438
340
|
}
|
|
439
341
|
|
|
440
|
-
public async logout(keepSigner = false)
|
|
342
|
+
public async logout(keepSigner = false) {
|
|
441
343
|
if (!keepSigner) this.releaseSigner();
|
|
442
344
|
|
|
443
345
|
// move current to recent
|
|
@@ -449,7 +351,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
449
351
|
this.emit('updateAccounts');
|
|
450
352
|
}
|
|
451
353
|
|
|
452
|
-
private setUserInfo(userInfo: Info | null)
|
|
354
|
+
private setUserInfo(userInfo: Info | null) {
|
|
453
355
|
this.params.userInfo = userInfo;
|
|
454
356
|
this.emit('onUserInfo', userInfo);
|
|
455
357
|
|
|
@@ -459,14 +361,13 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
459
361
|
}
|
|
460
362
|
}
|
|
461
363
|
|
|
462
|
-
public exportKeys()
|
|
364
|
+
public exportKeys() {
|
|
463
365
|
if (!this.params.userInfo) return '';
|
|
464
366
|
if (this.params.userInfo.authMethod !== 'local') return '';
|
|
465
|
-
|
|
466
|
-
return nip19.nsecEncode(skBytes);
|
|
367
|
+
return nip19.nsecEncode(hexToBytes(this.params.userInfo.sk!));
|
|
467
368
|
}
|
|
468
369
|
|
|
469
|
-
private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null)
|
|
370
|
+
private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null) {
|
|
470
371
|
if (type !== 'logout' && !info) throw new Error('No user info in onAuth');
|
|
471
372
|
|
|
472
373
|
// make sure we emulate logout first
|
|
@@ -480,8 +381,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
480
381
|
|
|
481
382
|
if (info) {
|
|
482
383
|
// async profile fetch
|
|
483
|
-
|
|
484
|
-
fetchProfile(info, this.rxNostr as any).then(p => {
|
|
384
|
+
fetchProfile(info, this.profileNdk).then(p => {
|
|
485
385
|
if (this.params.userInfo !== info) return;
|
|
486
386
|
|
|
487
387
|
const userInfo = {
|
|
@@ -540,7 +440,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
540
440
|
}
|
|
541
441
|
}
|
|
542
442
|
|
|
543
|
-
private async createIframe(iframeUrl?: string)
|
|
443
|
+
private async createIframe(iframeUrl?: string) {
|
|
544
444
|
if (!iframeUrl) return undefined;
|
|
545
445
|
|
|
546
446
|
// ensure iframe
|
|
@@ -599,18 +499,18 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
599
499
|
// }
|
|
600
500
|
// }
|
|
601
501
|
|
|
602
|
-
public async sendNeedAuth()
|
|
502
|
+
public async sendNeedAuth() {
|
|
603
503
|
const [nostrconnect] = await this.getNostrConnectServices();
|
|
604
504
|
const event = new CustomEvent('nlNeedAuth', { detail: { nostrconnect } });
|
|
605
505
|
console.log('nostr-login need auth', nostrconnect);
|
|
606
506
|
document.dispatchEvent(event);
|
|
607
507
|
}
|
|
608
508
|
|
|
609
|
-
public isAuthing()
|
|
509
|
+
public isAuthing() {
|
|
610
510
|
return !!this.readyCallback;
|
|
611
511
|
}
|
|
612
512
|
|
|
613
|
-
public async startAuth()
|
|
513
|
+
public async startAuth() {
|
|
614
514
|
console.log('startAuth');
|
|
615
515
|
if (this.readyCallback) throw new Error('Already started');
|
|
616
516
|
|
|
@@ -618,7 +518,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
618
518
|
this.readyPromise = new Promise<void>(ok => (this.readyCallback = ok));
|
|
619
519
|
}
|
|
620
520
|
|
|
621
|
-
public async endAuth()
|
|
521
|
+
public async endAuth() {
|
|
622
522
|
console.log('endAuth', this.params.userInfo);
|
|
623
523
|
if (this.params.userInfo && this.params.userInfo.iframeUrl) {
|
|
624
524
|
// create iframe
|
|
@@ -634,23 +534,23 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
634
534
|
this.readyCallback = undefined;
|
|
635
535
|
}
|
|
636
536
|
|
|
637
|
-
public resetAuth()
|
|
537
|
+
public resetAuth() {
|
|
638
538
|
if (this.readyCallback) this.readyCallback();
|
|
639
539
|
this.readyCallback = undefined;
|
|
640
540
|
}
|
|
641
541
|
|
|
642
|
-
private async listen(info: Info)
|
|
542
|
+
private async listen(info: Info) {
|
|
643
543
|
if (!info.iframeUrl) return this.signer!.listen(this.nostrConnectSecret);
|
|
644
544
|
const r = await this.starterReady!.wait();
|
|
645
545
|
if (r[0] === 'starterError') throw new Error(r[1]);
|
|
646
546
|
return this.signer!.setListenReply(r[1], this.nostrConnectSecret);
|
|
647
547
|
}
|
|
648
548
|
|
|
649
|
-
public async connect(info: Info, perms?: string)
|
|
549
|
+
public async connect(info: Info, perms?: string) {
|
|
650
550
|
return this.signer!.connect(info.token, perms);
|
|
651
551
|
}
|
|
652
552
|
|
|
653
|
-
public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {})
|
|
553
|
+
public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {}) {
|
|
654
554
|
// mutex
|
|
655
555
|
if (this.signerPromise) {
|
|
656
556
|
try {
|
|
@@ -696,28 +596,23 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
696
596
|
return this.signerPromise;
|
|
697
597
|
}
|
|
698
598
|
|
|
699
|
-
private async initSignerInternal(info: Info, listen: boolean, connect: boolean, eventToAddAccount: boolean, resolve: () => void)
|
|
599
|
+
private async initSignerInternal(info: Info, listen: boolean, connect: boolean, eventToAddAccount: boolean, resolve: () => void) {
|
|
700
600
|
// pre-connect if we're creating the connection (listen|connect) or
|
|
701
601
|
// not iframe mode
|
|
702
602
|
if (info.relays && !info.iframeUrl) {
|
|
703
|
-
|
|
704
|
-
|
|
603
|
+
for (const r of info.relays) {
|
|
604
|
+
this.ndk.addExplicitRelay(r, undefined);
|
|
605
|
+
}
|
|
705
606
|
}
|
|
706
607
|
|
|
707
|
-
//
|
|
608
|
+
// wait until we connect, otherwise
|
|
609
|
+
// signer won't start properly
|
|
610
|
+
// NOTE: Deferred connection. ensureNIP46Connection will be called on demand.
|
|
611
|
+
// await this.ndk.connect();
|
|
708
612
|
|
|
709
613
|
// create and prepare the signer
|
|
710
|
-
const localSigner = new PrivateKeySigner(
|
|
711
|
-
|
|
712
|
-
// TODO: 元のNDKロジック:
|
|
713
|
-
// This initialized the Nip46Signer with NDK instance.
|
|
714
|
-
//
|
|
715
|
-
// New Implementation Plan:
|
|
716
|
-
// 1. Pass the SimplePool instance or relay list to Nip46Signer.
|
|
717
|
-
// 2. Nip46Signer should manage its own connection or use the shared pool to publish/subscribe.
|
|
718
|
-
|
|
719
|
-
// TODO: ndk引数を削除する
|
|
720
|
-
this.signer = new Nip46Signer(this.rxNostr, localSigner, info.signerPubkey!, info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined);
|
|
614
|
+
const localSigner = new PrivateKeySigner(info.sk!);
|
|
615
|
+
this.signer = new Nip46Signer(this.ndk, localSigner, info.signerPubkey!, info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined);
|
|
721
616
|
|
|
722
617
|
// we should notify the banner the same way as
|
|
723
618
|
// the onAuthUrl does
|
|
@@ -769,7 +664,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
769
664
|
iframeUrl = '',
|
|
770
665
|
customRelays,
|
|
771
666
|
}: { name: string; bunkerUrl: string; sk?: string; domain?: string; iframeUrl?: string; customRelays?: string[] },
|
|
772
|
-
)
|
|
667
|
+
) {
|
|
773
668
|
try {
|
|
774
669
|
const info = bunkerUrlToInfo(bunkerUrl, sk);
|
|
775
670
|
if (isBunkerUrl(name)) info.bunkerUrl = name;
|
|
@@ -806,15 +701,17 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
806
701
|
}
|
|
807
702
|
}
|
|
808
703
|
|
|
809
|
-
public async signEvent(event: any)
|
|
704
|
+
public async signEvent(event: any) {
|
|
810
705
|
if (this.localSigner) {
|
|
811
|
-
|
|
812
|
-
event.pubkey = getPublicKey(this.localSigner.privateKey!);
|
|
706
|
+
event.pubkey = getPublicKey(hexToBytes(this.localSigner.privateKey!));
|
|
813
707
|
event.id = getEventHash(event);
|
|
814
708
|
event.sig = await this.localSigner.sign(event);
|
|
815
709
|
} else {
|
|
816
710
|
await this.ensureSigner();
|
|
817
711
|
|
|
712
|
+
// 署名前に接続を確認 (rpc.sendRequest内でも行われるが、安全のため)
|
|
713
|
+
await ensureNIP46Connection(this.ndk, 5000);
|
|
714
|
+
|
|
818
715
|
event.pubkey = this.signer!.remotePubkey;
|
|
819
716
|
event.id = getEventHash(event);
|
|
820
717
|
event.sig = await this.signer!.sign(event);
|
|
@@ -823,89 +720,72 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
823
720
|
return event;
|
|
824
721
|
}
|
|
825
722
|
|
|
826
|
-
private async ensureSigner()
|
|
723
|
+
private async ensureSigner() {
|
|
827
724
|
// signerがキャンセル等で破棄されている場合は再初期化
|
|
828
725
|
if (!this.signer && this.params.userInfo) {
|
|
829
726
|
console.log('Signer was destroyed, reinitializing...');
|
|
830
727
|
await this.initSigner(this.params.userInfo);
|
|
831
|
-
return;
|
|
728
|
+
return;
|
|
832
729
|
}
|
|
833
730
|
|
|
834
731
|
if (!this.signer) {
|
|
835
732
|
throw new Error('No signer available');
|
|
836
733
|
}
|
|
837
734
|
|
|
838
|
-
//
|
|
839
|
-
//
|
|
840
|
-
// Checks ndk.pool.stats() for connectivity.
|
|
841
|
-
//
|
|
842
|
-
// New Implementation Plan:
|
|
843
|
-
// 1. Iterate through SimplePool.relays or listConnectionStatus().
|
|
844
|
-
// 2. If no relays are connected, modify code to reconnect.
|
|
845
|
-
|
|
846
|
-
// TODO: rx-nostrの統計を確認する
|
|
847
|
-
// const stats = this.pool.listConnectionStatus();
|
|
848
|
-
// const stats = this.pool.listConnectionStatus();
|
|
849
|
-
// const stats = this.ndk.pool.stats();
|
|
850
|
-
// console.log('NDK pool stats:', stats);
|
|
851
|
-
const relayStates = this.rxNostr.getAllRelayStatus();
|
|
852
|
-
const connectedCount = Object.values(relayStates).filter(s => String(s) === 'connected').length;
|
|
853
|
-
|
|
854
|
-
if (connectedCount === 0) {
|
|
855
|
-
console.log('RxNostr relays disconnected, reinitializing signer...');
|
|
856
|
-
|
|
857
|
-
// リレーが完全に切断されている場合、signerも再初期化する必要がある
|
|
858
|
-
// (RPCサブスクリプションも切断されているため)
|
|
859
|
-
if (this.params.userInfo) {
|
|
860
|
-
// 古いsignerを破棄
|
|
861
|
-
this.signer = null;
|
|
862
|
-
|
|
863
|
-
// 既存のリレーを一度切断 (rx-nostr reconnects automatically, but we might want to force it)
|
|
864
|
-
this.rxNostr.reconnect(Object.keys(relayStates)[0]);
|
|
865
|
-
|
|
866
|
-
// signerを再初期化(リレー接続も含む)
|
|
867
|
-
await this.initSigner(this.params.userInfo);
|
|
868
|
-
} else {
|
|
869
|
-
throw new Error('Cannot reconnect: no user info');
|
|
870
|
-
}
|
|
871
|
-
}
|
|
735
|
+
// 接続確立は署名時またはRPCリクエスト時に行われるため、
|
|
736
|
+
// ここでの冗長な接続チェックは削除
|
|
872
737
|
}
|
|
873
738
|
|
|
739
|
+
private async codec_call(method: string, pubkey: string, param: string) {
|
|
740
|
+
return new Promise<string>((resolve, reject) => {
|
|
741
|
+
this.signer!.rpc.sendRequest(this.signer!.remotePubkey!, method, [pubkey, param], 24133, (response: NDKRpcResponse) => {
|
|
742
|
+
if (!response.error) {
|
|
743
|
+
resolve(response.result);
|
|
744
|
+
} else {
|
|
745
|
+
reject(response.error);
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
}
|
|
874
750
|
|
|
875
|
-
|
|
876
|
-
public async encrypt04(pubkey: string, plaintext: string): Promise<string> {
|
|
751
|
+
public async encrypt04(pubkey: string, plaintext: string) {
|
|
877
752
|
if (this.localSigner) {
|
|
878
|
-
return this.localSigner.encrypt(pubkey
|
|
753
|
+
return this.localSigner.encrypt(new NDKUser({ pubkey }), plaintext);
|
|
879
754
|
} else {
|
|
880
755
|
await this.ensureSigner();
|
|
881
|
-
return this.signer!.encrypt(pubkey, plaintext);
|
|
756
|
+
return this.signer!.encrypt(new NDKUser({ pubkey }), plaintext);
|
|
882
757
|
}
|
|
883
758
|
}
|
|
884
759
|
|
|
885
|
-
public async decrypt04(pubkey: string, ciphertext: string)
|
|
760
|
+
public async decrypt04(pubkey: string, ciphertext: string) {
|
|
886
761
|
if (this.localSigner) {
|
|
887
|
-
return this.localSigner.decrypt(pubkey
|
|
762
|
+
return this.localSigner.decrypt(new NDKUser({ pubkey }), ciphertext);
|
|
888
763
|
} 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
|
|
889
767
|
await this.ensureSigner();
|
|
890
|
-
return this.
|
|
768
|
+
return this.codec_call('nip04_decrypt', pubkey, ciphertext);
|
|
891
769
|
}
|
|
892
770
|
}
|
|
893
771
|
|
|
894
|
-
public async encrypt44(pubkey: string, plaintext: string)
|
|
772
|
+
public async encrypt44(pubkey: string, plaintext: string) {
|
|
895
773
|
if (this.localSigner) {
|
|
896
774
|
return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
|
|
897
775
|
} else {
|
|
776
|
+
// no support of nip44 in ndk yet
|
|
898
777
|
await this.ensureSigner();
|
|
899
|
-
return this.
|
|
778
|
+
return this.codec_call('nip44_encrypt', pubkey, plaintext);
|
|
900
779
|
}
|
|
901
780
|
}
|
|
902
781
|
|
|
903
|
-
public async decrypt44(pubkey: string, ciphertext: string)
|
|
782
|
+
public async decrypt44(pubkey: string, ciphertext: string) {
|
|
904
783
|
if (this.localSigner) {
|
|
905
784
|
return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
|
|
906
785
|
} else {
|
|
786
|
+
// no support of nip44 in ndk yet
|
|
907
787
|
await this.ensureSigner();
|
|
908
|
-
return this.
|
|
788
|
+
return this.codec_call('nip44_decrypt', pubkey, ciphertext);
|
|
909
789
|
}
|
|
910
790
|
}
|
|
911
791
|
}
|