ns-auth-sdk 1.10.0 → 1.12.0
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/README.md +99 -44
- package/dist/index.cjs +211 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +64 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +177 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -8
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Stoic Identity
|
|
2
2
|
|
|
3
3
|
_The simplest way of doing Auth with seamless and decentralized Key-Management for SSO, Authentication, Membership, and Profile-Management_
|
|
4
4
|
|
|
@@ -75,49 +75,32 @@ relayService.initialize(eventStore);
|
|
|
75
75
|
// Create a passkey (triggers biometric)
|
|
76
76
|
const credentialId = await authService.createPasskey('user@example.com');
|
|
77
77
|
|
|
78
|
-
// Create
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
// Create Key
|
|
79
|
+
const keyInfo = await authService.createKey(credentialId, undefined, {
|
|
80
|
+
username: 'user@example.com',
|
|
81
|
+
recoveryPassword: 'my-recovery-password', // optional
|
|
82
|
+
});
|
|
83
83
|
|
|
84
84
|
// Store keyInfo for later use
|
|
85
85
|
authService.setCurrentKeyInfo(keyInfo);
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
const event = {
|
|
92
|
-
kind: 1,
|
|
93
|
-
content: 'Hello Nostr!',
|
|
94
|
-
tags: [],
|
|
95
|
-
created_at: Math.floor(Date.now() / 1000),
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Sign - no password needed if key was cached at creation
|
|
99
|
-
const signed = await authService.signEvent(event);
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Password Fallback
|
|
88
|
+
### Password Fallback (when PRF unavailable)
|
|
103
89
|
|
|
104
|
-
When PRF is not supported
|
|
90
|
+
When PRF is not supported, username is required and password is used to derive the key:
|
|
105
91
|
|
|
106
92
|
```typescript
|
|
107
|
-
import { AuthService, checkPRFSupport } from 'ns-auth-sdk';
|
|
108
|
-
|
|
109
93
|
// Check if password fallback is needed
|
|
110
|
-
const prfSupported = await checkPRFSupport();
|
|
94
|
+
const prfSupported = await authService.checkPRFSupport();
|
|
111
95
|
|
|
112
96
|
if (!prfSupported) {
|
|
113
|
-
|
|
97
|
+
// Username is REQUIRED when PRF unavailable
|
|
98
|
+
const keyInfo = await authService.createKey(undefined, userPassword, {
|
|
99
|
+
username: 'user@example.com',
|
|
100
|
+
});
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
//
|
|
117
|
-
const keyInfo = await authService.createNostrKey(credentialId, password);
|
|
118
|
-
|
|
119
|
-
// First sign: password used to decrypt and cache key
|
|
120
|
-
// Subsequent signs: uses cached key (no password needed)
|
|
103
|
+
// Sign events
|
|
121
104
|
const signedEvent = await authService.signEvent(event);
|
|
122
105
|
```
|
|
123
106
|
|
|
@@ -126,36 +109,66 @@ const signedEvent = await authService.signEvent(event);
|
|
|
126
109
|
#### Methods
|
|
127
110
|
|
|
128
111
|
- `createPasskey(username?: string): Promise<Uint8Array>` - Create a new passkey
|
|
129
|
-
- `createKey(credentialId?: Uint8Array, password?: string): Promise<KeyInfo>` - Create key from passkey (auto-detects PRF support, uses password fallback if needed)
|
|
112
|
+
- `createKey(credentialId?: Uint8Array, password?: string, options?: KeyOptions): Promise<KeyInfo>` - Create key from passkey (auto-detects PRF support, uses password fallback if needed)
|
|
130
113
|
- `getPublicKey(): Promise<string>` - Get current public key
|
|
131
|
-
- `signEvent(event: Event
|
|
114
|
+
- `signEvent(event: Event): Promise<Event>` - Sign an event
|
|
132
115
|
- `getCurrentKeyInfo(): KeyInfo | null` - Get current key info
|
|
133
116
|
- `setCurrentKeyInfo(keyInfo: KeyInfo): void` - Set current key info
|
|
134
117
|
- `hasKeyInfo(): boolean` - Check if key info exists
|
|
135
118
|
- `clearStoredKeyInfo(): void` - Clear stored key info
|
|
136
|
-
- `checkPRFSupport(): Promise<boolean>` - Check if PRF is supported
|
|
119
|
+
- `checkPRFSupport(): Promise<boolean>` - Check if PRF is supported
|
|
120
|
+
- `deriveSaltFromUsername(username?: string): Promise<string>` - Derive salt from username (SHA-256)
|
|
137
121
|
|
|
138
|
-
####
|
|
122
|
+
#### Recovery Methods
|
|
123
|
+
|
|
124
|
+
- `addPasswordRecovery(password: string): Promise<KeyInfo>` - Add password recovery to an existing PRF key
|
|
125
|
+
- `activateWithPassword(password: string, newCredentialId: Uint8Array): Promise<KeyInfo>` - Recover using password with a new passkey credential ID from a new device
|
|
126
|
+
- `getRecoveryForKind0(): RecoveryData | null` - Get recovery data for publishing to kind-0
|
|
127
|
+
- `parseRecoveryTag(tags: string[][]): KeyRecovery | null` - Parse recovery tag from event
|
|
128
|
+
- `verifyRecoverySignature(kind0: Event): Promise<boolean>` - Verify recovery signature (async)
|
|
129
|
+
|
|
130
|
+
#### KeyOptions
|
|
139
131
|
|
|
140
132
|
```typescript
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
saltBase64: string;
|
|
146
|
-
ivBase64: string;
|
|
133
|
+
interface KeyOptions {
|
|
134
|
+
username?: string; // Required when PRF unavailable
|
|
135
|
+
password?: string; // Required when PRF unavailable
|
|
136
|
+
recoveryPassword?: string; // Password for recovery (optional)
|
|
147
137
|
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### RecoveryData
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
interface RecoveryData {
|
|
144
|
+
recoveryPubkey: string;
|
|
145
|
+
recoverySalt: string;
|
|
146
|
+
createdAt?: number;
|
|
147
|
+
signature?: string; // Schnorr signature from recovery key signing the current pubkey
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Types
|
|
148
152
|
|
|
149
|
-
|
|
153
|
+
```typescript
|
|
154
|
+
// KeyInfo with optional recovery
|
|
150
155
|
interface KeyInfo {
|
|
151
156
|
credentialId: string;
|
|
152
157
|
pubkey: string;
|
|
153
158
|
salt: string;
|
|
154
159
|
username?: string;
|
|
155
|
-
|
|
160
|
+
recovery?: KeyRecovery;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Key recovery configuration
|
|
164
|
+
interface KeyRecovery {
|
|
165
|
+
recoveryPubkey: string;
|
|
166
|
+
recoverySalt: string;
|
|
167
|
+
createdAt?: number;
|
|
168
|
+
signature?: string; // Schnorr signature from recovery key signing the current pubkey
|
|
156
169
|
}
|
|
157
170
|
|
|
158
|
-
// Sign options
|
|
171
|
+
// Sign options
|
|
159
172
|
interface SignOptions {
|
|
160
173
|
clearMemory?: boolean;
|
|
161
174
|
tags?: string[][];
|
|
@@ -163,6 +176,48 @@ interface SignOptions {
|
|
|
163
176
|
}
|
|
164
177
|
```
|
|
165
178
|
|
|
179
|
+
### Recovery Flow
|
|
180
|
+
|
|
181
|
+
The SDK supports password-based recovery for passkey-protected keys. When creating a key, you can optionally provide a recovery password:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Create key with recovery enabled
|
|
185
|
+
const keyInfo = await authService.createKey(credentialId, undefined, {
|
|
186
|
+
username: 'user@example.com',
|
|
187
|
+
recoveryPassword: 'my-recovery-password',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// The recovery data is stored in kind-0 tags:
|
|
191
|
+
// ["r", recoveryPubkey, recoverySalt, createdAt, signature]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Recovery on a new device:**
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// On new device - create new passkey first
|
|
198
|
+
const newCredentialId = await authService.createPasskey('user@example.com');
|
|
199
|
+
|
|
200
|
+
// Recover using password
|
|
201
|
+
const keyInfo = await authService.activateWithPassword('my-recovery-password', newCredentialId);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Verification:**
|
|
205
|
+
|
|
206
|
+
Anyone can verify ownership by fetching the kind-0 and checking the signature:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { parseRecoveryTag, verifyRecoverySignature } from 'ns-auth-sdk';
|
|
210
|
+
|
|
211
|
+
// Fetch kind-0 from relay
|
|
212
|
+
const kind0 = await relayService.fetchProfile(pubkey);
|
|
213
|
+
|
|
214
|
+
// Verify recovery signature (async)
|
|
215
|
+
const isValid = await verifyRecoverySignature(kind0);
|
|
216
|
+
if (isValid) {
|
|
217
|
+
console.log('Recovery key holder controls this identity');
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
166
221
|
#### Configuration Options
|
|
167
222
|
|
|
168
223
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
1
28
|
let applesauce_core_helpers = require("applesauce-core/helpers");
|
|
29
|
+
let _noble_secp256k1 = require("@noble/secp256k1");
|
|
30
|
+
_noble_secp256k1 = __toESM(_noble_secp256k1);
|
|
2
31
|
|
|
3
32
|
//#region src/utils/utils.ts
|
|
4
33
|
/**
|
|
@@ -391,7 +420,55 @@ async function getPublicKeyFromPassword(password, salt = DEFAULT_SALT) {
|
|
|
391
420
|
* Nosskey class for Passkey-Derived Nostr Identity
|
|
392
421
|
* @packageDocumentation
|
|
393
422
|
*/
|
|
394
|
-
|
|
423
|
+
async function deriveSaltFromUsername(username) {
|
|
424
|
+
if (!username) return "";
|
|
425
|
+
const msgBuffer = new TextEncoder().encode(username.toLowerCase().trim());
|
|
426
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
|
|
427
|
+
return bytesToHex(new Uint8Array(hashBuffer));
|
|
428
|
+
}
|
|
429
|
+
function parseRecoveryTag(tags) {
|
|
430
|
+
const recoveryTag = tags.find((tag) => tag[0] === "r");
|
|
431
|
+
if (!recoveryTag || recoveryTag.length < 3) return null;
|
|
432
|
+
const createdAt = recoveryTag[3] ? parseInt(recoveryTag[3], 10) : void 0;
|
|
433
|
+
return {
|
|
434
|
+
recoveryPubkey: recoveryTag[1],
|
|
435
|
+
recoverySalt: recoveryTag[2],
|
|
436
|
+
createdAt: createdAt || void 0,
|
|
437
|
+
signature: recoveryTag[4] || void 0
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function createRecoveryTag(recovery) {
|
|
441
|
+
return [
|
|
442
|
+
"r",
|
|
443
|
+
recovery.recoveryPubkey,
|
|
444
|
+
recovery.recoverySalt,
|
|
445
|
+
recovery.createdAt?.toString() || "",
|
|
446
|
+
recovery.signature || ""
|
|
447
|
+
];
|
|
448
|
+
}
|
|
449
|
+
function getRecoverySignature(kind0) {
|
|
450
|
+
if (!parseRecoveryTag(kind0.tags || [])) return null;
|
|
451
|
+
const tag = kind0.tags?.find((t) => t[0] === "r");
|
|
452
|
+
return tag && tag.length > 4 ? tag[4] || null : null;
|
|
453
|
+
}
|
|
454
|
+
async function verifyRecoverySignature(kind0) {
|
|
455
|
+
try {
|
|
456
|
+
if (!parseRecoveryTag(kind0.tags || [])) return false;
|
|
457
|
+
const signature = getRecoverySignature(kind0);
|
|
458
|
+
if (!signature || !kind0.pubkey) return false;
|
|
459
|
+
const messageHash = await sha256(kind0.pubkey);
|
|
460
|
+
const signatureBytes = hexToBytes(signature);
|
|
461
|
+
const pubkeyBytes = hexToBytes(kind0.pubkey);
|
|
462
|
+
return _noble_secp256k1.verify(signatureBytes, messageHash, pubkeyBytes);
|
|
463
|
+
} catch (e) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function sha256(message) {
|
|
468
|
+
const msgBuffer = new TextEncoder().encode(message);
|
|
469
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
|
|
470
|
+
return new Uint8Array(hashBuffer);
|
|
471
|
+
}
|
|
395
472
|
/**
|
|
396
473
|
* Nosskey - Passkey-Derived Nostr Keys
|
|
397
474
|
*/
|
|
@@ -576,11 +653,17 @@ var NosskeyManager = class {
|
|
|
576
653
|
* @param options
|
|
577
654
|
*/
|
|
578
655
|
async createKey(credentialId, password, options = {}) {
|
|
579
|
-
|
|
580
|
-
|
|
656
|
+
const prfSupported = await this.checkPRFSupport();
|
|
657
|
+
let keyInfo;
|
|
658
|
+
if (prfSupported) {
|
|
659
|
+
keyInfo = await this.createPrfNostrKey(credentialId, options);
|
|
660
|
+
if (options.recoveryPassword) keyInfo = await this.addPasswordRecovery(options.recoveryPassword, credentialId);
|
|
661
|
+
} else {
|
|
581
662
|
if (!password) throw new Error("Password is required when PRF is not supported");
|
|
582
|
-
|
|
663
|
+
if (!options.username) throw new Error("Username is required when PRF is not supported");
|
|
664
|
+
keyInfo = await this.createPasswordProtectedNostrKey(password, options);
|
|
583
665
|
}
|
|
666
|
+
return keyInfo;
|
|
584
667
|
}
|
|
585
668
|
/**
|
|
586
669
|
* Create Nostr key using PRF (standard passkey flow)
|
|
@@ -590,10 +673,11 @@ var NosskeyManager = class {
|
|
|
590
673
|
if (sk.every((byte) => byte === 0)) throw new Error("Invalid PRF output: all zeros");
|
|
591
674
|
bytesToHex(sk);
|
|
592
675
|
const publicKey = (0, applesauce_core_helpers.getPublicKey)(sk);
|
|
676
|
+
const salt = await deriveSaltFromUsername(options.username);
|
|
593
677
|
const keyInfo = {
|
|
594
678
|
credentialId: bytesToHex(credentialId || responseId),
|
|
595
679
|
pubkey: publicKey,
|
|
596
|
-
salt
|
|
680
|
+
salt,
|
|
597
681
|
...options.username && { username: options.username }
|
|
598
682
|
};
|
|
599
683
|
if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
@@ -605,11 +689,12 @@ var NosskeyManager = class {
|
|
|
605
689
|
* @param options
|
|
606
690
|
*/
|
|
607
691
|
async createPasswordProtectedNostrKey(password, options = {}) {
|
|
608
|
-
const
|
|
692
|
+
const salt = await deriveSaltFromUsername(options.username);
|
|
693
|
+
const pubkey = await getPublicKeyFromPassword(password, salt);
|
|
609
694
|
return {
|
|
610
695
|
credentialId: bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16))),
|
|
611
696
|
pubkey,
|
|
612
|
-
salt
|
|
697
|
+
salt,
|
|
613
698
|
...options.username && { username: options.username }
|
|
614
699
|
};
|
|
615
700
|
}
|
|
@@ -681,6 +766,90 @@ var NosskeyManager = class {
|
|
|
681
766
|
return isPrfSupported();
|
|
682
767
|
}
|
|
683
768
|
/**
|
|
769
|
+
* Add password recovery to an existing PRF key
|
|
770
|
+
* @param password Password for recovery key
|
|
771
|
+
* @param currentCredentialId Current passkey credential ID
|
|
772
|
+
*/
|
|
773
|
+
async addPasswordRecovery(password, currentCredentialId) {
|
|
774
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
775
|
+
if (!keyInfo) throw new Error("No current KeyInfo set");
|
|
776
|
+
if (keyInfo.passwordProtectedBundle) throw new Error("Password recovery already exists for password-derived key");
|
|
777
|
+
if (keyInfo.recovery) throw new Error("Recovery already configured");
|
|
778
|
+
if (!(currentCredentialId || (keyInfo.credentialId ? hexToBytes(keyInfo.credentialId) : void 0))) throw new Error("Credential ID required");
|
|
779
|
+
const recoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
780
|
+
const recoveryPubkey = await getPublicKeyFromPassword(password, recoverySalt);
|
|
781
|
+
const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
|
|
782
|
+
const signature = this.#signWithKey(recoverySk, keyInfo.pubkey);
|
|
783
|
+
this.#clearKey(recoverySk);
|
|
784
|
+
const updatedKeyInfo = {
|
|
785
|
+
...keyInfo,
|
|
786
|
+
recovery: {
|
|
787
|
+
recoveryPubkey,
|
|
788
|
+
recoverySalt,
|
|
789
|
+
createdAt: Date.now(),
|
|
790
|
+
signature
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
794
|
+
return updatedKeyInfo;
|
|
795
|
+
}
|
|
796
|
+
#signWithKey(sk, message) {
|
|
797
|
+
return (0, applesauce_core_helpers.finalizeEvent)({
|
|
798
|
+
kind: 0,
|
|
799
|
+
content: "",
|
|
800
|
+
tags: [],
|
|
801
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
802
|
+
}, sk).sig;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Activate recovery using password
|
|
806
|
+
* Requires new credential ID from new device
|
|
807
|
+
* @param password Password for recovery key
|
|
808
|
+
* @param newCredentialId New passkey credential ID (required)
|
|
809
|
+
*/
|
|
810
|
+
async activateWithPassword(password, newCredentialId) {
|
|
811
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
812
|
+
if (!keyInfo) throw new Error("No current KeyInfo set");
|
|
813
|
+
if (!keyInfo.recovery) throw new Error("No recovery key configured");
|
|
814
|
+
if (!newCredentialId) throw new Error("New credential ID is required for recovery");
|
|
815
|
+
const { recoveryPubkey, recoverySalt } = keyInfo.recovery;
|
|
816
|
+
if (await getPublicKeyFromPassword(password, recoverySalt) !== recoveryPubkey) throw new Error("Invalid recovery password");
|
|
817
|
+
const { secret: newSk } = await getPrfSecret(newCredentialId, this.#prfOptions);
|
|
818
|
+
const newPubkey = (0, applesauce_core_helpers.getPublicKey)(newSk);
|
|
819
|
+
this.#clearKey(newSk);
|
|
820
|
+
const newRecoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
821
|
+
const newRecoveryPubkey = await getPublicKeyFromPassword(password, newRecoverySalt);
|
|
822
|
+
const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
|
|
823
|
+
const signature = this.#signWithKey(recoverySk, newPubkey);
|
|
824
|
+
this.#clearKey(recoverySk);
|
|
825
|
+
const updatedKeyInfo = {
|
|
826
|
+
credentialId: bytesToHex(newCredentialId),
|
|
827
|
+
pubkey: newPubkey,
|
|
828
|
+
salt: keyInfo.salt,
|
|
829
|
+
recovery: {
|
|
830
|
+
recoveryPubkey: newRecoveryPubkey,
|
|
831
|
+
recoverySalt: newRecoverySalt,
|
|
832
|
+
createdAt: Date.now(),
|
|
833
|
+
signature
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
837
|
+
return updatedKeyInfo;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Get the recovery data for publishing to kind-0
|
|
841
|
+
*/
|
|
842
|
+
getRecoveryForKind0() {
|
|
843
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
844
|
+
if (!keyInfo || !keyInfo.recovery) return null;
|
|
845
|
+
return {
|
|
846
|
+
recoveryPubkey: keyInfo.recovery.recoveryPubkey,
|
|
847
|
+
recoverySalt: keyInfo.recovery.recoverySalt,
|
|
848
|
+
createdAt: keyInfo.recovery.createdAt,
|
|
849
|
+
signature: keyInfo.recovery.signature
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
684
853
|
* @param key
|
|
685
854
|
*/
|
|
686
855
|
#clearKey(key) {
|
|
@@ -852,9 +1021,13 @@ var AuthService = class {
|
|
|
852
1021
|
/**
|
|
853
1022
|
* Create a new Nostr key from a credential ID
|
|
854
1023
|
* Automatically uses password fallback if PRF is not supported
|
|
1024
|
+
* @param credentialId Passkey credential ID
|
|
1025
|
+
* @param password Password (required if PRF not supported)
|
|
1026
|
+
* @param options.username Username for the key
|
|
1027
|
+
* @param options.recoveryPassword Password for recovery (enables recovery on new device)
|
|
855
1028
|
*/
|
|
856
|
-
async createKey(credentialId, password) {
|
|
857
|
-
return await this.getManager().createKey(credentialId, password);
|
|
1029
|
+
async createKey(credentialId, password, options) {
|
|
1030
|
+
return await this.getManager().createKey(credentialId, password, options);
|
|
858
1031
|
}
|
|
859
1032
|
/**
|
|
860
1033
|
* Check if PRF is supported, otherwise password fallback is needed
|
|
@@ -904,6 +1077,28 @@ var AuthService = class {
|
|
|
904
1077
|
async isPrfSupported() {
|
|
905
1078
|
return this.checkPRFSupport();
|
|
906
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Add password recovery to an existing PRF key
|
|
1082
|
+
* @param password Password for recovery key
|
|
1083
|
+
*/
|
|
1084
|
+
async addPasswordRecovery(password) {
|
|
1085
|
+
return await this.getManager().addPasswordRecovery(password);
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Activate recovery using password
|
|
1089
|
+
* Requires new credential ID from new device
|
|
1090
|
+
* @param password Password for recovery key
|
|
1091
|
+
* @param newCredentialId New passkey credential ID (required)
|
|
1092
|
+
*/
|
|
1093
|
+
async activateWithPassword(password, newCredentialId) {
|
|
1094
|
+
return await this.getManager().activateWithPassword(password, newCredentialId);
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Get recovery data for kind-0
|
|
1098
|
+
*/
|
|
1099
|
+
getRecoveryForKind0() {
|
|
1100
|
+
return this.getManager().getRecoveryForKind0();
|
|
1101
|
+
}
|
|
907
1102
|
};
|
|
908
1103
|
|
|
909
1104
|
//#endregion
|
|
@@ -1297,14 +1492,21 @@ exports.aesGcmEncrypt = aesGcmEncrypt;
|
|
|
1297
1492
|
exports.bytesToHex = bytesToHex;
|
|
1298
1493
|
exports.checkPRFSupport = checkPRFSupport;
|
|
1299
1494
|
exports.createPasskey = createPasskey;
|
|
1495
|
+
exports.createRecoveryTag = createRecoveryTag;
|
|
1300
1496
|
exports.deriveAesGcmKey = deriveAesGcmKey;
|
|
1497
|
+
exports.deriveNostrPrivateKey = deriveNostrPrivateKey;
|
|
1498
|
+
exports.deriveSaltFromUsername = deriveSaltFromUsername;
|
|
1301
1499
|
exports.generatePasswordProtectedKey = generatePasswordProtectedKey;
|
|
1302
1500
|
exports.getPrfSecret = getPrfSecret;
|
|
1501
|
+
exports.getPublicKeyFromPassword = getPublicKeyFromPassword;
|
|
1502
|
+
exports.getRecoverySignature = getRecoverySignature;
|
|
1303
1503
|
exports.hexToBytes = hexToBytes;
|
|
1304
1504
|
exports.importPublicKeyFromBundle = importPublicKeyFromBundle;
|
|
1305
1505
|
exports.isPrfSupported = isPrfSupported;
|
|
1506
|
+
exports.parseRecoveryTag = parseRecoveryTag;
|
|
1306
1507
|
exports.registerDummyPasskey = registerDummyPasskey;
|
|
1307
1508
|
exports.unwrapPasswordProtectedPrivateKey = unwrapPasswordProtectedPrivateKey;
|
|
1509
|
+
exports.verifyRecoverySignature = verifyRecoverySignature;
|
|
1308
1510
|
var applesauce_core = require("applesauce-core");
|
|
1309
1511
|
Object.keys(applesauce_core).forEach(function (k) {
|
|
1310
1512
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|