w3pk 0.3.0 β†’ 0.4.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # w3pk
2
2
 
3
- WebAuthn SDK for passwordless authentication with client-side encrypted Ethereum wallets.
3
+ WebAuthn SDK for passwordless authentication, encrypted Ethereum wallets, and privacy-preserving stealth addresses.
4
+
5
+ Live demo: **https://d2u.w3hc.org/voting**
4
6
 
5
7
  ## Install
6
8
 
@@ -8,53 +10,39 @@ WebAuthn SDK for passwordless authentication with client-side encrypted Ethereum
8
10
  npm install w3pk
9
11
  ```
10
12
 
13
+ ## Features
14
+
15
+ - **πŸ” Passwordless Authentication**: WebAuthn/FIDO2 biometric authentication
16
+ - **πŸ’° Encrypted Wallet Management**: Client-side AES-GCM-256 encrypted wallets
17
+ - **🌱 HD Wallet Generation**: BIP39/BIP44 compliant wallet derivation
18
+ - **πŸ₯· Stealth Addresses**: Privacy-preserving stealth address generation with unlinkable transactions
19
+ - **πŸ›‘οΈ Network Agnostic**: Works with any blockchain - you handle the transactions
20
+
11
21
  ## Quick Start
12
22
 
13
23
  ```typescript
14
24
  import { createWeb3Passkey } from 'w3pk'
15
- import { startRegistration, startAuthentication } from '@simplewebauthn/browser'
16
25
 
17
26
  const w3pk = createWeb3Passkey({
18
27
  apiBaseUrl: 'https://webauthn.w3hc.org'
19
28
  })
20
29
 
21
- // Generate wallet
22
- const wallet = await w3pk.generateWallet()
23
- console.log('Backup this mnemonic:', wallet.mnemonic)
24
-
25
- // Register
26
- const beginRes = await fetch('https://webauthn.w3hc.org/webauthn/register/begin', {
27
- method: 'POST',
28
- headers: { 'Content-Type': 'application/json' },
29
- body: JSON.stringify({ username: 'alice', ethereumAddress: wallet.address })
30
- })
31
- const { data } = await beginRes.json()
32
- const credential = await startRegistration(data.options)
33
-
34
- await w3pk.register({
35
- username: 'alice',
36
- ethereumAddress: wallet.address,
37
- mnemonic: wallet.mnemonic,
38
- credentialId: credential.id,
39
- challenge: data.options.challenge
40
- })
30
+ // Register new user (wallet generated automatically)
31
+ const result = await w3pk.register({ username: 'alice' })
32
+ console.log('⚠️ BACKUP THIS MNEMONIC:', result.mnemonic)
33
+ console.log('Ethereum address:', result.ethereumAddress)
41
34
 
42
- // Login
43
- const result = await w3pk.login()
44
- console.log('Logged in:', result.user?.username)
35
+ // Login (usernameless)
36
+ const loginResult = await w3pk.login()
37
+ console.log('Logged in:', loginResult.user?.username)
38
+ console.log('Address:', loginResult.user?.ethereumAddress)
45
39
 
46
- // Sign message
47
- const authBeginRes = await fetch('https://webauthn.w3hc.org/webauthn/authenticate/usernameless/begin', {
48
- method: 'POST'
49
- })
50
- const authData = await authBeginRes.json()
51
- const authCredential = await startAuthentication(authData.data.options)
40
+ // Sign message (handles fresh auth automatically)
41
+ const signature = await w3pk.signMessage('Hello, Web3!')
42
+ console.log('Signature:', signature)
52
43
 
53
- const signature = await w3pk.signMessage(
54
- 'Hello, Web3!',
55
- authCredential.id,
56
- authData.data.options.challenge
57
- )
44
+ // Logout
45
+ w3pk.logout()
58
46
  ```
59
47
 
60
48
  ## API
@@ -67,52 +55,314 @@ createWeb3Passkey({
67
55
  timeout?: number, // Optional: Request timeout (default: 30000ms)
68
56
  debug?: boolean, // Optional: Enable logs (default: false)
69
57
  onError?: (error) => void, // Optional: Error handler
70
- onAuthStateChanged?: (isAuth, user?) => void // Optional: Auth callback
58
+ onAuthStateChanged?: (isAuth, user?) => void, // Optional: Auth callback
59
+ stealthAddresses?: {} // Optional: Enable stealth address generation
71
60
  })
72
61
  ```
