w3pk 0.2.0 → 0.4.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 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/web3**
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)
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)
24
34
 
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
- })
35
+ // Login (usernameless)
36
+ const loginResult = await w3pk.login()
37
+ console.log('Logged in:', loginResult.user?.username)
38
+ console.log('Address:', loginResult.user?.ethereumAddress)
41
39
 
42
- // Login
43
- const result = await w3pk.login()
44
- console.log('Logged in:', result.user?.username)
40
+ // Sign message (handles fresh auth automatically)
41
+ const signature = await w3pk.signMessage('Hello, Web3!')
42
+ console.log('Signature:', signature)
45
43
 
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)
52
-
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,43 +55,53 @@ 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
+
77
+ #### Authentication
78
+
79
+ ```typescript
80
+ // Register new user (auto-generates wallet)
81
+ await w3pk.register({ username: string })
82
+ // Returns: { ethereumAddress: string, mnemonic?: string }
83
+
84
+ // Register with existing wallet
85
+ await w3pk.register({
83
86
  username: string,
84
87
  ethereumAddress: string,
85
- mnemonic: string,
86
- credentialId: string,
87
- challenge: string
88
+ mnemonic: string
88
89
  })
89
90
 
90
91
  // Login (usernameless)
91
92
  await w3pk.login()
92
- // Returns: { verified: boolean, user?: { id, username, ethereumAddress } }
93
-
94
- // Authenticate with address
95
- await w3pk.authenticate(ethereumAddress: string)
96
-
97
- // Sign message (requires fresh auth)
98
- await w3pk.signMessage(message: string, credentialId: string, challenge: string)
99
- // Returns: string (signature)
93
+ // Returns: { verified: boolean, user?: UserInfo }
100
94
 
101
95
  // Logout
102
96
  w3pk.logout()
97
+ ```
103
98
 
104
- // Check wallet exists
105
- await w3pk.hasWallet()
106
- // Returns: boolean
99
+ #### Message Signing
100
+
101
+ ```typescript
102
+ // Sign message (handles WebAuthn authentication internally)
103
+ await w3pk.signMessage(message: string)
104
+ // Returns: string (signature)
107
105
  ```
108
106
 
109
107
  ### Properties
@@ -113,6 +111,161 @@ w3pk.isAuthenticated // boolean
113
111
  w3pk.user // UserInfo | null
114
112
  w3pk.version // string
115
113
  w3pk.isBrowserEnvironment // boolean
