@cryptforge/auth 0.1.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 ADDED
@@ -0,0 +1,914 @@
1
+ # @cryptforge/auth
2
+
3
+ Browser-compatible authentication and key management for cryptocurrency wallets. Built on industry-standard BIP39/BIP44 with secure keystore encryption.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure Key Management** - BIP39 mnemonics encrypted with PBKDF2 + AES-256-CBC
8
+ - 🌐 **Browser-First** - Zero configuration, works everywhere (browser, Node.js, Electron, React Native)
9
+ - 🔗 **Multi-Blockchain** - Pluggable adapter system for any blockchain
10
+ - 👤 **Multi-Identity** - Manage multiple wallets with separate keystores
11
+ - 🔒 **Auto-Lock** - Configurable session timeouts for security
12
+ - ⏱️ **Key Expiration** - Automatic tracking with live countdowns
13
+ - 💾 **IndexedDB Storage** - Encrypted keystores persisted locally
14
+ - 🎯 **Type-Safe** - Full TypeScript support
15
+ - 📦 **Lightweight** - ~26 KB minified
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @cryptforge/auth @cryptforge/blockchain-evm @cryptforge/blockchain-btc
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { createAuthClient } from "@cryptforge/auth";
27
+ import { EVMAdapter } from "@cryptforge/blockchain-evm";
28
+ import { BitcoinAdapter } from "@cryptforge/blockchain-btc";
29
+
30
+ // Create auth client
31
+ const auth = createAuthClient();
32
+
33
+ // Register blockchain adapters
34
+ auth.registerAdapter(
35
+ "ethereum",
36
+ new EVMAdapter({
37
+ chainData: { name: "Ethereum", symbol: "ETH", cmc_id: 1027 },
38
+ coinType: 60,
39
+ })
40
+ );
41
+
42
+ auth.registerAdapter("bitcoin", new BitcoinAdapter());
43
+
44
+ // Generate mnemonic
45
+ const mnemonic = auth.generateMnemonic({ wordCount: 12 });
46
+
47
+ // Create identity
48
+ const { identity, keys } = await auth.createIdentity({
49
+ mnemonic,
50
+ password: "secure-password",
51
+ label: "Personal Wallet",
52
+ chainId: "ethereum",
53
+ });
54
+
55
+ console.log("Address:", keys.address);
56
+ ```
57
+
58
+ ## Usage Guide
59
+
60
+ ### First Time Setup
61
+
62
+ #### 1. Generate and Backup Mnemonic
63
+
64
+ ```typescript
65
+ // Generate a new mnemonic
66
+ const mnemonic = auth.generateMnemonic({ wordCount: 12 });
67
+ // Example: "abandon ability able about above absent absorb abstract absurd abuse access accident"
68
+
69
+ // ⚠️ CRITICAL: Display to user and require backup confirmation
70
+ // User must write down or securely store the mnemonic
71
+ ```
72
+
73
+ #### 2. Create Identity
74
+
75
+ ```typescript
76
+ const { identity, keys } = await auth.createIdentity({
77
+ mnemonic: mnemonic,
78
+ password: "user-chosen-password",
79
+ label: "Personal Wallet",
80
+ metadata: {
81
+ createdBy: "MyApp",
82
+ version: "1.0.0",
83
+ },
84
+ chainId: "ethereum", // Optional: unlock with specific chain
85
+ });
86
+
87
+ // Identity created and encrypted in IndexedDB
88
+ // If chainId provided, wallet is unlocked and ready to use
89
+ ```
90
+
91
+ ### Returning User Flow
92
+
93
+ #### 1. List Available Identities
94
+
95
+ ```typescript
96
+ const identities = await auth.listIdentities();
97
+ // [
98
+ // {
99
+ // id: 'identity_A1B2C3D4',
100
+ // label: 'Personal Wallet',
101
+ // fingerprint: 'A1B2C3D4',
102
+ // createdAt: Date,
103
+ // lastAccess: Date
104
+ // }
105
+ // ]
106
+ ```
107
+
108
+ #### 2. Select and Unlock
109
+
110
+ ```typescript
111
+ // Select an identity
112
+ await auth.switchIdentity(identities[0].id);
113
+
114
+ // Unlock with password
115
+ const { keys } = await auth.unlock({
116
+ password: "user-chosen-password",
117
+ chainId: "ethereum",
118
+ duration: 10 * 60 * 1000, // Auto-lock after 10 minutes
119
+ });
120
+
121
+ // Wallet is now unlocked and ready to sign
122
+ console.log("Address:", keys.address);
123
+ console.log("Expires in:", auth.currentExpiresIn, "seconds");
124
+ ```
125
+
126
+ ## Core Features
127
+
128
+ ### Identity Management
129
+
130
+ #### Create Identity
131
+
132
+ ```typescript
133
+ const { identity, keys } = await auth.createIdentity({
134
+ mnemonic: "word1 word2 ... word12",
135
+ password: "secure-password",
136
+ label: "Trading Wallet",
137
+ metadata: { purpose: "trading" },
138
+ chainId: "ethereum", // Optional
139
+ });
140
+ ```
141
+
142
+ #### Import Identity
143
+
144
+ ```typescript
145
+ // From mnemonic backup
146
+ const { identity } = await auth.importIdentity(
147
+ "word1 word2 ... word12",
148
+ "new-password",
149
+ "mnemonic"
150
+ );
151
+
152
+ // From keystore JSON
153
+ const { identity } = await auth.importIdentity(
154
+ keystoreJsonString,
155
+ "password",
156
+ "keystore"
157
+ );
158
+ ```
159
+
160
+ #### Export Identity
161
+
162
+ ```typescript
163
+ // Export as mnemonic (for backup)
164
+ const { data: mnemonic } = await auth.exportIdentity(identity.id, {
165
+ password: "password",
166
+ format: "mnemonic",
167
+ });
168
+
169
+ // Export as keystore JSON
170
+ const { data: keystore } = await auth.exportIdentity(identity.id, {
171
+ password: "password",
172
+ format: "keystore",
173
+ });
174
+ ```
175
+
176
+ #### Delete Identity
177
+
178
+ ```typescript
179
+ // Permanently delete (requires password)
180
+ await auth.deleteIdentity(identity.id, "password");
181
+ // ⚠️ Make sure mnemonic is backed up first!
182
+ ```
183
+
184
+ #### Update Identity
185
+
186
+ ```typescript
187
+ await auth.updateIdentity(identity.id, {
188
+ label: "Updated Wallet Name",
189
+ metadata: { theme: "dark" },
190
+ });
191
+ ```
192
+
193
+ #### Change Password
194
+
195
+ ```typescript
196
+ await auth.changePassword(identity.id, "old-password", "new-password");
197
+ ```
198
+
199
+ ### Session Management
200
+
201
+ #### Unlock
202
+
203
+ ```typescript
204
+ const { keys } = await auth.unlock({
205
+ password: "password",
206
+ chainId: "ethereum",
207
+ duration: 15 * 60 * 1000, // Optional: auto-lock after 15 min
208
+ });
209
+
210
+ // Access derived keys
211
+ console.log("Address:", keys.address);
212
+ console.log("Public Key:", keys.publicKeyHex);
213
+ console.log("Derivation Path:", keys.derivationPath);
214
+ console.log("Expires At:", keys.expiresAt);
215
+ ```
216
+
217
+ #### Lock
218
+
219
+ ```typescript
220
+ await auth.lock();
221
+ // Clears keys from memory
222
+ // Keystore remains encrypted in IndexedDB
223
+ ```
224
+
225
+ ### Blockchain Operations
226
+
227
+ #### Switch Chain
228
+
229
+ ```typescript
230
+ // Switch to different blockchain (if already unlocked)
231
+ await auth.switchChain("bitcoin");
232
+
233
+ // Switch when locked (requires password)
234
+ await auth.switchChain("bitcoin", "password");
235
+ ```
236
+
237
+ #### Get Addresses
238
+
239
+ ```typescript
240
+ // Get multiple addresses for a chain
241
+ const addresses = await auth.getAddresses("ethereum", 0, 5);
242
+ // [
243
+ // { address: '0x...', path: "m/44'/60'/0'/0/0", index: 0 },
244
+ // { address: '0x...', path: "m/44'/60'/0'/0/1", index: 1 },
245
+ // ...
246
+ // ]
247
+
248
+ // Get specific address by index
249
+ const { address, publicKey, derivationPath } = await auth.getAddressForChain(
250
+ "bitcoin",
251
+ 0
252
+ );
253
+ ```
254
+
255
+ #### Find Used Addresses (Account Discovery)
256
+
257
+ ```typescript
258
+ // Find all addresses with balance
259
+ const usedAddresses = await auth.findUsedAddresses(
260
+ "ethereum",
261
+ async (address) => {
262
+ // Your balance checking logic
263
+ const balance = await checkBalance(address);
264
+ return balance > 0;
265
+ }
266
+ );
267
+ // Scans with BIP44 gap limit of 20
268
+ ```
269
+
270
+ ### Cryptographic Operations
271
+
272
+ #### Sign Message
273
+
274
+ ```typescript
275
+ const { signature, address, publicKey } = await auth.signMessage({
276
+ message: "Hello CryptForge!",
277
+ });
278
+
279
+ // Sign with different derivation path
280
+ const result = await auth.signMessage({
281
+ message: "Custom path",
282
+ derivationPath: "m/44'/60'/0'/0/1",
283
+ });
284
+ ```
285
+
286
+ #### Sign Transaction
287
+
288
+ ```typescript
289
+ const { signedTransaction, signature } = await auth.signTransaction({
290
+ transaction: {
291
+ to: "0x...",
292
+ value: "1000000000000000000", // 1 ETH in wei
293
+ gasLimit: 21000,
294
+ },
295
+ });
296
+
297
+ // Sign with different key
298
+ const result = await auth.signTransaction({
299
+ transaction: tx,
300
+ derivationPath: "m/44'/60'/0'/0/5",
301
+ });
302
+ ```
303
+
304
+ #### Verify Signature
305
+
306
+ ```typescript
307
+ const isValid = await auth.verifySignature(
308
+ "Hello CryptForge!",
309
+ signature,
310
+ publicKey
311
+ );
312
+ ```
313
+
314
+ ### Key Management
315
+
316
+ #### Rotate Keys
317
+
318
+ ```typescript
319
+ // Rotate to next address index
320
+ const { keys } = await auth.rotateKeys();
321
+ console.log("New address:", keys.address);
322
+
323
+ // Rotate to custom derivation path
324
+ const { keys } = await auth.rotateKeys("m/44'/60'/0'/0/5");
325
+ ```
326
+
327
+ #### Derive Custom Key
328
+
329
+ ```typescript
330
+ // One-time key derivation (not stored in session)
331
+ const { privateKey, publicKey, address, path } = await auth.deriveKey({
332
+ path: "m/44'/60'/1'/0/0", // Different account
333
+ });
334
+ ```
335
+
336
+ ## State Management
337
+
338
+ ### Getters
339
+
340
+ ```typescript
341
+ // Identity state
342
+ auth.currentIdentity; // Identity | null
343
+ auth.hasIdentity; // boolean
344
+
345
+ // Keys state
346
+ auth.currentKeys; // Keys | null
347
+ auth.currentAddress; // string | null
348
+ auth.currentPublicKey; // string | null (hex)
349
+
350
+ // Chain state
351
+ auth.currentChain; // Chain | null
352
+
353
+ // Lock state
354
+ auth.isLocked; // boolean
355
+ auth.isUnlocked; // boolean
356
+
357
+ // Expiration state
358
+ auth.currentExpiresAt; // Date | null
359
+ auth.currentExpiresIn; // number | null (seconds remaining)
360
+
361
+ // Available chains
362
+ auth.getRegisteredChains(); // string[]
363
+ ```
364
+
365
+ ### Event Subscription
366
+
367
+ ```typescript
368
+ const unsubscribe = auth.onAuthStateChange((event, keys) => {
369
+ console.log("Event:", event);
370
+
371
+ switch (event) {
372
+ case "IDENTITY_CREATED":
373
+ console.log("New identity created");
374
+ break;
375
+ case "UNLOCKED":
376
+ console.log("Wallet unlocked:", keys?.address);
377
+ break;
378
+ case "LOCKED":
379
+ console.log("Wallet locked");
380
+ break;
381
+ case "CHAIN_SWITCHED":
382
+ console.log("Switched to:", keys?.chain.name);
383
+ break;
384
+ case "KEYS_ROTATED":
385
+ console.log("Keys rotated to:", keys?.address);
386
+ break;
387
+ case "KEYS_EXPIRED":
388
+ console.log("Keys expired, please unlock again");
389
+ break;
390
+ // ... other events
391
+ }
392
+ });
393
+
394
+ // Unsubscribe when done
395
+ unsubscribe();
396
+ ```
397
+
398
+ ### Available Events
399
+
400
+ - `IDENTITY_CREATED` - New identity created
401
+ - `IDENTITY_RESTORED` - Identity imported/restored
402
+ - `IDENTITY_SWITCHED` - Switched to different identity
403
+ - `IDENTITY_UPDATED` - Identity metadata updated
404
+ - `IDENTITY_DELETED` - Identity removed
405
+ - `PASSWORD_CHANGED` - Password updated
406
+ - `UNLOCKED` - Wallet unlocked with keys
407
+ - `LOCKED` - Wallet locked
408
+ - `KEYS_ROTATED` - Keys rotated to new address
409
+ - `KEYS_EXPIRED` - Keys passed expiration time
410
+ - `KEY_DERIVED` - Custom key derived
411
+ - `CHAIN_SWITCHED` - Switched to different blockchain
412
+
413
+ ## Blockchain Adapters
414
+
415
+ ### Registering Adapters
416
+
417
+ ```typescript
418
+ import { EVMAdapter } from "@cryptforge/blockchain-evm";
419
+ import { BitcoinAdapter } from "@cryptforge/blockchain-btc";
420
+
421
+ // Ethereum
422
+ auth.registerAdapter(
423
+ "ethereum",
424
+ new EVMAdapter({
425
+ chainData: { name: "Ethereum", symbol: "ETH", cmc_id: 1027 },
426
+ coinType: 60,
427
+ networks: {
428
+ mainnet: {
429
+ name: "Ethereum Mainnet",
430
+ rpcUrl: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
431
+ chainId: 1,
432
+ },
433
+ },
434
+ })
435
+ );
436
+
437
+ // Bitcoin
438
+ auth.registerAdapter("bitcoin", new BitcoinAdapter());
439
+
440
+ // Polygon (EVM-compatible)
441
+ auth.registerAdapter(
442
+ "polygon",
443
+ new EVMAdapter({
444
+ chainData: { name: "Polygon", symbol: "MATIC", cmc_id: 3890 },
445
+ coinType: 60,
446
+ networks: {
447
+ mainnet: {
448
+ name: "Polygon Mainnet",
449
+ rpcUrl: "https://polygon-rpc.com",
450
+ chainId: 137,
451
+ },
452
+ },
453
+ })
454
+ );
455
+ ```
456
+
457
+ ### Creating Custom Adapters
458
+
459
+ Implement the `BlockchainAdapter` interface to add support for new blockchains:
460
+
461
+ ```typescript
462
+ import type { BlockchainAdapter, KeyData, ChainData } from "@cryptforge/core";
463
+
464
+ class SolanaAdapter implements BlockchainAdapter {
465
+ readonly chainData: ChainData = {
466
+ name: "Solana",
467
+ symbol: "SOL",
468
+ cmc_id: 5426,
469
+ };
470
+
471
+ async deriveKeys(mnemonic: string): Promise<KeyData> {
472
+ // Implement Solana key derivation
473
+ }
474
+
475
+ async deriveKeysAtIndex(mnemonic: string, index: number): Promise<KeyData> {
476
+ // Implement indexed derivation
477
+ }
478
+
479
+ // ... implement all required methods
480
+ }
481
+
482
+ // Register your custom adapter
483
+ auth.registerAdapter("solana", new SolanaAdapter());
484
+ ```
485
+
486
+ ## API Reference
487
+
488
+ ### Methods
489
+
490
+ #### Identity Management
491
+
492
+ | Method | Parameters | Returns | Description |
493
+ | ------------------ | ------------------------------------------------------------------- | ----------------------------- | ----------------------- |
494
+ | `generateMnemonic` | `options?: { wordCount?: 12 \| 24 }` | `string` | Generate BIP39 mnemonic |
495
+ | `createIdentity` | `CreateIdentityOptions & { chainId?: string }` | `Promise<{ identity, keys }>` | Create new identity |
496
+ | `importIdentity` | `data: string, password: string, format?: 'mnemonic' \| 'keystore'` | `Promise<{ identity }>` | Import from backup |
497
+ | `exportIdentity` | `identityId: string, options: ExportOptions` | `Promise<{ format, data }>` | Export identity |
498
+ | `listIdentities` | - | `Promise<Identity[]>` | Get all identities |
499
+ | `switchIdentity` | `identityId: string` | `Promise<{ identity }>` | Switch active identity |
500
+ | `updateIdentity` | `identityId: string, updates: { label?, metadata? }` | `Promise<{ identity }>` | Update metadata |
501
+ | `deleteIdentity` | `identityId: string, password: string` | `Promise<void>` | Delete identity |
502
+ | `changePassword` | `identityId: string, oldPassword: string, newPassword: string` | `Promise<void>` | Change password |
503
+
504
+ #### Session Management
505
+
506
+ | Method | Parameters | Returns | Description |
507
+ | -------- | --------------- | ------------------- | ---------------------- |
508
+ | `unlock` | `UnlockOptions` | `Promise<{ keys }>` | Decrypt and load keys |
509
+ | `lock` | - | `Promise<void>` | Clear keys from memory |
510
+
511
+ #### Chain Management
512
+
513
+ | Method | Parameters | Returns | Description |
514
+ | --------------------- | --------------------------------------------- | ------------------- | ------------------------ |
515
+ | `registerAdapter` | `chainId: string, adapter: BlockchainAdapter` | `void` | Register blockchain |
516
+ | `switchChain` | `chainId: string, password?: string` | `Promise<{ keys }>` | Switch blockchain |
517
+ | `getRegisteredChains` | - | `string[]` | Get registered chain IDs |
518
+
519
+ #### Key Operations
520
+
521
+ | Method | Parameters | Returns | Description |
522
+ | ------------ | ---------------------------- | --------------------------------------------------- | ---------------------- |
523
+ | `rotateKeys` | `newDerivationPath?: string` | `Promise<{ keys }>` | Rotate to next address |
524
+ | `deriveKey` | `DeriveKeyOptions` | `Promise<{ privateKey, publicKey, address, path }>` | One-time derivation |
525
+
526
+ #### Cryptographic Operations
527
+
528
+ | Method | Parameters | Returns | Description |
529
+ | ----------------- | ------------------------------- | -------------------------------------------- | ---------------- |
530
+ | `signMessage` | `SignMessageOptions` | `Promise<{ signature, address, publicKey }>` | Sign message |
531
+ | `signTransaction` | `SignTransactionOptions` | `Promise<{ signedTransaction, signature }>` | Sign transaction |
532
+ | `verifySignature` | `message, signature, publicKey` | `Promise<boolean>` | Verify signature |
533
+
534
+ #### Address Management
535
+
536
+ | Method | Parameters | Returns | Description |
537
+ | -------------------- | ------------------------------------------------- | ------------------------------------------------- | ---------------------- |
538
+ | `getAddressForChain` | `chainId: string, index?: number` | `Promise<{ address, publicKey, derivationPath }>` | Get single address |
539
+ | `getAddresses` | `chainId: string, start?: number, count?: number` | `Promise<Array<{ address, path, index }>>` | Get multiple addresses |
540
+ | `findUsedAddresses` | `chainId: string, checkBalance: Function` | `Promise<Array<{ address, path, index }>>` | Account discovery |
541
+
542
+ #### Event Management
543
+
544
+ | Method | Parameters | Returns | Description |
545
+ | ------------------- | ------------------------------ | ------------ | ----------------------------------------- |
546
+ | `onAuthStateChange` | `callback: AuthChangeCallback` | `() => void` | Subscribe to events (returns unsubscribe) |
547
+
548
+ ### Types
549
+
550
+ #### CreateIdentityOptions
551
+
552
+ ```typescript
553
+ interface CreateIdentityOptions {
554
+ mnemonic: string; // BIP39 mnemonic (12 or 24 words)
555
+ password: string; // Password to encrypt keystore
556
+ label?: string; // Human-readable label
557
+ metadata?: Record<string, any>; // Custom metadata
558
+ chainId?: string; // Optional: unlock with specific chain
559
+ }
560
+ ```
561
+
562
+ #### UnlockOptions
563
+
564
+ ```typescript
565
+ interface UnlockOptions {
566
+ password: string; // Keystore decryption password
567
+ identityId?: string; // Which identity to unlock (defaults to current)
568
+ chainId?: string; // Which blockchain to use (required)
569
+ derivationPath?: string; // Custom derivation path (advanced)
570
+ duration?: number; // Auto-lock duration in ms
571
+ }
572
+ ```
573
+
574
+ #### Identity
575
+
576
+ ```typescript
577
+ interface Identity {
578
+ id: string; // Unique identifier
579
+ publicKey: string; // Master public key (xpub)
580
+ fingerprint: string; // Master key fingerprint
581
+ label?: string; // User-defined label
582
+ metadata: Record<string, any>;
583
+ createdAt: Date;
584
+ lastAccess?: Date;
585
+ }
586
+ ```
587
+
588
+ #### Keys
589
+
590
+ ```typescript
591
+ interface Keys {
592
+ privateKey: Uint8Array; // Derived private key (binary)
593
+ privateKeyHex: string; // Derived private key (hex)
594
+ publicKey: Uint8Array; // Derived public key (binary)
595
+ publicKeyHex: string; // Derived public key (hex)
596
+ address: string; // Blockchain address
597
+ derivationPath: string; // BIP44 path (e.g., "m/44'/60'/0'/0/0")
598
+ chain: Chain; // Current blockchain
599
+ expiresAt: Date; // When keys expire
600
+ expiresIn: number; // Seconds until expiration (at creation)
601
+ identity: Identity; // Associated identity
602
+ }
603
+ ```
604
+
605
+ #### Chain
606
+
607
+ ```typescript
608
+ interface Chain {
609
+ id: string; // Chain identifier (e.g., 'ethereum')
610
+ name: string; // Display name (e.g., 'Ethereum')
611
+ symbol: string; // Token symbol (e.g., 'ETH')
612
+ }
613
+ ```
614
+
615
+ ## Security Best Practices
616
+
617
+ ### 1. Mnemonic Backup
618
+
619
+ ```typescript
620
+ // Always require user confirmation before creating identity
621
+ const mnemonic = auth.generateMnemonic({ wordCount: 12 });
622
+
623
+ // Show to user with clear warnings:
624
+ // ⚠️ Write down these words in order
625
+ // ⚠️ Never share with anyone
626
+ // ⚠️ Store in a secure location
627
+ // ⚠️ Losing these words = losing access to funds
628
+
629
+ // Only proceed after confirmation
630
+ await auth.createIdentity({ mnemonic, password, ... });
631
+ ```
632
+
633
+ ### 2. Password Requirements
634
+
635
+ ```typescript
636
+ // Enforce strong passwords
637
+ function isStrongPassword(password: string): boolean {
638
+ return (
639
+ password.length >= 12 &&
640
+ /[A-Z]/.test(password) &&
641
+ /[a-z]/.test(password) &&
642
+ /[0-9]/.test(password) &&
643
+ /[^A-Za-z0-9]/.test(password)
644
+ );
645
+ }
646
+ ```
647
+
648
+ ### 3. Auto-Lock
649
+
650
+ ```typescript
651
+ // Always use auto-lock for security
652
+ await auth.unlock({
653
+ password,
654
+ chainId: "ethereum",
655
+ duration: 5 * 60 * 1000, // Lock after 5 minutes of inactivity
656
+ });
657
+ ```
658
+
659
+ ### 4. Key Expiration Monitoring
660
+
661
+ ```typescript
662
+ // Monitor expiration in your UI
663
+ setInterval(() => {
664
+ const remaining = auth.currentExpiresIn;
665
+ if (remaining !== null && remaining < 60) {
666
+ console.warn("Keys expiring soon!");
667
+ // Show UI warning
668
+ }
669
+ }, 1000);
670
+
671
+ // Listen for expiration event
672
+ auth.onAuthStateChange((event) => {
673
+ if (event === "KEYS_EXPIRED") {
674
+ // Prompt user to unlock again
675
+ }
676
+ });
677
+ ```
678
+
679
+ ### 5. Secure Key Handling
680
+
681
+ ```typescript
682
+ // Never log private keys in production
683
+ if (process.env.NODE_ENV !== "production") {
684
+ console.log("Private Key:", keys.privateKeyHex);
685
+ }
686
+
687
+ // Use public key for verification
688
+ console.log("Public Key:", keys.publicKeyHex); // Safe to log
689
+
690
+ // Lock when not in use
691
+ window.addEventListener("beforeunload", () => {
692
+ auth.lock();
693
+ });
694
+ ```
695
+
696
+ ## Advanced Usage
697
+
698
+ ### Multiple Identities
699
+
700
+ ```typescript
701
+ // Create multiple wallets
702
+ const personal = await auth.createIdentity({
703
+ mnemonic: generateMnemonic(),
704
+ password: "password1",
705
+ label: "Personal",
706
+ });
707
+
708
+ const trading = await auth.createIdentity({
709
+ mnemonic: generateMnemonic(),
710
+ password: "password2",
711
+ label: "Trading",
712
+ });
713
+
714
+ // Switch between them
715
+ await auth.switchIdentity(trading.identity.id);
716
+ await auth.unlock({ password: "password2", chainId: "ethereum" });
717
+ ```
718
+
719
+ ### Custom Derivation Paths
720
+
721
+ ```typescript
722
+ // Derive keys at custom path
723
+ const customKey = await auth.deriveKey({
724
+ path: "m/44'/60'/1'/0/0", // Account 1 instead of 0
725
+ });
726
+
727
+ // Sign with custom path
728
+ const { signature } = await auth.signMessage({
729
+ message: "Hello",
730
+ derivationPath: "m/44'/60'/1'/0/0",
731
+ });
732
+ ```
733
+
734
+ ### React/Vue Integration
735
+
736
+ ```typescript
737
+ import { ref, onMounted, onUnmounted } from "vue";
738
+
739
+ const currentAddress = ref(auth.currentAddress);
740
+ const expiresIn = ref(auth.currentExpiresIn);
741
+
742
+ // Subscribe to changes
743
+ const unsubscribe = auth.onAuthStateChange((event, keys) => {
744
+ currentAddress.value = auth.currentAddress;
745
+ expiresIn.value = auth.currentExpiresIn;
746
+ });
747
+
748
+ // Live expiration countdown
749
+ const interval = setInterval(() => {
750
+ expiresIn.value = auth.currentExpiresIn;
751
+ }, 1000);
752
+
753
+ // Cleanup
754
+ onUnmounted(() => {
755
+ unsubscribe();
756
+ clearInterval(interval);
757
+ });
758
+ ```
759
+
760
+ ## Master Public Key
761
+
762
+ Get the master public key derived from the mnemonic.
763
+
764
+ ### Security Note
765
+
766
+ This returns ONLY the public key bytes (33 bytes, compressed secp256k1), NOT the extended public key (xpub). This is safe to expose publicly because:
767
+
768
+ 1. ✅ Cannot derive child keys (no chaincode included)
769
+ 2. ✅ Cannot compute private keys
770
+ 3. ✅ Standard public key cryptography - public keys are meant to be public
771
+ 4. ✅ Chain-independent - same key regardless of blockchain
772
+
773
+ ### Use Cases
774
+
775
+ - Document ownership verification
776
+ - Identity verification across different blockchains
777
+ - Signature verification
778
+ - Access control (e.g., "owner" field in documents)
779
+
780
+ ### What This Is NOT
781
+
782
+ - ❌ NOT an extended public key (xpub) - does not include chaincode
783
+ - ❌ NOT a blockchain address - this is the raw master public key
784
+ - ❌ NOT blockchain-specific - same for all chains
785
+
786
+ ### Usage
787
+
788
+ ```typescript
789
+ await auth.unlock({ password: 'my-password' });
790
+
791
+ const masterPubKey = auth.masterPublicKey;
792
+ // "02a1b2c3d4e5f6..." (33 bytes hex, compressed secp256k1)
793
+
794
+ // Use for document ownership
795
+ const document = {
796
+ id: 'doc_123',
797
+ owner: masterPubKey, // ← Chain-independent identity!
798
+ data: { ... }
799
+ };
800
+
801
+ // Sign with current blockchain key
802
+ const signature = await auth.signMessage({
803
+ message: `${document.id}:update:${Date.now()}`
804
+ });
805
+
806
+ // Server can verify ownership regardless of chain
807
+ ```
808
+
809
+ ## BIP44 Document ID
810
+
811
+ Derives a deterministic document ID using BIP44-style hierarchical derivation.
812
+
813
+ This creates chain-independent, deterministic document IDs that can be used with Automerge or any other document system. The ID is derived from your mnemonic using BIP44 HD key derivation, ensuring the same inputs always produce the same ID.
814
+
815
+ **How it works:** `deriveBIP44DocumentID()` generates a 32-byte seed from your mnemonic. When passed to `store.create()` as the second parameter, Automerge uses this seed with UUID v5 to create a deterministic document URL. Same seed = same document every time!
816
+
817
+ ### Path Structure
818
+
819
+ `m/44'/[appId]'/[account]'/[purpose]/[index]`
820
+
821
+ ### Purpose Parameter Guidance
822
+
823
+ The `purpose` parameter (4th level) can represent document types or categories. This is flexible and application-specific. Examples:
824
+
825
+ - 0: Profile/Settings
826
+ - 1: Notes
827
+ - 2: Tasks
828
+ - 3: Documents
829
+ - 100+: Custom types
830
+
831
+ ### Parameters
832
+
833
+ - `options.appId` - Application identifier (1000000+ recommended, will be hardened)
834
+ - `options.account` - Account/workspace number (default: 0, hardened)
835
+ - `options.purpose` - Document type/category (default: 0, non-hardened)
836
+ - `options.index` - Document instance number (default: 0, non-hardened)
837
+
838
+ ### Returns
839
+
840
+ Hex-encoded document ID (32 bytes / 64 hex chars)
841
+
842
+ ### Usage
843
+
844
+ ```typescript
845
+ // Derive document ID
846
+ const docId = await auth.deriveBIP44DocumentID({
847
+ appId: 1000000,
848
+ purpose: 0, // Settings type
849
+ index: 0, // First settings doc
850
+ });
851
+
852
+ // Create document with deterministic ID
853
+ await store.create({ type: "settings", theme: "dark" }, docId);
854
+
855
+ // Later, re-derive the same ID
856
+ const sameId = await auth.deriveBIP44DocumentID({
857
+ appId: 1000000,
858
+ purpose: 0,
859
+ index: 0,
860
+ });
861
+ const settings = await store.get(sameId); // Gets existing doc!
862
+ ```
863
+
864
+ See `BIP44_DOCUMENT_ID.md` for detailed documentation and examples.
865
+
866
+ ## Browser Compatibility
867
+
868
+ This package is **100% browser-compatible** with zero configuration:
869
+
870
+ - ✅ Chrome, Firefox, Safari, Edge
871
+ - ✅ Node.js (v16+)
872
+ - ✅ Electron (main and renderer)
873
+ - ✅ React Native
874
+ - ✅ Web Workers
875
+ - ✅ Service Workers
876
+
877
+ **No polyfills required.** Uses native browser APIs:
878
+
879
+ - `crypto.subtle` for encryption
880
+ - `indexedDB` for storage
881
+ - `Uint8Array` for binary data
882
+
883
+ ## Dependencies
884
+
885
+ ```json
886
+ {
887
+ "@scure/bip39": "^1.2.1",
888
+ "@scure/bip32": "^1.3.2",
889
+ "@cryptforge/core": "workspace:*"
890
+ }
891
+ ```
892
+
893
+ All dependencies are browser-safe, audited, and actively maintained.
894
+
895
+ ## Examples
896
+
897
+ See the complete working example in `examples/vue-electron-example/src/AuthTest.vue`.
898
+
899
+ ## License
900
+
901
+ MIT
902
+
903
+ ## Contributing
904
+
905
+ Contributions welcome! Please ensure:
906
+
907
+ - All code is browser-compatible (use `@scure` and `@noble` libraries)
908
+ - TypeScript types are properly defined
909
+ - Examples are updated
910
+ - Tests pass (when implemented)
911
+
912
+ ## Support
913
+
914
+ For issues, questions, or feature requests, please open an issue on GitHub.