73
62
 
74
63
  ### Methods
75
64
 
65
+ #### Wallet Management
66
+
76
67
  ```typescript
77
68
  // Generate BIP39 wallet (12-word mnemonic)
78
69
  await w3pk.generateWallet()
79
70
  // Returns: { address: string, mnemonic: string }
80
71
 
81
- // Register new user
82
- await w3pk.register({
72
+ // Check if wallet exists for current user
73
+ await w3pk.hasWallet()
74
+ // Returns: boolean
75
+
76
+ // Derive HD wallet at specific index (requires authentication)
77
+ await w3pk.deriveWallet(0) // First address (default)
78
+ await w3pk.deriveWallet(1) // Second address
79
+ await w3pk.deriveWallet(5) // Sixth address
80
+ // Returns: { address: string, privateKey: string }
81
+ ```
82
+
83
+ #### Authentication
84
+
85
+ ```typescript
86
+ // Register new user (auto-generates wallet)
87
+ await w3pk.register({ username: string })
88
+ // Returns: { ethereumAddress: string, mnemonic?: string }
89
+
90
+ // Register with existing wallet
91
+ await w3pk.register({
83
92
  username: string,
84
93
  ethereumAddress: string,
85
- mnemonic: string,
86
- credentialId: string,
87
- challenge: string
94
+ mnemonic: string
88
95
  })
89
96
 
90
97
  // Login (usernameless)
91
98
  await w3pk.login()
92
- // Returns: { verified: boolean, user?: { id, username, ethereumAddress } }
99
+ // Returns: { verified: boolean, user?: UserInfo }
100
+
101
+ // Logout
102
+ w3pk.logout()
103
+ ```
93
104
 
94
- // Authenticate with address
95
- await w3pk.authenticate(ethereumAddress: string)
105
+ #### Message Signing
96
106
 
97
- // Sign message (requires fresh auth)
98
- await w3pk.signMessage(message: string, credentialId: string, challenge: string)
107
+ ```typescript
108
+ // Sign message (handles WebAuthn authentication internally)
109
+ await w3pk.signMessage(message: string)
99
110
  // Returns: string (signature)
111
+ ```
100
112
 
101
- // Logout
102
- w3pk.logout()
113
+ ### Properties
103
114
 
104
- // Check wallet exists
105
- await w3pk.hasWallet()
115
+ ```typescript
116
+ w3pk.isAuthenticated // boolean - Current authentication state
117
+ w3pk.user // UserInfo | null - Current user data
118
+ w3pk.version // string - SDK version
119
+ w3pk.isBrowserEnvironment // boolean - Browser environment detection
120
+ w3pk.stealth // StealthAddressModule | null - Stealth address module
121
+ ```
122
+
123
+ ### Error Handling
124
+
125
+ The SDK provides specific error types for better error handling:
126
+
127
+ ```typescript
128
+ import {
129
+ Web3PasskeyError,
130
+ AuthenticationError,
131
+ RegistrationError,
132
+ WalletError,
133
+ CryptoError,
134
+ StorageError,
135
+ ApiError
136
+ } from 'w3pk'
137
+
138
+ try {
139
+ await w3pk.register({ username: 'alice' })
140
+ } catch (error) {
141
+ if (error instanceof AuthenticationError) {
142
+ console.error('WebAuthn authentication failed:', error.message)
143
+ } else if (error instanceof WalletError) {
144
+ console.error('Wallet operation failed:', error.message)
145
+ } else if (error instanceof ApiError) {
146
+ console.error('Backend API error:', error.message)
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Stealth Address API
152
+
153
+ When configured with `stealthAddresses` option, the SDK provides privacy-preserving stealth address generation:
154
+
155
+ ```typescript
156
+ // Generate a fresh stealth address for privacy-preserving transactions
157
+ await w3pk.stealth.generateStealthAddress()
158
+ // Returns: { stealthAddress, stealthPrivateKey, ephemeralPublicKey }
159
+
160
+ // Get stealth keys for advanced operations
161
+ await w3pk.stealth.getKeys()
162
+ // Returns: { metaAddress, viewingKey, spendingKey }
163
+
164
+ // Check if a stealth address belongs to you (from crypto utils)
165
+ import { canControlStealthAddress } from 'w3pk'
166
+ canControlStealthAddress(viewingKey, ephemeralPublicKey, targetAddress)
106
167
  // Returns: boolean