114
+ w3pk.stealth // StealthAddressModule | null
115
+ ```
116
+
117
+ ### Stealth Address API
118
+
119
+ When configured with `stealthAddresses` option, the SDK provides privacy-preserving stealth address generation:
120
+
121
+ ```typescript
122
+ // Generate a fresh stealth address for privacy-preserving transactions
123
+ await w3pk.stealth.generateStealthAddress()
124
+ // Returns: { stealthAddress, stealthPrivateKey, ephemeralPublicKey }
125
+
126
+ // Get stealth keys for advanced operations
127
+ await w3pk.stealth.getKeys()
128
+ // Returns: { metaAddress, viewingKey, spendingKey }
129
+
130
+ // Check if a stealth address belongs to you (from crypto utils)
131
+ import { canControlStealthAddress } from 'w3pk'
132
+ canControlStealthAddress(viewingKey, ephemeralPublicKey, targetAddress)
133
+ // Returns: boolean
134
+ ```
135
+
136
+ ### Types
137
+
138
+ ```typescript
139
+ interface UserInfo {
140
+ id: string
141
+ username: string
142
+ displayName: string
143
+ ethereumAddress: string
144
+ }
145
+
146
+ interface WalletInfo {
147
+ address: string
148
+ mnemonic: string
149
+ }
150
+
151
+ interface AuthResult {
152
+ verified: boolean
153
+ user?: UserInfo
154
+ }
155
+
156
+ interface StealthKeys {
157
+ metaAddress: string
158
+ viewingKey: string
159
+ spendingKey: string
160
+ }
161
+
162
+ interface StealthAddressResult {
163
+ stealthAddress: string
164
+ stealthPrivateKey: string
165
+ ephemeralPublicKey: string
166
+ }
167
+ ```
168
+
169
+ ## Stealth Address Example
170
+
171
+ ```typescript
172
+ import { createWeb3Passkey } from 'w3pk'
173
+ import { ethers } from 'ethers'
174
+
175
+ // Initialize SDK with stealth addresses enabled
176
+ const w3pk = createWeb3Passkey({
177
+ apiBaseUrl: 'https://webauthn.w3hc.org',
178
+ stealthAddresses: {}
179
+ })
180
+
181
+ // 1. Login with w3pk
182
+ await w3pk.login()
183
+
184
+ // 2. Generate a fresh stealth address
185
+ const stealthResult = await w3pk.stealth.generateStealthAddress()
186
+ console.log('Stealth address:', stealthResult.stealthAddress)
187
+ console.log('Private key:', stealthResult.stealthPrivateKey)
188
+ console.log('Ephemeral public key:', stealthResult.ephemeralPublicKey)
189
+
190
+ // 3. Use the private key with any blockchain library
191
+ const stealthWallet = new ethers.Wallet(stealthResult.stealthPrivateKey)
192
+
193
+ // 4. Sign transactions with any provider
194
+ const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com')
195
+ const connectedWallet = stealthWallet.connect(provider)
196
+
197
+ // 5. Send transactions normally - now unlinkable!
198
+ const tx = await connectedWallet.sendTransaction({
199
+ to: '0x742d35Cc6139FE1C2f1234567890123456789014',
200
+ value: ethers.parseEther('0.001')
201
+ })
202
+ console.log('Transaction sent from stealth address:', tx.hash)
203
+
204
+ // 6. Get stealth keys for advanced operations
205
+ const keys = await w3pk.stealth.getKeys()
206
+ console.log('Your stealth meta address:', keys.metaAddress)
207
+ console.log('Your viewing key (keep private):', keys.viewingKey)
208
+ ```
209
+
210
+ ## Complete Example
211
+
212
+ ```typescript
213
+ import { createWeb3Passkey } from 'w3pk'
214
+
215
+ // Initialize SDK
216
+ const w3pk = createWeb3Passkey({
217
+ apiBaseUrl: 'https://webauthn.w3hc.org',
218
+ debug: true,
219
+ onError: (error) => {
220
+ console.error('SDK Error:', error.message)
221
+ },
222
+ onAuthStateChanged: (isAuth, user) => {
223
+ console.log('Auth changed:', isAuth, user?.username)
224
+ }
225
+ })
226
+
227
+ // 1. Register new user
228
+ try {
229
+ const result = await w3pk.register({ username: 'alice' })
230
+
231
+ // User MUST backup this mnemonic!
232
+ if (result.mnemonic) {
233
+ alert(`⚠️ SAVE THIS: ${result.mnemonic}`)
234
+ }
235
+ } catch (error) {
236
+ console.error('Registration failed:', error)
237
+ }
238
+
239
+ // 2. Login existing user
240
+ try {
241
+ const result = await w3pk.login()
242
+
243
+ if (result.verified) {
244
+ console.log('Welcome back,', result.user?.username)
245
+
246
+ // Check if wallet is available on this device
247
+ const hasWallet = await w3pk.hasWallet()
248
+ console.log('Wallet available:', hasWallet)
249
+ }
250
+ } catch (error) {
251
+ console.error('Login failed:', error)
252
+ }
253
+
254
+ // 3. Sign a message
255
+ if (w3pk.isAuthenticated) {
256
+ try {
257
+ // This will prompt for WebAuthn authentication
258
+ const signature = await w3pk.signMessage('Hello, Web3!')
259
+ console.log('Signature:', signature)
260
+
261
+ // Verify on Etherscan: https://etherscan.io/verifiedSignatures
262
+ } catch (error) {
263
+ console.error('Signing failed:', error)
264
+ }
265
+ }
266
+
267
+ // 4. Logout
268
+ w3pk.logout()
116
269
  ```
117
270
 
118
271
  ## Backend
@@ -129,24 +282,99 @@ pnpm start:dev
129
282
  ## Security
130
283
 
131
284
  - ✅ Client-side AES-GCM-256 encryption
132
- - ✅ PBKDF2 key derivation from WebAuthn credentials
285
+ - ✅ PBKDF2 key derivation (100,000 iterations) from WebAuthn credentials
133
286
  - ✅ Private keys never leave the browser
134
287
  - ✅ IndexedDB encrypted storage per device
288
+ - ✅ BIP39 standard 12-word mnemonic
289
+ - ✅ BIP44 HD wallet derivation (m/44'/60'/0'/0/0)
135
290
  - ⚠️ Users MUST backup their 12-word mnemonic
136
291
 
137
- ## Testing
292
+ ### Security Notes
293
+
294
+ - Your wallet is protected by device biometrics (fingerprint, Face ID, etc.)
295
+ - If you lose your device or passkey, your wallet **cannot be recovered** without the mnemonic
296
+ - The mnemonic is only shown once during registration
297
+ - Each device stores its own encrypted copy of the wallet
298
+
299
+ ## React Integration
300
+
301
+ View live example: **https://d2u.w3hc.org/web3**
302
+
303
+ ```typescript
304
+ import { createWeb3Passkey } from 'w3pk'
305
+ import { useState, useEffect } from 'react'
306
+
307
+ function App() {
308
+ const [w3pk, setW3pk] = useState(null)
309
+ const [user, setUser] = useState(null)
310
+
311
+ useEffect(() => {
312
+ const sdk = createWeb3Passkey({
313
+ apiBaseUrl: 'https://webauthn.w3hc.org',
314
+ onAuthStateChanged: (isAuth, user) => {
315
+ setUser(isAuth ? user : null)
316
+ }
317
+ })
318
+ setW3pk(sdk)
319
+ }, [])
320
+
321
+ const handleRegister = async () => {
322
+ const result = await w3pk.register({ username: 'alice' })
323
+ alert(`Save this mnemonic: ${result.mnemonic}`)
324
+ }
325
+
326
+ const handleLogin = async () => {
327
+ await w3pk.login()
328
+ }
329
+
330
+ const handleSign = async () => {
331
+ const sig = await w3pk.signMessage('Hello!')
332
+ console.log('Signature:', sig)
333
+ }
334
+
335
+ return (
336
+ <div>
337
+ {!user ? (
338
+ <>
339
+ <button onClick={handleRegister}>Register</button>
340
+ <button onClick={handleLogin}>Login</button>
341
+ </>
342
+ ) : (
343
+ <>
344
+ <p>Welcome {user.username}!</p>
345
+ <button onClick={handleSign}>Sign Message</button>
346
+ <button onClick={() => w3pk.logout()}>Logout</button>
347
+ </>
348
+ )}
349
+ </div>
350
+ )
351
+ }
352
+ ```
353
+
354
+ ## Development
138
355
 
139
356
  ```bash
