@konemono/nostr-login 1.7.50 → 1.7.51

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konemono/nostr-login",
3
- "version": "1.7.50",
3
+ "version": "1.7.51",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,106 @@
1
+ import { NDKUser } from '@nostr-dev-kit/ndk';
2
+ import { Signer } from './Nostr';
3
+
4
+ export class AmberDirectSigner implements Signer {
5
+ private _pubkey: string = '';
6
+
7
+ constructor(pubkey?: string) {
8
+ this._pubkey = pubkey || '';
9
+ }
10
+
11
+ get pubkey() {
12
+ return this._pubkey;
13
+ }
14
+
15
+ public set pubkey(v: string) {
16
+ this._pubkey = v;
17
+ }
18
+
19
+ nip04 = {
20
+ encrypt: (pubkey: string, plaintext: string) => this.encrypt04(pubkey, plaintext),
21
+ decrypt: (pubkey: string, ciphertext: string) => this.decrypt04(pubkey, ciphertext),
22
+ };
23
+
24
+ nip44 = {
25
+ encrypt: (pubkey: string, plaintext: string) => this.encrypt44(pubkey, plaintext),
26
+ decrypt: (pubkey: string, ciphertext: string) => this.decrypt44(pubkey, ciphertext),
27
+ };
28
+
29
+ async signEvent(event: any): Promise<any> {
30
+ const id = Math.random().toString(36).substring(7);
31
+ const url = this.generateUrl(JSON.stringify(event), 'sign_event', id);
32
+ window.location.href = url;
33
+ // This will never resolve because of page reload
34
+ return new Promise(() => {});
35
+ }
36
+
37
+ async encrypt04(pubkey: string, plaintext: string): Promise<string> {
38
+ const id = Math.random().toString(36).substring(7);
39
+ const url = this.generateUrl(plaintext, 'nip04_encrypt', id, pubkey);
40
+ window.location.href = url;
41
+ return new Promise(() => {});
42
+ }
43
+
44
+ async decrypt04(pubkey: string, ciphertext: string): Promise<string> {
45
+ const id = Math.random().toString(36).substring(7);
46
+ const url = this.generateUrl(ciphertext, 'nip04_decrypt', id, pubkey);
47
+ window.location.href = url;
48
+ return new Promise(() => {});
49
+ }
50
+
51
+ async encrypt44(pubkey: string, plaintext: string): Promise<string> {
52
+ const id = Math.random().toString(36).substring(7);
53
+ const url = this.generateUrl(plaintext, 'nip44_encrypt', id, pubkey);
54
+ window.location.href = url;
55
+ return new Promise(() => {});
56
+ }
57
+
58
+ async decrypt44(pubkey: string, ciphertext: string): Promise<string> {
59
+ const id = Math.random().toString(36).substring(7);
60
+ const url = this.generateUrl(ciphertext, 'nip44_decrypt', id, pubkey);
61
+ window.location.href = url;
62
+ return new Promise(() => {});
63
+ }
64
+
65
+ public async getPublicKey(): Promise<string> {
66
+ const id = Math.random().toString(36).substring(7);
67
+ const url = this.generateUrl('', 'get_public_key', id);
68
+ window.location.href = url;
69
+ return new Promise(() => {});
70
+ }
71
+
72
+ private generateUrl(content: string, type: string, id: string, recipient?: string): string {
73
+ const callbackUrl = new URL(window.location.href);
74
+ callbackUrl.searchParams.set('amberType', type);
75
+ callbackUrl.searchParams.set('amberId', id);
76
+
77
+ const params = new URLSearchParams();
78
+ params.set('type', type);
79
+ params.set('id', id);
80
+ params.set('callbackUrl', callbackUrl.toString());
81
+ if (this._pubkey) params.set('pubkey', this._pubkey);
82
+ if (recipient) params.set('pubkey', recipient); // Amber uses pubkey param for recipient in encrypt/decrypt
83
+
84
+ return `nostrsigner:${encodeURIComponent(content)}?${params.toString()}`;
85
+ }
86
+
87
+ public static parseResponse(): { type: string; id: string; result: string } | null {
88
+ const params = new URLSearchParams(window.location.search);
89
+ const type = params.get('amberType');
90
+ const id = params.get('amberId');
91
+ const result = params.get('signature') || params.get('result'); // Amber uses signature for events, result for others?
92
+
93
+ if (type && id && result) {
94
+ // Clean up URL
95
+ const newUrl = new URL(window.location.href);
96
+ newUrl.searchParams.delete('amberType');
97
+ newUrl.searchParams.delete('amberId');
98
+ newUrl.searchParams.delete('signature');
99
+ newUrl.searchParams.delete('result');
100
+ window.history.replaceState({}, '', newUrl.toString());
101
+
102
+ return { type, id, result };
103
+ }
104
+ return null;
105
+ }
106
+ }
@@ -9,6 +9,8 @@ import { Signer } from './Nostr';
9
9
  import { Nip44 } from '../utils/nip44';
