shogun-core 3.0.3 → 3.0.4

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.
Files changed (95) hide show
  1. package/package.json +1 -1
  2. package/dist/browser/shogun-core.js +0 -92128
  3. package/dist/browser/shogun-core.js.map +0 -1
  4. package/dist/config/simplified-config.js +0 -230
  5. package/dist/core.js +0 -338
  6. package/dist/gundb/crypto.js +0 -268
  7. package/dist/gundb/db.js +0 -1833
  8. package/dist/gundb/derive.js +0 -229
  9. package/dist/gundb/errors.js +0 -66
  10. package/dist/gundb/index.js +0 -6
  11. package/dist/gundb/restricted-put.js +0 -81
  12. package/dist/gundb/rxjs.js +0 -445
  13. package/dist/gundb/simple-api.js +0 -438
  14. package/dist/gundb/types.js +0 -4
  15. package/dist/index.js +0 -16
  16. package/dist/interfaces/common.js +0 -1
  17. package/dist/interfaces/events.js +0 -36
  18. package/dist/interfaces/plugin.js +0 -1
  19. package/dist/interfaces/shogun.js +0 -34
  20. package/dist/managers/AuthManager.js +0 -225
  21. package/dist/managers/CoreInitializer.js +0 -234
  22. package/dist/managers/EventManager.js +0 -67
  23. package/dist/managers/PluginManager.js +0 -296
  24. package/dist/migration-test.js +0 -91
  25. package/dist/plugins/base.js +0 -47
  26. package/dist/plugins/index.js +0 -15
  27. package/dist/plugins/nostr/index.js +0 -4
  28. package/dist/plugins/nostr/nostrConnector.js +0 -413
  29. package/dist/plugins/nostr/nostrConnectorPlugin.js +0 -446
  30. package/dist/plugins/nostr/nostrSigner.js +0 -313
  31. package/dist/plugins/nostr/types.js +0 -1
  32. package/dist/plugins/oauth/index.js +0 -3
  33. package/dist/plugins/oauth/oauthConnector.js +0 -753
  34. package/dist/plugins/oauth/oauthPlugin.js +0 -396
  35. package/dist/plugins/oauth/types.js +0 -1
  36. package/dist/plugins/web3/index.js +0 -4
  37. package/dist/plugins/web3/types.js +0 -1
  38. package/dist/plugins/web3/web3Connector.js +0 -528
  39. package/dist/plugins/web3/web3ConnectorPlugin.js +0 -448
  40. package/dist/plugins/web3/web3Signer.js +0 -308
  41. package/dist/plugins/webauthn/index.js +0 -3
  42. package/dist/plugins/webauthn/types.js +0 -11
  43. package/dist/plugins/webauthn/webauthn.js +0 -478
  44. package/dist/plugins/webauthn/webauthnPlugin.js +0 -398
  45. package/dist/plugins/webauthn/webauthnSigner.js +0 -304
  46. package/dist/storage/storage.js +0 -147
  47. package/dist/types/config/simplified-config.d.ts +0 -114
  48. package/dist/types/core.d.ts +0 -305
  49. package/dist/types/gundb/crypto.d.ts +0 -95
  50. package/dist/types/gundb/db.d.ts +0 -404
  51. package/dist/types/gundb/derive.d.ts +0 -21
  52. package/dist/types/gundb/errors.d.ts +0 -42
  53. package/dist/types/gundb/index.d.ts +0 -3
  54. package/dist/types/gundb/restricted-put.d.ts +0 -15
  55. package/dist/types/gundb/rxjs.d.ts +0 -110
  56. package/dist/types/gundb/simple-api.d.ts +0 -90
  57. package/dist/types/gundb/types.d.ts +0 -264
  58. package/dist/types/index.d.ts +0 -14
  59. package/dist/types/interfaces/common.d.ts +0 -85
  60. package/dist/types/interfaces/events.d.ts +0 -131
  61. package/dist/types/interfaces/plugin.d.ts +0 -162
  62. package/dist/types/interfaces/shogun.d.ts +0 -215
  63. package/dist/types/managers/AuthManager.d.ts +0 -72
  64. package/dist/types/managers/CoreInitializer.d.ts +0 -40
  65. package/dist/types/managers/EventManager.d.ts +0 -49
  66. package/dist/types/managers/PluginManager.d.ts +0 -145
  67. package/dist/types/migration-test.d.ts +0 -16
  68. package/dist/types/plugins/base.d.ts +0 -35
  69. package/dist/types/plugins/index.d.ts +0 -14
  70. package/dist/types/plugins/nostr/index.d.ts +0 -4
  71. package/dist/types/plugins/nostr/nostrConnector.d.ts +0 -119
  72. package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +0 -163
  73. package/dist/types/plugins/nostr/nostrSigner.d.ts +0 -105
  74. package/dist/types/plugins/nostr/types.d.ts +0 -122
  75. package/dist/types/plugins/oauth/index.d.ts +0 -3
  76. package/dist/types/plugins/oauth/oauthConnector.d.ts +0 -110
  77. package/dist/types/plugins/oauth/oauthPlugin.d.ts +0 -91
  78. package/dist/types/plugins/oauth/types.d.ts +0 -114
  79. package/dist/types/plugins/web3/index.d.ts +0 -4
  80. package/dist/types/plugins/web3/types.d.ts +0 -107
  81. package/dist/types/plugins/web3/web3Connector.d.ts +0 -129
  82. package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +0 -160
  83. package/dist/types/plugins/web3/web3Signer.d.ts +0 -114
  84. package/dist/types/plugins/webauthn/index.d.ts +0 -3
  85. package/dist/types/plugins/webauthn/types.d.ts +0 -162
  86. package/dist/types/plugins/webauthn/webauthn.d.ts +0 -129
  87. package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +0 -158
  88. package/dist/types/plugins/webauthn/webauthnSigner.d.ts +0 -91
  89. package/dist/types/storage/storage.d.ts +0 -50
  90. package/dist/types/utils/errorHandler.d.ts +0 -119
  91. package/dist/types/utils/eventEmitter.d.ts +0 -39
  92. package/dist/types/utils/validation.d.ts +0 -27
  93. package/dist/utils/errorHandler.js +0 -241
  94. package/dist/utils/eventEmitter.js +0 -76
  95. package/dist/utils/validation.js +0 -72