140
- # Node.js test (wallet generation)
141
- pnpm test
357
+ # Install dependencies
358
+ pnpm install
142
359
 
143
360
  # Build
144
361
  pnpm build
145
362
 
146
363
  # Watch mode
147
364
  pnpm dev
365
+
366
+ # Test (Node.js environment - wallet generation)
367
+ pnpm test
148
368
  ```
149
369
 
370
+ ## Browser Compatibility
371
+
372
+ Requires browsers with WebAuthn support:
373
+ - Chrome/Edge 67+
374
+ - Firefox 60+
375
+ - Safari 13+
376
+ - All modern mobile browsers
377
+
150
378
  ## Support
151
379
 
152
380
  Contact [Julien Béranger](https://github.com/julienbrg):
@@ -155,4 +383,8 @@ Contact [Julien Béranger](https://github.com/julienbrg):
155
383
  - Telegram: [@julienbrg](https://t.me/julienbrg)
156
384
  - Twitter: [@julienbrg](https://twitter.com/julienbrg)
157
385
 
158
- <img src="https://bafkreid5xwxz4bed67bxb2wjmwsec4uhlcjviwy7pkzwoyu5oesjd3sp64.ipfs.w3s.link" alt="built-with-ethereum-w3hc" width="100"/>
386
+ ## License
387
+
388
+ GPL-3.0-or-later
389
+
390
+ <img src="https://bafkreid5xwxz4bed67bxb2wjmwsec4uhlcjviwy7pkzwoyu5oesjd3sp64.ipfs.w3s.link" alt="built-with-ethereum-w3hc" width="100"/>
package/dist/index.d.mts CHANGED
@@ -1,9 +1,59 @@
1
+ /**
2
+ * Stealth address cryptography for privacy-preserving transactions
3
+ */
4
+ interface StealthKeys {
5
+ metaAddress: string;
6
+ viewingKey: string;
7
+ spendingKey: string;
8
+ }
9
+ /**
10
+ * Check if a stealth address can be controlled by the stealth keys
11
+ * Useful for scanning and proving ownership of stealth addresses
12
+ */
13
+ declare function canControlStealthAddress(viewingKey: string, spendingKey: string, ephemeralPubkey: string, targetAddress: string): boolean;
14
+
15
+ /**
16
+ * Stealth Address Module for w3pk SDK
17
+ * Provides privacy-preserving stealth address generation capabilities
18
+ */
19
+
20
+ interface StealthAddressConfig$1 {
21
+ }
22
+ interface StealthAddressResult {
23
+ stealthAddress: string;
24
+ stealthPrivateKey: string;
25
+ ephemeralPublicKey: string;
26
+ }
27
+ /**
28
+ * Main Stealth Address Module
29
+ * Integrates with w3pk WebAuthn for seamless privacy-preserving stealth address generation
30
+ */
31
+ declare class StealthAddressModule {
32
+ private config;
33
+ private getMnemonic;
34
+ constructor(config: StealthAddressConfig$1, getMnemonic: () => Promise<string | null>);
35
+ /**
36
+ * Generate a fresh stealth address for privacy-preserving transactions
37
+ * Returns the stealth address and private key for the user to handle transactions
38
+ */
39
+ generateStealthAddress(): Promise<StealthAddressResult>;
40
+ /**
41
+ * Get stealth keys for manual operations
42
+ */
43
+ getKeys(): Promise<StealthKeys>;
44
+ /**
45
+ * Check if stealth addresses are available (always true if properly configured)
46
+ */
47
+ get isAvailable(): boolean;
48
+ }
49
+
1
50
  /**
2
51
  * Shared types used across the SDK
3
52
  */
4
53
  interface UserInfo {
5
54
  id: string;
6
55
  username: string;
56
+ displayName: string;
7
57
  ethereumAddress: string;
8
58
  }
9
59
  interface WalletInfo {
@@ -43,6 +93,8 @@ declare class ApiError extends Web3PasskeyError {
43
93
  * SDK Configuration
44
94
  */
45
95
 
96
+ interface StealthAddressConfig {
97
+ }
46
98
  interface Web3PasskeyConfig {
47
99
  /**
48
100
  * Base URL of the WebAuthn API
@@ -67,25 +119,21 @@ interface Web3PasskeyConfig {
67
119
  * Auth state change callback
68
120
  */
69
121
  onAuthStateChanged?: (isAuthenticated: boolean, user?: UserInfo) => void;
122
+ /**
123
+ * Optional stealth address configuration
124
+ * If provided, enables privacy-preserving stealth address generation
125
+ */
126
+ stealthAddresses?: StealthAddressConfig;
70
127
  }
71
128
 
72
129
  /**
73
- * Authentication-related types
130
+ * Main Web3Passkey SDK class
74
131
  */
75
132
 
76
133
  interface AuthResult {
77
134
  verified: boolean;
78
- user?: {
79
- id: string;
80
- username: string;
81
- ethereumAddress: string;
82
- };
135
+ user?: UserInfo;
83
136
  }
84
-
85
- /**
86
- * Main Web3Passkey SDK class
87
- */
88
-
89
137
  declare class Web3Passkey {
90
138
  private config;
91
139
  private apiClient;
@@ -94,11 +142,18 @@ declare class Web3Passkey {
94
142
  private currentUser;
95
143
  private isAuthenticatedState;
96
144
  private isBrowser;
145
+ private stealthAddresses;
97
146
  constructor(config: Web3PasskeyConfig);
98
147
  private loadAuthState;
99
148
  private saveAuthState;
100
149
  private clearAuthState;
101
150
  private notifyAuthStateChange;
151
+ private createUserInfo;
152
+ /**
153
+ * Get mnemonic for current authenticated user
154
+ * Used by stealth address module
155
+ */
156
+ private getMnemonic;
102
157
  /**
103
158
  * Generate a new BIP39 wallet
104
159
  * @returns Wallet info with mnemonic (user MUST backup)
@@ -110,23 +165,22 @@ declare class Web3Passkey {
110
165
  hasWallet(): Promise<boolean>;
111
166
  /**
112
167
  * Register a new user with WebAuthn
168
+ * Handles the complete registration flow internally
169
+ * Automatically generates wallet if not provided
170
+ *
113
171
  * @param username Username for the account
114
- * @param ethereumAddress Ethereum address from generated wallet
115
- * @param mnemonic BIP39 mnemonic to encrypt and store
116
- * @param credentialId WebAuthn credential ID (from registration response)
117
- * @param challenge Challenge used in registration
172
+ * @param ethereumAddress Optional: Ethereum address (will generate if not provided)
173
+ * @param mnemonic Optional: BIP39 mnemonic (will generate if not provided)
174
+ * @returns Object containing ethereumAddress and mnemonic (only if generated)
118
175
  */
119
176
  register(options: {
120
177
  username: string;
178
+ ethereumAddress?: string;
179
+ mnemonic?: string;
180
+ }): Promise<{
121
181
  ethereumAddress: string;
122
- mnemonic: string;
123
- credentialId: string;
124
- challenge: string;
125
- }): Promise<void>;
126
- /**
127
- * Authenticate with Ethereum address
128
- */
129
- authenticate(ethereumAddress: string): Promise<AuthResult>;
182
+ mnemonic?: string;
183
+ }>;
130
184
  /**
131
185
  * Authenticate without username (usernameless flow)
132
186
  */
@@ -137,12 +191,11 @@ declare class Web3Passkey {
137
191
  logout(): void;
138
192
  /**
139
193
  * Sign a message with encrypted wallet
140
- * Requires fresh WebAuthn authentication
194
+ * Handles fresh WebAuthn authentication internally
195
+ *
141
196
  * @param message Message to sign
142
- * @param credentialId WebAuthn credential ID (from fresh auth)
143
- * @param challenge Challenge from fresh auth
144
197
  */
145
- signMessage(message: string, credentialId: string, challenge: string): Promise<string>;
198
+ signMessage(message: string): Promise<string>;
146
199
  /**
147
200
  * Check if user is authenticated
148
201
  */
@@ -159,6 +212,10 @@ declare class Web3Passkey {
159
212
  * Check if running in browser environment
160
213
  */
161
214
  get isBrowserEnvironment(): boolean;
215
+ /**
216
+ * Get stealth address module (if configured)
217
+ */
218
+ get stealth(): StealthAddressModule | null;
162
219
  }
163
220
 
164
221
  /**
@@ -168,4 +225,4 @@ declare class Web3Passkey {
168
225
 
169
226
  declare function createWeb3Passkey(config: Web3PasskeyConfig): Web3Passkey;
170
227
 
171
- export { ApiError, type AuthResult, AuthenticationError, CryptoError, RegistrationError, StorageError, type UserInfo, WalletError, type WalletInfo, Web3Passkey, type Web3PasskeyConfig, Web3PasskeyError, createWeb3Passkey, createWeb3Passkey as default };
228
+ export { ApiError, AuthenticationError, CryptoError, RegistrationError, type StealthAddressConfig, StealthAddressModule, type StealthAddressResult, type StealthKeys, StorageError, type UserInfo, WalletError, type WalletInfo, Web3Passkey, type Web3PasskeyConfig, Web3PasskeyError, canControlStealthAddress, createWeb3Passkey, createWeb3Passkey as default };