10
10
  import { IframeNostrRpc, Nip46Signer, ReadyListener } from './Nip46';
11
11
  import { PrivateKeySigner } from './Signer';
12
+ import { AmberDirectSigner } from './AmberDirectSigner';
13
+
12
14
 
13
15
  const OUTBOX_RELAYS = ['wss://user.kindpag.es', 'wss://purplepag.es', 'wss://relay.nos.social'];
14
16
  const DEFAULT_NOSTRCONNECT_RELAYS = ['wss://relay.nsec.app/', 'wss://ephemeral.snowflare.cc/'];
@@ -40,6 +42,7 @@ class AuthNostrService extends EventEmitter implements Signer {
40
42
  private ndk: NDK;
41
43
  private profileNdk: NDK;
42
44
  private signer: Nip46Signer | null = null;
45
+ private amberSigner: AmberDirectSigner | null = null;
43
46
  private localSigner: PrivateKeySigner | null = null;
44
47
  private params: NostrParams;
45
48
  private signerPromise?: Promise<void>;
@@ -82,6 +85,31 @@ class AuthNostrService extends EventEmitter implements Signer {
82
85
  encrypt: this.encrypt44.bind(this),
83
86
  decrypt: this.decrypt44.bind(this),
84
87
  };
88
+
89
+ this.checkAmberResponse();
90
+ }
91
+
92
+ private checkAmberResponse() {
93
+ const response = AmberDirectSigner.parseResponse();
94
+ if (response) {
95
+ if (response.type === 'get_public_key') {
96
+ const info: Info = {
97
+ pubkey: response.result,
98
+ authMethod: 'amber' as any,
99
+ };
100
+ this.onAuth('login', info);
101
+ } else {
102
+ // For other types, we might want to store the result in a way
103
+ // that the next call to the same method can return it immediately
104
+ // but for now, we just log it.
105
+ console.log('Amber response', response);
106
+ if (response.type === 'sign_event') {
107
+ // If it's a signed event, we could potentially use it if someone asks for it.
108
+ // But usually the app will re-request signing.
109
+ // A better way would be to have a session-based cache.
110
+ }
111
+ }
112
+ }
85
113
  }
86
114
 