107
168
  ```
108
169
 
109
- ### Properties
170
+ ### Utility Functions
171
+
172
+ For direct wallet operations without SDK authentication:
110
173
 
111
174
  ```typescript
112
- w3pk.isAuthenticated // boolean
113
- w3pk.user // UserInfo | null
114
- w3pk.version // string
115
- w3pk.isBrowserEnvironment // boolean
175
+ import { generateBIP39Wallet, createWalletFromMnemonic, deriveWalletFromMnemonic } from 'w3pk'
176
+
177
+ // Generate new BIP39 wallet
178
+ const wallet = generateBIP39Wallet()
179
+ // Returns: { address: string, mnemonic: string }
180
+
181
+ // Create ethers wallet from mnemonic
182
+ const ethersWallet = createWalletFromMnemonic(mnemonic)
183
+ // Returns: ethers.HDNodeWallet
184
+
185
+ // Derive HD wallet at specific index
186
+ const derived = deriveWalletFromMnemonic(mnemonic, 2)
187
+ // Returns: { address: string, privateKey: string }
188
+ ```
189
+
190
+ ### Types
191
+
192
+ ```typescript
193
+ interface UserInfo {
194
+ id: string
195
+ username: string
196
+ displayName: string
197
+ ethereumAddress: string
198
+ }
199
+
200
+ interface WalletInfo {
201
+ address: string
202
+ mnemonic: string
203
+ }
204
+
205
+ interface AuthResult {
206
+ verified: boolean
207
+ user?: UserInfo
208
+ }
209
+
210
+ interface StealthKeys {
211
+ metaAddress: string
212
+ viewingKey: string
213
+ spendingKey: string
214
+ }
215
+
216
+ interface StealthAddressResult {
217
+ stealthAddress: string
218
+ stealthPrivateKey: string
219
+ ephemeralPublicKey: string
220
+ }
221
+ ```
222
+
223
+ ## HD Wallet Example
224
+
225
+ Access multiple addresses from a single mnemonic for advanced wallet management:
226
+
227
+ ```typescript
228
+ import { createWeb3Passkey } from 'w3pk'
229
+ import { ethers } from 'ethers'
230
+
231
+ // Initialize and authenticate
232
+ const w3pk = createWeb3Passkey({
233
+ apiBaseUrl: 'https://webauthn.w3hc.org'
234
+ })
235
+ await w3pk.login()
236
+
237
+ // Generate multiple wallet addresses from the same mnemonic
238
+ const mainWallet = await w3pk.deriveWallet(0) // Main wallet
239
+ const savingsWallet = await w3pk.deriveWallet(1) // Savings wallet
240
+ const tradingWallet = await w3pk.deriveWallet(2) // Trading wallet
241
+
242
+ console.log('Main address:', mainWallet.address)
243
+ console.log('Savings address:', savingsWallet.address)
244
+ console.log('Trading address:', tradingWallet.address)
245
+
246
+ // Use private keys directly with ethers.js
247
+ const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com')
248
+ const mainSigner = new ethers.Wallet(mainWallet.privateKey, provider)
249
+ const savingsSigner = new ethers.Wallet(savingsWallet.privateKey, provider)
250
+
251
+ // Send transactions from different derived addresses
252
+ const tx1 = await mainSigner.sendTransaction({
253
+ to: savingsWallet.address,
254
+ value: ethers.parseEther('0.1')
255
+ })
256
+
257
+ const tx2 = await savingsSigner.sendTransaction({
258
+ to: tradingWallet.address,
259
+ value: ethers.parseEther('0.05')
260
+ })
261
+
262
+ console.log('Transfer to savings:', tx1.hash)
263
+ console.log('Transfer to trading:', tx2.hash)
264
+ ```
265
+
266
+ ## Stealth Address Example
267
+
268
+ ```typescript
269
+ import { createWeb3Passkey } from 'w3pk'
270
+ import { ethers } from 'ethers'
271
+
272
+ // Initialize SDK with stealth addresses enabled
273
+ const w3pk = createWeb3Passkey({
274
+ apiBaseUrl: 'https://webauthn.w3hc.org',
275
+ stealthAddresses: {}
276
+ })
277
+
278
+ // 1. Login with w3pk
279
+ await w3pk.login()
280
+
281
+ // 2. Generate a fresh stealth address
282
+ const stealthResult = await w3pk.stealth.generateStealthAddress()
283
+ console.log('Stealth address:', stealthResult.stealthAddress)
284
+ console.log('Private key:', stealthResult.stealthPrivateKey)
285
+ console.log('Ephemeral public key:', stealthResult.ephemeralPublicKey)
286
+
287
+ // 3. Use the private key with any blockchain library
288
+ const stealthWallet = new ethers.Wallet(stealthResult.stealthPrivateKey)
289
+
290
+ // 4. Sign transactions with any provider
291
+ const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com')
292
+ const connectedWallet = stealthWallet.connect(provider)
293
+
294
+ // 5. Send transactions normally - now unlinkable!
295
+ const tx = await connectedWallet.sendTransaction({
296
+ to: '0x742d35Cc6139FE1C2f1234567890123456789014',
297
+ value: ethers.parseEther('0.001')
298
+ })
299
+ console.log('Transaction sent from stealth address:', tx.hash)
300
+
301
+ // 6. Get stealth keys for advanced operations
302
+ const keys = await w3pk.stealth.getKeys()
303
+ console.log('Your stealth meta address:', keys.metaAddress)
304
+ console.log('Your viewing key (keep private):', keys.viewingKey)
305
+ ```
306
+
307
+ ## Complete Example
308
+
309
+ ```typescript
310
+ import { createWeb3Passkey } from 'w3pk'
311
+
312
+ // Initialize SDK
313
+ const w3pk = createWeb3Passkey({
314
+ apiBaseUrl: 'https://webauthn.w3hc.org',
315
+ debug: true,
316
+ onError: (error) => {
317
+ console.error('SDK Error:', error.message)
318
+ },
319
+ onAuthStateChanged: (isAuth, user) => {
320
+ console.log('Auth changed:', isAuth, user?.username)
321
+ }
322
+ })
323
+
324
+ // 1. Register new user
325
+ try {
326
+ const result = await w3pk.register({ username: 'alice' })
327
+
328
+ // User MUST backup this mnemonic!
329
+ if (result.mnemonic) {
330
+ alert(`⚠️ SAVE THIS: ${result.mnemonic}`)
331
+ }
332
+ } catch (error) {
333
+ console.error('Registration failed:', error)
334
+ }
335
+
336
+ // 2. Login existing user
337
+ try {
338
+ const result = await w3pk.login()
339
+
340
+ if (result.verified) {
341
+ console.log('Welcome back,', result.user?.username)
342
+
343
+ // Check if wallet is available on this device
344
+ const hasWallet = await w3pk.hasWallet()
345
+ console.log('Wallet available:', hasWallet)
346
+ }
347
+ } catch (error) {
348
+ console.error('Login failed:', error)
349
+ }
350
+
351
+ // 3. Sign a message
352
+ if (w3pk.isAuthenticated) {
353
+ try {
354
+ // This will prompt for WebAuthn authentication
355
+ const signature = await w3pk.signMessage('Hello, Web3!')
356
+ console.log('Signature:', signature)
357
+
358
+ // Verify on Etherscan: https://etherscan.io/verifiedSignatures
359
+ } catch (error) {
360
+ console.error('Signing failed:', error)
361
+ }
362
+ }
363
+
364
+ // 4. Logout
365
+ w3pk.logout()
116
366
  ```
117
367
 
118
368
  ## Backend
@@ -128,24 +378,161 @@ pnpm start:dev
128
378
 
129
379
  ## Security
130
380
 
131
- - βœ… Client-side AES-GCM-256 encryption
132
- - βœ… PBKDF2 key derivation from WebAuthn credentials
133
- - βœ… Private keys never leave the browser
134
- - βœ… IndexedDB encrypted storage per device
135
- - ⚠️ Users MUST backup their 12-word mnemonic
381
+ ### Encryption & Storage
382
+ - βœ… **Client-side AES-GCM-256 encryption** - All sensitive data encrypted in browser
383
+ - βœ… **PBKDF2 key derivation** (100,000 iterations) from WebAuthn credentials
384
+ - βœ… **Private keys never leave device** - Zero server-side key storage
385
+ - βœ… **IndexedDB encrypted storage** - Separate encrypted storage per device
386
+ - βœ… **WebAuthn/FIDO2 authentication** - Hardware-backed biometric security
387
+
388
+ ### Wallet Standards
389
+ - βœ… **BIP39 standard mnemonic** - Industry-standard 12-word recovery phrase
390
+ - βœ… **BIP44 HD derivation** - Standard path `m/44'/60'/0'/0/{index}` for Ethereum
391
+ - βœ… **Deterministic addresses** - Same mnemonic always produces same addresses
392
+ - βœ… **Multiple address support** - Derive unlimited addresses from one mnemonic
393
+
394
+ ### Privacy Features
395
+ - βœ… **Stealth addresses** - Unlinkable transaction privacy (optional)
396
+ - βœ… **Zero-knowledge proofs** - Privacy-preserving stealth address generation
397
+ - βœ… **Ephemeral keys** - Fresh keys for each stealth transaction
398
+ - βœ… **Unlinkable transactions** - No on-chain connection between stealth addresses
399
+
400
+ ### Security Notes & Best Practices
401
+
402
+ #### ⚠️ Critical Security Requirements
403
+ - **MUST backup mnemonic** - The 12-word phrase is shown only once during registration
404
+ - **MUST secure mnemonic** - Store it offline, never share or store digitally
405
+ - **Cannot recover without mnemonic** - Lost device + lost mnemonic = lost wallet forever
406
+
407
+ #### πŸ”’ Device Security
408
+ - Your wallet is protected by device biometrics (fingerprint, Face ID, Windows Hello)
409
+ - Each device stores its own encrypted copy of the wallet
410
+ - WebAuthn credentials are bound to your specific device hardware
411
+ - Fresh authentication required for sensitive operations (signing, key derivation)
412
+
413
+ #### 🌐 Network Security
414
+ - SDK works entirely client-side - no private keys sent to servers
415
+ - Backend only stores WebAuthn public key credentials (no wallet data)
416
+ - All wallet encryption/decryption happens in your browser
417
+ - Compatible with any Ethereum-compatible network
418
+
419
+ #### πŸ’‘ Operational Security Tips
420
+ - Test wallet functionality with small amounts first
421
+ - Verify signatures on Etherscan before sending large transactions
422
+ - Use different derived addresses for different purposes (privacy by design)
423
+ - Consider using stealth addresses for maximum transaction privacy
424
+
425
+ ## React Integration
426
+
427
+ View live example: **https://d2u.w3hc.org/web3**
428
+
429
+ ```typescript
430
+ import { createWeb3Passkey } from 'w3pk'
431
+ import { useState, useEffect } from 'react'
432
+
433
+ function App() {
434
+ const [w3pk, setW3pk] = useState(null)
435
+ const [user, setUser] = useState(null)
436
+
437
+ useEffect(() => {
438
+ const sdk = createWeb3Passkey({
439
+ apiBaseUrl: 'https://webauthn.w3hc.org',
440
+ onAuthStateChanged: (isAuth, user) => {
441
+ setUser(isAuth ? user : null)
442
+ }
443
+ })
444
+ setW3pk(sdk)
445
+ }, [])
446
+
447
+ const handleRegister = async () => {
448
+ const result = await w3pk.register({ username: 'alice' })
449
+ alert(`Save this mnemonic: ${result.mnemonic}`)
450
+ }
451
+
452
+ const handleLogin = async () => {
453
+ await w3pk.login()
454
+ }
455
+
456
+ const handleSign = async () => {
457
+ const sig = await w3pk.signMessage('Hello!')
458
+ console.log('Signature:', sig)
459
+ }
460
+
461
+ return (
462
+ <div>
463
+ {!user ? (
464
+ <>
465
+ <button onClick={handleRegister}>Register</button>
466
+ <button onClick={handleLogin}>Login</button>
467
+ </>
468
+ ) : (
469
+ <>
470
+ <p>Welcome {user.username}!</p>
471
+ <button onClick={handleSign}>Sign Message</button>
472
+ <button onClick={() => w3pk.logout()}>Logout</button>
473
+ </>
474
+ )}
475
+ </div>
476
+ )
477
+ }
478
+ ```
136
479
 
