@konemono/nostr-login 1.7.59 → 1.7.61
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 +2 -2
- package/dist/unpkg.js +2 -2
- package/package.json +1 -1
- package/src/modules/AmberDirectSigner.ts +58 -39
- package/src/modules/AuthNostrService.ts +21 -4
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Signer } from './Nostr';
|
|
2
2
|
|
|
3
|
+
const PENDING_KEY = 'amber_pending_request';
|
|
4
|
+
|
|
3
5
|
export class AmberDirectSigner implements Signer {
|
|
4
6
|
private _pubkey: string = '';
|
|
5
7
|
private static pendingResolves: Map<string, (result: any) => void> = new Map();
|
|
@@ -49,31 +51,36 @@ export class AmberDirectSigner implements Signer {
|
|
|
49
51
|
|
|
50
52
|
private setPending(id: string, content: string, type: string) {
|
|
51
53
|
const hash = this.getHash(content);
|
|
52
|
-
|
|
54
|
+
const data = JSON.stringify({ id, type, hash, timestamp: Date.now() });
|
|
55
|
+
sessionStorage.setItem(`amber_pending_${id}`, data);
|
|
56
|
+
localStorage.setItem(PENDING_KEY, data); // Backup for reloads
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
public static resolvePending(id: string, type: string, result: string) {
|
|
56
|
-
|
|
60
|
+
console.log('AmberDirectSigner: resolvePending', { id, type, pendingCount: this.pendingResolves.size });
|
|
61
|
+
|
|
57
62
|
let resolve = this.pendingResolves.get(id);
|
|
58
63
|
|
|
59
|
-
// Fallback
|
|
64
|
+
// Fallback by type if only one pending of that type
|
|
60
65
|
if (!resolve) {
|
|
61
|
-
console.log('
|
|
66
|
+
console.log('AmberDirectSigner: Falling back to resolve by type', type);
|
|
62
67
|
for (const [pendingId, pendingResolve] of this.pendingResolves.entries()) {
|
|
63
|
-
const pending = sessionStorage.getItem(`amber_pending_${pendingId}`);
|
|
68
|
+
const pending = sessionStorage.getItem(`amber_pending_${pendingId}`) || localStorage.getItem(PENDING_KEY);
|
|
64
69
|
if (pending) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
try {
|
|
71
|
+
const { type: pendingType } = JSON.parse(pending);
|
|
72
|
+
if (pendingType === type || type.startsWith(pendingType)) {
|
|
73
|
+
resolve = pendingResolve;
|
|
74
|
+
id = pendingId;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {}
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
if (resolve) {
|
|
76
|
-
console.log('
|
|
83
|
+
console.log('AmberDirectSigner: Promise resolved', id);
|
|
77
84
|
resolve(result);
|
|
78
85
|
this.pendingResolves.delete(id);
|
|
79
86
|
return true;
|
|
@@ -84,9 +91,7 @@ export class AmberDirectSigner implements Signer {
|
|
|
84
91
|
async signEvent(event: any): Promise<any> {
|
|
85
92
|
const content = JSON.stringify(event);
|
|
86
93
|
const cached = this.checkCache(content, 'sign_event');
|
|
87
|
-
if (cached)
|
|
88
|
-
return JSON.parse(cached);
|
|
89
|
-
}
|
|
94
|
+
if (cached) return JSON.parse(cached);
|
|
90
95
|
|
|
91
96
|
const id = Math.random().toString(36).substring(7);
|
|
92
97
|
this.setPending(id, content, 'sign_event');
|
|
@@ -99,6 +104,7 @@ export class AmberDirectSigner implements Signer {
|
|
|
99
104
|
|
|
100
105
|
async getPublicKey(): Promise<string> {
|
|
101
106
|
const id = Math.random().toString(36).substring(7);
|
|
107
|
+
this.setPending(id, '', 'get_public_key');
|
|
102
108
|
const url = this.generateUrl('', 'get_public_key', id);
|
|
103
109
|
window.location.href = url;
|
|
104
110
|
return new Promise((resolve) => {
|
|
@@ -161,36 +167,28 @@ export class AmberDirectSigner implements Signer {
|
|
|
161
167
|
private generateUrl(content: string, type: string, id: string, recipient?: string): string {
|
|
162
168
|
const callbackUrl = new URL(window.location.origin + window.location.pathname);
|
|
163
169
|
|
|
164
|
-
//
|
|
165
|
-
const currentParams = new URLSearchParams(window.location.search);
|
|
166
|
-
currentParams.forEach((value, key) => {
|
|
167
|
-
if (!['amberType', 'amberId', 'signature', 'result', 'pubKey', 'pubkey'].includes(key)) {
|
|
168
|
-
callbackUrl.searchParams.set(key, value);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
170
|
+
// Minimal parameters in callbackUrl to avoid conflicts
|
|
172
171
|
callbackUrl.searchParams.set('amberType', type);
|
|
173
172
|
callbackUrl.searchParams.set('amberId', id);
|
|
174
173
|
|
|
175
174
|
const name = document.title || window.location.hostname || 'Nostr Login';
|
|
176
175
|
|
|
177
|
-
const params =
|
|
178
|
-
params.
|
|
179
|
-
params.
|
|
180
|
-
params.
|
|
181
|
-
params.
|
|
182
|
-
params.
|
|
176
|
+
const params: string[] = [];
|
|
177
|
+
params.push(`type=${encodeURIComponent(type)}`);
|
|
178
|
+
params.push(`id=${encodeURIComponent(id)}`);
|
|
179
|
+
params.push(`callbackUrl=${encodeURIComponent(callbackUrl.toString())}`);
|
|
180
|
+
params.push(`name=${encodeURIComponent(name)}`);
|
|
181
|
+
params.push(`app_name=${encodeURIComponent(name)}`);
|
|
183
182
|
|
|
184
183
|
if (recipient) {
|
|
185
|
-
params.
|
|
186
|
-
params.set('pubKey', recipient); // Alias
|
|
184
|
+
params.push(`pubkey=${encodeURIComponent(recipient)}`);
|
|
187
185
|
} else if (this._pubkey) {
|
|
188
|
-
params.
|
|
189
|
-
params.set('pubKey', this._pubkey); // Alias
|
|
186
|
+
params.push(`pubkey=${encodeURIComponent(this._pubkey)}`);
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
const dataPart = content ? encodeURIComponent(content) : '
|
|
193
|
-
|
|
189
|
+
const dataPart = content ? encodeURIComponent(content) : 'get_public_key';
|
|
190
|
+
// Use nostrsigner: scheme as primary which is standard NIP-55
|
|
191
|
+
return `nostrsigner:${dataPart}?${params.join('&')}`;
|
|
194
192
|
}
|
|
195
193
|
|
|
196
194
|
public static parseResponse(): { type: string; id: string; result: string } | null {
|
|
@@ -200,12 +198,31 @@ export class AmberDirectSigner implements Signer {
|
|
|
200
198
|
|
|
201
199
|
const getParam = (name: string) => params.get(name) || hashParams.get(name);
|
|
202
200
|
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
let type = getParam('amberType') || getParam('type');
|
|
202
|
+
let id = getParam('amberId') || getParam('id');
|
|
205
203
|
const result = getParam('signature') || getParam('result') || getParam('pubKey') || getParam('pubkey');
|
|
206
204
|
|
|
205
|
+
// Fallback to localStorage pending if params are missing/merged
|
|
206
|
+
if (result && (!type || !id)) {
|
|
207
|
+
const pending = localStorage.getItem(PENDING_KEY);
|
|
208
|
+
if (pending) {
|
|
209
|
+
try {
|
|
210
|
+
const { id: pId, type: pType } = JSON.parse(pending);
|
|
211
|
+
if (!type) type = pType;
|
|
212
|
+
if (!id) id = pId;
|
|
213
|
+
console.log('AmberDirectSigner: Recovered type/id from localStorage');
|
|
214
|
+
} catch (e) {}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
207
218
|
if (type && result) {
|
|
208
|
-
// ID
|
|
219
|
+
// Handle "ID stuck to type" or typo cases
|
|
220
|
+
let finalType = type.toLowerCase();
|
|
221
|
+
if (finalType.includes('get_pub')) finalType = 'get_public_key';
|
|
222
|
+
else if (finalType.includes('sign_event')) finalType = 'sign_event';
|
|
223
|
+
else if (finalType.includes('encrypt')) finalType = finalType.includes('44') ? 'nip44_encrypt' : 'nip04_encrypt';
|
|
224
|
+
else if (finalType.includes('decrypt')) finalType = finalType.includes('44') ? 'nip44_decrypt' : 'nip04_decrypt';
|
|
225
|
+
|
|
209
226
|
const finalId = id || '';
|
|
210
227
|
|
|
211
228
|
// Clean up URL
|
|
@@ -215,8 +232,10 @@ export class AmberDirectSigner implements Signer {
|
|
|
215
232
|
});
|
|
216
233
|
newUrl.hash = '';
|
|
217
234
|
window.history.replaceState({}, '', newUrl.toString());
|
|
235
|
+
|
|
236
|
+
localStorage.removeItem(PENDING_KEY); // Done
|
|
218
237
|
|
|
219
|
-
return { type, id: finalId, result };
|
|
238
|
+
return { type: finalType, id: finalId, result };
|
|
220
239
|
}
|
|
221
240
|
return null;
|
|
222
241
|
}
|
|
@@ -88,10 +88,19 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
88
88
|
|
|
89
89
|
setTimeout(() => this.checkAmberResponse(), 100);
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
console.log('Window focused, checking Amber response');
|
|
91
|
+
const check = () => {
|
|
93
92
|
this.checkAmberResponse();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
window.addEventListener('focus', check);
|
|
96
|
+
window.addEventListener('visibilitychange', () => {
|
|
97
|
+
if (document.visibilityState === 'visible') check();
|
|
94
98
|
});
|
|
99
|
+
window.addEventListener('popstate', check);
|
|
100
|
+
window.addEventListener('hashchange', check);
|
|
101
|
+
|
|
102
|
+
// Periodic check as a safety net
|
|
103
|
+
setInterval(check, 1000);
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
private checkAmberResponse() {
|
|
@@ -99,10 +108,16 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
99
108
|
if (response) {
|
|
100
109
|
console.log('Amber response detected', response);
|
|
101
110
|
|
|
111
|
+
// Stop the "Connecting..." spinner
|
|
112
|
+
this.emit('onAuthUrl', { url: '' });
|
|
113
|
+
|
|
102
114
|
// Resolve pending promises if any (for non-reload cases)
|
|
103
|
-
AmberDirectSigner.resolvePending(response.id, response.type, response.result);
|
|
115
|
+
const resolved = AmberDirectSigner.resolvePending(response.id, response.type, response.result);
|
|
116
|
+
if (resolved) {
|
|
117
|
+
console.log('Resolved pending Amber promise via resolvePending');
|
|
118
|
+
}
|
|
104
119
|
|
|
105
|
-
if (response.type === 'get_public_key') {
|
|
120
|
+
if (response.type === 'get_public_key' || response.type.includes('pub')) {
|
|
106
121
|
const info: Info = {
|
|
107
122
|
pubkey: response.result,
|
|
108
123
|
authMethod: 'amber' as any,
|
|
@@ -181,6 +196,8 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
181
196
|
if (link && !iframeUrl) {
|
|
182
197
|
if (link === 'amber') {
|
|
183
198
|
const signer = new AmberDirectSigner();
|
|
199
|
+
const url = (signer as any).generateUrl('', 'get_public_key', Math.random().toString(36).substring(7));
|
|
200
|
+
this.emit('onAuthUrl', { url });
|
|
184
201
|
return (signer as any).getPublicKey(); // will redirect
|
|
185
202
|
}
|
|
186
203
|
window.open(link, '_blank', 'width=400,height=700');
|