87
115
  public isIframe() {
@@ -137,7 +165,13 @@ class AuthNostrService extends EventEmitter implements Signer {
137
165
  console.log('nostrconnect info', info, link);
138
166
 
139
167
  // non-iframe flow
140
- if (link && !iframeUrl) window.open(link, '_blank', 'width=400,height=700');
168
+ if (link && !iframeUrl) {
169
+ if (link === 'amber') {
170
+ const signer = new AmberDirectSigner();
171
+ return (signer as any).getPublicKey(); // will redirect
172
+ }
173
+ window.open(link, '_blank', 'width=400,height=700');
174
+ }
141
175
 
142
176
  // init nip46 signer
143
177
  await this.initSigner(info, { listen: true });
@@ -299,6 +333,12 @@ class AuthNostrService extends EventEmitter implements Signer {
299
333
  await this.endAuth();
300
334
  }
301
335
 
336
+ public async setAmber(info: Info) {
337
+ this.releaseSigner();
338
+ this.amberSigner = new AmberDirectSigner(info.pubkey);
339
+ this.onAuth('login', info);
340
+ }
341
+
302
342
  public async createAccount(nip05: string) {
303
343
  const [name, domain] = nip05.split('@');
304
344
 
@@ -329,6 +369,7 @@ class AuthNostrService extends EventEmitter implements Signer {
329
369
  this.signer = null;
330
370
  this.signerErrCallback?.('cancelled');
331
371
  this.localSigner = null;
372
+ this.amberSigner = null;
332
373
 
333
374
  // disconnect from signer relays
334
375
  for (const r of this.ndk.pool.relays.keys()) {
@@ -678,6 +719,10 @@ class AuthNostrService extends EventEmitter implements Signer {
678
719
  event.pubkey = getPublicKey(this.localSigner.privateKey!);
679
720
  event.id = getEventHash(event);
680
721
  event.sig = await this.localSigner.sign(event);
722
+ } else if (this.params.userInfo?.authMethod === ('amber' as any)) {
723
+ const userInfo = this.params.userInfo!;
724
+ if (!this.amberSigner) this.amberSigner = new AmberDirectSigner(userInfo.pubkey);
725
+ return this.amberSigner.signEvent(event);
681
726
  } else {
682
727
  event.pubkey = this.signer?.remotePubkey;
683
728
  event.id = getEventHash(event);
@@ -710,6 +755,10 @@ class AuthNostrService extends EventEmitter implements Signer {
710
755
  public async encrypt04(pubkey: string, plaintext: string) {
711
756
  if (this.localSigner) {
712
757
  return this.localSigner.encrypt(new NDKUser({ pubkey }), plaintext);
758
+ } else if (this.params.userInfo?.authMethod === ('amber' as any)) {
759
+ const userInfo = this.params.userInfo!;
760
+ if (!this.amberSigner) this.amberSigner = new AmberDirectSigner(userInfo.pubkey);
761
+ return this.amberSigner.encrypt04(pubkey, plaintext);
713
762
  } else {
714
763
  return this.signer!.encrypt(new NDKUser({ pubkey }), plaintext);
715
764
  }
@@ -718,6 +767,10 @@ class AuthNostrService extends EventEmitter implements Signer {
718
767
  public async decrypt04(pubkey: string, ciphertext: string) {
719
768
  if (this.localSigner) {
720
769
  return this.localSigner.decrypt(new NDKUser({ pubkey }), ciphertext);
770
+ } else if (this.params.userInfo?.authMethod === ('amber' as any)) {
771
+ const userInfo = this.params.userInfo!;
772
+ if (!this.amberSigner) this.amberSigner = new AmberDirectSigner(userInfo.pubkey);
773
+ return this.amberSigner.decrypt04(pubkey, ciphertext);
721
774
  } else {
722
775
  // decrypt is broken in ndk v2.3.1, and latest
723
776
  // ndk v2.8.1 doesn't allow to override connect easily,
@@ -730,6 +783,10 @@ class AuthNostrService extends EventEmitter implements Signer {
730
783
  public async encrypt44(pubkey: string, plaintext: string) {
731
784
  if (this.localSigner) {
732
785
  return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
786
+ } else if (this.params.userInfo?.authMethod === ('amber' as any)) {
787
+ const userInfo = this.params.userInfo!;
788
+ if (!this.amberSigner) this.amberSigner = new AmberDirectSigner(userInfo.pubkey);
789
+ return this.amberSigner.encrypt44(pubkey, plaintext);
733
790
  } else {
734
791
  // no support of nip44 in ndk yet
735
792
  return this.codec_call('nip44_encrypt', pubkey, plaintext);
@@ -739,6 +796,10 @@ class AuthNostrService extends EventEmitter implements Signer {
739
796
  public async decrypt44(pubkey: string, ciphertext: string) {
740
797
  if (this.localSigner) {
741
798
  return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
799
+ } else if (this.params.userInfo?.authMethod === ('amber' as any)) {
800
+ const userInfo = this.params.userInfo!;
801
+ if (!this.amberSigner) this.amberSigner = new AmberDirectSigner(userInfo.pubkey);
802
+ return this.amberSigner.decrypt44(pubkey, ciphertext);
742
803
  } else {
743
804
  // no support of nip44 in ndk yet
744
805
  return this.codec_call('nip44_decrypt', pubkey, ciphertext);
@@ -402,6 +402,9 @@ class ModalManager extends EventEmitter {
402
402
  } else if (userInfo.authMethod === 'extension') {
403
403
  await this.extensionService.trySetExtensionForPubkey(userInfo.pubkey);
404
404
  dialog.close();
405
+ } else if (userInfo.authMethod === ('amber' as any)) {
406
+ this.authNostrService.setAmber(userInfo);
407
+ dialog.close();
405
408
  } else {
406
409
  const input = userInfo.bunkerUrl || userInfo.nip05;
407
410
  if (!input) throw new Error('Bad connect info');