@@ -1,398 +0,0 @@
1
- import { BasePlugin } from "../base";
2
- import { Webauthn } from "./webauthn";
3
- import { WebAuthnSigner } from "./webauthnSigner";
4
- import { ErrorHandler, ErrorType } from "../../utils/errorHandler";
5
- /**
6
- * Plugin per la gestione delle funzionalità WebAuthn in ShogunCore
7
- */
8
- export class WebauthnPlugin extends BasePlugin {
9
- name = "webauthn";
10
- version = "1.0.0";
11
- description = "Provides WebAuthn authentication functionality for ShogunCore";
12
- webauthn = null;
13
- signer = null;
14
- /**
15
- * @inheritdoc
16
- */
17
- initialize(core) {
18
- super.initialize(core);
19
- // Verifica se siamo in ambiente browser
20
- if (typeof window === "undefined") {
21
- console.warn("[webauthnPlugin] WebAuthn plugin disabled - not in browser environment");
22
- return;
23
- }
24
- // Verifica se WebAuthn è supportato
25
- if (!this.isSupported()) {
26
- console.warn("[webauthnPlugin] WebAuthn not supported in this environment");
27
- return;
28
- }
29
- // Inizializziamo il modulo WebAuthn
30
- this.webauthn = new Webauthn(core.gun);
31
- this.signer = new WebAuthnSigner(this.webauthn);
32
- console.log("[webauthnPlugin] WebAuthn plugin initialized with signer support");
33
- }
34
- /**
35
- * @inheritdoc
36
- */
37
- destroy() {
38
- this.webauthn = null;
39
- this.signer = null;
40
- super.destroy();
41
- console.log("[webauthnPlugin] WebAuthn plugin destroyed");
42
- }
43
- /**
44
- * Assicura che il modulo Webauthn sia inizializzato
45
- * @private
46
- */
47
- assertWebauthn() {
48
- this.assertInitialized();
49
- if (!this.webauthn) {
50
- throw new Error("WebAuthn module not initialized");
51
- }
52
- return this.webauthn;
53
- }
54
- /**
55
- * Assicura che il signer sia inizializzato
56
- * @private
57
- */
58
- assertSigner() {
59
- this.assertInitialized();
60
- if (!this.signer) {
61
- throw new Error("WebAuthn signer not initialized");
62
- }
63
- return this.signer;
64
- }
65
- /**
66
- * Genera un pair SEA dalle credenziali WebAuthn
67
- * @private
68
- */
69
- async generatePairFromCredentials(credentials) {
70
- try {
71
- // Use the signer to create a derived key pair from the WebAuthn credentials
72
- const pair = await this.assertSigner().createDerivedKeyPair(credentials.credentialId, credentials.username);
73
- return pair;
74
- }
75
- catch (error) {
76
- console.error("Error generating pair from WebAuthn credentials:", error);
77
- return null;
78
- }
79
- }
80
- /**
81
- * @inheritdoc
82
- */
83
- isSupported() {
84
- // Verifica se siamo in ambiente browser
85
- if (typeof window === "undefined") {
86
- return false;
87
- }
88
- // Check if PublicKeyCredential is available
89
- if (typeof window.PublicKeyCredential === "undefined") {
90
- return false;
91
- }
92
- // In test environment, allow initialization if window.PublicKeyCredential is mocked
93
- if (process.env.NODE_ENV === "test") {
94
- return typeof window.PublicKeyCredential !== "undefined";
95
- }
96
- // Se il plugin non è stato inizializzato, verifica direttamente il supporto
97
- if (!this.webauthn) {
98
- return typeof window.PublicKeyCredential !== "undefined";
99
- }
100
- return this.webauthn.isSupported();
101
- }
102
- /**
103
- * @inheritdoc
104
- */
105
- async generateCredentials(username, existingCredential, isLogin = false) {
106
- return this.assertWebauthn().generateCredentials(username, existingCredential, isLogin);
107
- }
108
- /**
109
- * @inheritdoc
110
- */
111
- async createAccount(username, credentials, isNewDevice = false) {
112
- return this.assertWebauthn().createAccount(username, credentials, isNewDevice);
113
- }
114
- /**
115
- * @inheritdoc
116
- */
117
- async authenticateUser(username, salt, options) {
118
- return this.assertWebauthn().authenticateUser(username, salt, options);
119
- }
120
- /**
121
- * @inheritdoc
122
- */
123
- abortAuthentication() {
124
- this.assertWebauthn().abortAuthentication();
125
- }
126
- /**
127
- * @inheritdoc
128
- */
129
- async removeDevice(username, credentialId, credentials) {
130
- return this.assertWebauthn().removeDevice(username, credentialId, credentials);
131
- }
132
- /**
133
- * @inheritdoc
134
- */
135
- async createSigningCredential(username) {
136
- try {
137
- // Delegate to underlying WebAuthn module (tests mock these methods)
138
- const wa = this.assertWebauthn();
139
- if (typeof wa.createSigningCredential === "function") {
140
- return await wa.createSigningCredential(username);
141
- }
142
- // Fallback to signer implementation if available
143
- return await this.assertSigner().createSigningCredential(username);
144
- }
145
- catch (error) {
146
- console.error(`Error creating signing credential: ${error.message}`);
147
- throw error;
148
- }
149
- }
150
- /**
151
- * @inheritdoc
152
- */
153
- createAuthenticator(credentialId) {
154
- try {
155
- const wa = this.assertWebauthn();
156
- if (typeof wa.createAuthenticator === "function") {
157
- return wa.createAuthenticator(credentialId);
158
- }
159
- return this.assertSigner().createAuthenticator(credentialId);
160
- }
161
- catch (error) {
162
- console.error(`Error creating authenticator: ${error.message}`);
163
- throw error;
164
- }
165
- }
166
- /**
167
- * @inheritdoc
168
- */
169
- async createDerivedKeyPair(credentialId, username, extra) {
170
- try {
171
- const wa = this.assertWebauthn();
172
- if (typeof wa.createDerivedKeyPair === "function") {
173
- return await wa.createDerivedKeyPair(credentialId, username, extra);
174
- }
175
- return await this.assertSigner().createDerivedKeyPair(credentialId, username, extra);
176
- }
177
- catch (error) {
178
- console.error(`Error creating derived key pair: ${error.message}`);
179
- throw error;
180
- }
181
- }
182
- /**
183
- * @inheritdoc
184
- */
185
- async signWithDerivedKeys(data, credentialId, username, extra) {
186
- try {
187
- const wa = this.assertWebauthn();
188
- if (typeof wa.signWithDerivedKeys === "function") {
189
- return await wa.signWithDerivedKeys(data, credentialId, username, extra);
190
- }
191
- return await this.assertSigner().signWithDerivedKeys(data, credentialId, username, extra);
192
- }
193
- catch (error) {
194
- console.error(`Error signing with derived keys: ${error.message}`);
195
- throw error;
196
- }
197
- }
198
- /**
199
- * @inheritdoc
200
- */
201
- getSigningCredential(credentialId) {
202
- const wa = this.assertWebauthn();
203
- if (typeof wa.getSigningCredential === "function") {
204
- return wa.getSigningCredential(credentialId);
205
- }
206
- return this.assertSigner().getCredential(credentialId);
207
- }
208
- /**
209
- * @inheritdoc
210
- */
211
- listSigningCredentials() {
212
- const wa = this.assertWebauthn();
213
- if (typeof wa.listSigningCredentials === "function") {
214
- return wa.listSigningCredentials();
215
- }
216
- return this.assertSigner().listCredentials();
217
- }
218
- /**
219
- * @inheritdoc
220
- */
221
- removeSigningCredential(credentialId) {
222
- const wa = this.assertWebauthn();
223
- if (typeof wa.removeSigningCredential === "function") {
224
- return wa.removeSigningCredential(credentialId);
225
- }
226
- return this.assertSigner().removeCredential(credentialId);
227
- }
228
- // === CONSISTENCY METHODS ===
229
- /**
230
- * Creates a Gun user from WebAuthn signing credential
231
- * This ensures the SAME user is created as with normal approach
232
- */
233
- async createGunUserFromSigningCredential(credentialId, username) {
234
- try {
235
- const wa = this.assertWebauthn();
236
- if (typeof wa.createGunUserFromSigningCredential === "function") {
237
- return await wa.createGunUserFromSigningCredential(credentialId, username);
238
- }
239
- const core = this.assertInitialized();
240
- return await this.assertSigner().createGunUser(credentialId, username, core.gun);
241
- }
242
- catch (error) {
243
- console.error(`Error creating Gun user from signing credential: ${error.message}`);
244
- throw error;
245
- }
246
- }
247
- /**
248
- * Get the Gun user public key for a signing credential
249
- */
250
- getGunUserPubFromSigningCredential(credentialId) {
251
- const wa = this.assertWebauthn();
252
- if (typeof wa.getGunUserPubFromSigningCredential === "function") {
253
- return wa.getGunUserPubFromSigningCredential(credentialId);
254
- }
255
- return this.assertSigner().getGunUserPub(credentialId);
256
- }
257
- /**
258
- * Get the hashed credential ID (for consistency checking)
259
- */
260
- getHashedCredentialId(credentialId) {
261
- const wa = this.assertWebauthn();
262
- if (typeof wa.getHashedCredentialId === "function") {
263
- return wa.getHashedCredentialId(credentialId);
264
- }
265
- return this.assertSigner().getHashedCredentialId(credentialId);
266
- }
267
- /**
268
- * Verify consistency between oneshot and normal approaches
269
- * This ensures both approaches create the same Gun user
270
- */
271
- async verifyConsistency(credentialId, username, expectedUserPub) {
272
- try {
273
- const wa = this.assertWebauthn();
274
- if (typeof wa.verifyConsistency === "function") {
275
- return await wa.verifyConsistency(credentialId, username, expectedUserPub);
276
- }
277
- return await this.assertSigner().verifyConsistency(credentialId, username, expectedUserPub);
278
- }
279
- catch (error) {
280
- console.error(`Error verifying consistency: ${error.message}`);
281
- return { consistent: false };
282
- }
283
- }
284
- /**
285
- * Complete oneshot workflow that creates the SAME Gun user as normal approach
286
- * This is the recommended method for oneshot signing with full consistency
287
- */
288
- async setupConsistentOneshotSigning(username) {
289
- try {
290
- const wa = this.assertWebauthn();
291
- if (typeof wa.setupConsistentOneshotSigning === "function") {
292
- return await wa.setupConsistentOneshotSigning(username);
293
- }
294
- // Fallback to local flow when not available
295
- const credential = await this.createSigningCredential(username);
296
- const authenticator = this.createAuthenticator(credential.id);
297
- const gunUser = await this.createGunUserFromSigningCredential(credential.id, username);
298
- return {
299
- credential,
300
- authenticator,
301
- gunUser,
302
- pub: credential.pub,
303
- hashedCredentialId: credential.hashedCredentialId,
304
- };
305
- }
306
- catch (error) {
307
- console.error(`Error setting up consistent oneshot signing: ${error.message}`);
308
- throw error;
309
- }
310
- }
311
- /**
312
- * Login with WebAuthn
313
- * This is the recommended method for WebAuthn authentication
314
- * @param username - Username
315
- * @returns {Promise<AuthResult>} Authentication result
316
- * @description Authenticates user using WebAuthn credentials.
317
- * Requires browser support for WebAuthn and existing credentials.
318
- */
319
- async login(username) {
320
- try {
321
- const core = this.assertInitialized();
322
- if (!username) {
323
- throw new Error("Username required for WebAuthn login");
324
- }
325
- if (!this.isSupported()) {
326
- throw new Error("WebAuthn is not supported by this browser");
327
- }
328
- // Prefer the oneshot consistent signing flow (tests mock this)
329
- const { authenticator, pub } = (await this.setupConsistentOneshotSigning(username));
330
- // If core has an authenticate method (tests), use it
331
- if (core.authenticate) {
332
- return await core.authenticate(username, authenticator, pub);
333
- }
334
- // Fallback to credentials-based flow
335
- const credentials = await this.generateCredentials(username, null, true);
336
- if (!credentials?.success) {
337
- throw new Error(credentials?.error || "WebAuthn verification failed");
338
- }
339
- core.setAuthMethod("webauthn");
340
- return await core.login(username, "", credentials.key);
341
- }
342
- catch (error) {
343
- console.error(`Error during WebAuthn login: ${error}`);
344
- // Log but do not depend on handler return value
345
- ErrorHandler.handle(ErrorType.WEBAUTHN, "WEBAUTHN_LOGIN_ERROR", error.message || "Error during WebAuthn login", error);
346
- return {
347
- success: false,
348
- error: error.message || "Error during WebAuthn login",
349
- };
350
- }
351
- }
352
- /**
353
- * Register new user with WebAuthn
354
- * This is the recommended method for WebAuthn registration
355
- * @param username - Username
356
- * @returns {Promise<SignUpResult>} Registration result
357
- * @description Creates a new user account using WebAuthn credentials.
358
- * Requires browser support for WebAuthn.
359
- */
360
- async signUp(username) {
361
- try {
362
- const core = this.assertInitialized();
363
- if (!username) {
364
- throw new Error("Username required for WebAuthn registration");
365
- }
366
- if (!this.isSupported()) {
367
- throw new Error("WebAuthn is not supported by this browser");
368
- }
369
- // Prefer the oneshot consistent signing flow (tests mock this)
370
- const { authenticator, pub } = (await this.setupConsistentOneshotSigning(username));
371
- if (core.signUp) {
372
- // Some tests stub signUp directly
373
- return await core.signUp(username, authenticator, pub);
374
- }
375
- // Fallback to credentials-based flow
376
- const credentials = await this.generateCredentials(username, null, false);
377
- if (!credentials?.success) {
378
- throw new Error(credentials?.error || "Unable to generate WebAuthn credentials");
379
- }
380
- core.setAuthMethod("webauthn");
381
- // Convert WebAuthn credentials to SEA pair
382
- const pair = await this.generatePairFromCredentials(credentials);
383
- if (!pair) {
384
- throw new Error("Failed to generate SEA pair from WebAuthn credentials");
385
- }
386
- // Use pair-based authentication instead of password
387
- return await core.signUp(username, undefined, pair);
388
- }
389
- catch (error) {
390
- console.error(`Error during WebAuthn registration: ${error}`);
391
- ErrorHandler.handle(ErrorType.WEBAUTHN, "WEBAUTHN_SIGNUP_ERROR", error.message || "Error during WebAuthn registration", error);
392
- return {
393
- success: false,
394
- error: error.message || "Error during WebAuthn registration",
395
- };
396
- }
397
- }
398
- }
@@ -1,304 +0,0 @@
1
- import { Webauthn } from "./webauthn";
2
- import { p256 } from "@noble/curves/p256";
3
- import { sha256 } from "@noble/hashes/sha256";
4
- import derive from "../../gundb/derive";
5
- import { ethers } from "ethers";
6
- /**
7
- * Base64URL encoding utilities
8
- */
9
- const base64url = {
10
- encode: function (buffer) {
11
- const bytes = new Uint8Array(buffer);
12
- return btoa(String.fromCharCode(...bytes))
13
- .replace(/\+/g, "-")
14
- .replace(/\//g, "_")
15
- .replace(/=/g, "");
16
- },
17
- decode: function (str) {
18
- str = str.replace(/-/g, "+").replace(/_/g, "/");
19
- while (str.length % 4)
20
- str += "=";
21
- const binary = atob(str);
22
- return new Uint8Array(binary.split("").map((c) => c.charCodeAt(0)));
23
- },
24
- };
25
- /**
26
- * WebAuthn Signer - Provides oneshot signing functionality
27
- * Similar to webauthn.js but integrated with our architecture
28
- * CONSISTENT with normal WebAuthn approach
29
- */
30
- export class WebAuthnSigner {
31
- webauthn;
32
- credentials = new Map();
33
- constructor(webauthn) {
34
- this.webauthn = webauthn || new Webauthn();
35
- }
36
- /**
37
- * Creates a new WebAuthn credential for signing
38
- * Similar to webauthn.js create functionality but CONSISTENT with normal approach
39
- */
40
- async createSigningCredential(username) {
41
- try {
42
- const credential = (await navigator.credentials.create({
43
- publicKey: {
44
- challenge: crypto.getRandomValues(new Uint8Array(32)),
45
- rp: {
46
- id: window.location.hostname === "localhost"
47
- ? "localhost"
48
- : window.location.hostname,
49
- name: "Shogun Wallet",
50
- },
51
- user: {
52
- id: new TextEncoder().encode(username),
53
- name: username,
54
- displayName: username,
55
- },
56
- // Use the same algorithms as webauthn.js for SEA compatibility
57
- pubKeyCredParams: [
58
- { type: "public-key", alg: -7 }, // ECDSA, P-256 curve, for signing
59
- { type: "public-key", alg: -25 }, // ECDH, P-256 curve, for creating shared secrets
60
- { type: "public-key", alg: -257 },
61
- ],
62
- authenticatorSelection: {
63
- userVerification: "preferred",
64
- },
65
- timeout: 60000,
66
- attestation: "none",
67
- },
68
- }));
69
- if (!credential) {
70
- throw new Error("Failed to create WebAuthn credential");
71
- }
72
- // Extract public key in the same way as webauthn.js
73
- const response = credential.response;
74
- const publicKey = response.getPublicKey();
75
- if (!publicKey) {
76
- throw new Error("Failed to get public key from credential");
77
- }
78
- const rawKey = new Uint8Array(publicKey);
79
- // Extract coordinates like webauthn.js (slice positions may need adjustment)
80
- const xCoord = rawKey.slice(27, 59);
81
- const yCoord = rawKey.slice(59, 91);
82
- const x = base64url.encode(xCoord);
83
- const y = base64url.encode(yCoord);
84
- const pub = `${x}.${y}`;
85
- // CONSISTENCY: Use the same hashing approach as normal WebAuthn
86
- const hashedCredentialId = ethers.keccak256(ethers.toUtf8Bytes(credential.id));
87
- const signingCredential = {
88
- id: credential.id,
89
- rawId: credential.rawId,
90
- publicKey: { x, y },
91
- pub,
92
- hashedCredentialId, // This ensures consistency
93
- };
94
- // Store credential for later use
95
- this.credentials.set(credential.id, signingCredential);
96
- return signingCredential;
97
- }
98
- catch (error) {
99
- console.error("Error creating signing credential:", error);
100
- throw new Error(`Failed to create signing credential: ${error.message}`);
101
- }
102
- }
103
- /**
104
- * Creates an authenticator function compatible with SEA.sign
105
- * This is the key function that makes it work like webauthn.js
106
- */
107
- createAuthenticator(credentialId) {
108
- const credential = this.credentials.get(credentialId);
109
- if (!credential) {
110
- throw new Error(`Credential ${credentialId} not found`);
111
- }
112
- return async (data) => {
113
- try {
114
- const challenge = new TextEncoder().encode(JSON.stringify(data));
115
- const options = {
116
- challenge,
117
- rpId: window.location.hostname === "localhost"
118
- ? "localhost"
119
- : window.location.hostname,
120
- userVerification: "preferred",
121
- allowCredentials: [
122
- {
123
- type: "public-key",
124
- id: credential.rawId,
125
- },
126
- ],
127
- timeout: 60000,
128
- };
129
- const assertion = (await navigator.credentials.get({
130
- publicKey: options,
131
- }));
132
- if (!assertion) {
133
- throw new Error("WebAuthn assertion failed");
134
- }
135
- return assertion.response;
136
- }
137
- catch (error) {
138
- console.error("WebAuthn assertion error:", error);
139
- throw error;
140
- }
141
- };
142
- }
143
- /**
144
- * Creates a derived key pair from WebAuthn credential
145
- * CONSISTENT with normal approach: uses hashedCredentialId as password
146
- */
147
- async createDerivedKeyPair(credentialId, username, extra) {
148
- const credential = this.credentials.get(credentialId);
149
- if (!credential) {
150
- throw new Error(`Credential ${credentialId} not found`);
151
- }
152
- try {
153
- // CONSISTENCY: Use the same approach as normal WebAuthn
154
- // Use hashedCredentialId as password (same as normal approach)
155
- const derivedKeys = await derive(credential.hashedCredentialId, // This is the key change!
156
- extra, { includeP256: true });
157
- return {
158
- pub: derivedKeys.pub,
159
- priv: derivedKeys.priv,
160
- epub: derivedKeys.epub,
161
- epriv: derivedKeys.epriv,
162
- };
163
- }
164
- catch (error) {
165
- console.error("Error deriving keys from WebAuthn credential:", error);
166
- throw error;
167
- }
168
- }
169
- /**
170
- * Creates a Gun user from WebAuthn credential
171
- * This ensures the SAME user is created as with normal approach
172
- * FIX: Use derived pair instead of username/password for GunDB auth
173
- */
174
- async createGunUser(credentialId, username, gunInstance) {
175
- const credential = this.credentials.get(credentialId);
176
- if (!credential) {
177
- throw new Error(`Credential ${credentialId} not found`);
178
- }
179
- try {
180
- // FIX: Use derived pair for GunDB authentication instead of username/password
181
- const derivedPair = await this.createDerivedKeyPair(credentialId, username);
182
- return new Promise((resolve) => {
183
- // Use the derived pair directly for GunDB auth
184
- gunInstance.user().create(derivedPair, (ack) => {
185
- if (ack.err) {
186
- // Try to login if user already exists
187
- gunInstance.user().auth(derivedPair, (authAck) => {
188
- if (authAck.err) {
189
- resolve({ success: false, error: authAck.err });
190
- }
191
- else {
192
- const userPub = authAck.pub;
193
- // Update credential with Gun user pub
194
- credential.gunUserPub = userPub;
195
- this.credentials.set(credentialId, credential);
196
- resolve({ success: true, userPub });
197
- }
198
- });
199
- }
200
- else {
201
- // User created, now login
202
- gunInstance.user().auth(derivedPair, (authAck) => {
203
- if (authAck.err) {
204
- resolve({ success: false, error: authAck.err });
205
- }
206
- else {
207
- const userPub = authAck.pub;
208
- // Update credential with Gun user pub
209
- credential.gunUserPub = userPub;
210
- this.credentials.set(credentialId, credential);
211
- resolve({ success: true, userPub });
212
- }
213
- });
214
- }
215
- });
216
- });
217
- }
218
- catch (error) {
219
- console.error("Error creating Gun user:", error);
220
- return { success: false, error: error.message };
221
- }
222
- }
223
- /**
224
- * Signs data using WebAuthn + derived keys
225
- * This provides a hybrid approach: WebAuthn for user verification + derived keys for actual signing
226
- * CONSISTENT with normal approach
227
- */
228
- async signWithDerivedKeys(data, credentialId, username, extra) {
229
- try {
230
- // First, verify user with WebAuthn
231
- const authenticator = this.createAuthenticator(credentialId);
232
- await authenticator(data); // This verifies the user
233
- // Then use derived keys for actual signing (CONSISTENT approach)
234
- const keyPair = await this.createDerivedKeyPair(credentialId, username, extra);
235
- // Create signature using P-256 (same as SEA)
236
- const message = JSON.stringify(data);
237
- const messageHash = sha256(new TextEncoder().encode(message));
238
- // Convert base64url private key to bytes
239
- const privKeyBytes = base64url.decode(keyPair.priv);
240
- // Sign with P-256
241
- const signature = p256.sign(messageHash, privKeyBytes);
242
- // Format like SEA signature
243
- const seaSignature = {
244
- m: message,
245
- s: base64url.encode(signature.toCompactRawBytes()),
246
- };
247
- return "SEA" + JSON.stringify(seaSignature);
248
- }
249
- catch (error) {
250
- console.error("Error signing with derived keys:", error);
251
- throw error;
252
- }
253
- }
254
- /**
255
- * Get the Gun user public key for a credential
256
- * This allows checking if the same user would be created
257
- */
258
- getGunUserPub(credentialId) {
259
- const credential = this.credentials.get(credentialId);
260
- return credential?.gunUserPub;
261
- }
262
- /**
263
- * Get the hashed credential ID (for consistency checking)
264
- */
265
- getHashedCredentialId(credentialId) {
266
- const credential = this.credentials.get(credentialId);
267
- return credential?.hashedCredentialId;
268
- }
269
- /**
270
- * Check if this credential would create the same Gun user as normal approach
271
- */
272
- async verifyConsistency(credentialId, username, expectedUserPub) {
273
- const credential = this.credentials.get(credentialId);
274
- if (!credential) {
275
- return { consistent: false };
276
- }
277
- // The derived keys should be the same as normal approach
278
- const derivedKeys = await this.createDerivedKeyPair(credentialId, username);
279
- return {
280
- consistent: expectedUserPub ? derivedKeys.pub === expectedUserPub : true,
281
- actualUserPub: derivedKeys.pub,
282
- expectedUserPub,
283
- };
284
- }
285
- /**
286
- * Get credential by ID
287
- */
288
- getCredential(credentialId) {
289
- return this.credentials.get(credentialId);
290
- }
291
- /**
292
- * List all stored credentials
293
- */
294
- listCredentials() {
295
- return Array.from(this.credentials.values());
296
- }
297
- /**
298
- * Remove a credential
299
- */
300
- removeCredential(credentialId) {
301
- return this.credentials.delete(credentialId);
302
- }
303
- }
304
- export default WebAuthnSigner;