137
- ## Testing
480
+ ## Development
138
481
 
139
482
  ```bash
140
- # Node.js test (wallet generation)
141
- pnpm test
483
+ # Install dependencies
484
+ pnpm install
142
485
 
143
- # Build
486
+ # Build for production
144
487
  pnpm build
145
488
 
146
- # Watch mode
489
+ # Development mode with watch
147
490
  pnpm dev
491
+
492
+ # Run tests
493
+ pnpm test # Run basic + comprehensive test suite
494
+ pnpm test:basic # Run basic functionality tests only
495
+ pnpm test:comprehensive # Run full 23-test comprehensive suite
496
+
497
+ # Publish to npm
498
+ pnpm prepublishOnly # Builds before publishing
499
+ ```
500
+
501
+ ### Test Coverage
502
+
503
+ The SDK includes a comprehensive test suite with **23 test cases** covering:
504
+
505
+ - βœ… **Core SDK functionality** - Constructor, configuration, environment detection
506
+ - βœ… **Wallet generation** - BIP39/BIP44 compliance, HD derivation, consistency
507
+ - βœ… **Encryption/decryption** - AES-GCM-256, key derivation, data roundtrips
508
+ - βœ… **Storage operations** - IndexedDB CRUD, multiple wallets, cleanup
509
+ - βœ… **Message signing** - Signature generation, address verification
510
+ - βœ… **HD wallet derivation** - Multi-index support, validation, consistency
511
+ - βœ… **Error handling** - Graceful failure scenarios
512
+ - βœ… **Integration testing** - End-to-end workflows
513
+
514
+ ### Architecture
515
+
148
516
  ```
517
+ w3pk/
518
+ β”œβ”€β”€ src/
519
+ β”‚ β”œβ”€β”€ core/ # Main SDK class and configuration
520
+ β”‚ β”œβ”€β”€ wallet/ # Wallet generation, signing, storage
521
+ β”‚ β”œβ”€β”€ auth/ # WebAuthn authentication flows
522
+ β”‚ β”œβ”€β”€ stealth/ # Privacy-preserving stealth addresses
523
+ β”‚ β”œβ”€β”€ utils/ # API client, validation utilities
524
+ β”‚ └── types/ # TypeScript type definitions
525
+ β”œβ”€β”€ test/ # Comprehensive test suite
526
+ └── dist/ # Built output (CJS + ESM + types)
527
+ ```
528
+
529
+ ## Browser Compatibility
530
+
531
+ Requires browsers with WebAuthn support:
532
+ - Chrome/Edge 67+
533
+ - Firefox 60+
534
+ - Safari 13+
535
+ - All modern mobile browsers
149
536
 
150
537
  ## Support
151
538
 
@@ -155,4 +542,8 @@ Contact [Julien BΓ©ranger](https://github.com/julienbrg):
155
542
  - Telegram: [@julienbrg](https://t.me/julienbrg)
156
543
  - Twitter: [@julienbrg](https://twitter.com/julienbrg)
157
544
 
158
- <img src="https://bafkreid5xwxz4bed67bxb2wjmwsec4uhlcjviwy7pkzwoyu5oesjd3sp64.ipfs.w3s.link" alt="built-with-ethereum-w3hc" width="100"/>
545
+ ## License
546
+
547
+ GPL-3.0-or-later
548
+
549
+ <img src="https://bafkreid5xwxz4bed67bxb2wjmwsec4uhlcjviwy7pkzwoyu5oesjd3sp64.ipfs.w3s.link" alt="built-with-ethereum-w3hc" width="100"/>