@phantom/embedded-provider-core 0.1.8 → 0.1.10

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/dist/index.d.mts CHANGED
@@ -10,6 +10,8 @@ interface Keypair {
10
10
  interface StamperInfo {
11
11
  keyId: string;
12
12
  publicKey: string;
13
+ createdAt?: number;
14
+ authenticatorId?: string;
13
15
  }
14
16
  interface Session {
15
17
  sessionId: string;
@@ -22,6 +24,11 @@ interface Session {
22
24
  status: "pending" | "completed" | "failed";
23
25
  createdAt: number;
24
26
  lastUsed: number;
27
+ authenticatorCreatedAt: number;
28
+ authenticatorExpiresAt: number;
29
+ lastRenewalAttempt?: number;
30
+ username: string;
31
+ accountDerivationIndex?: number;
25
32
  }
26
33
  interface EmbeddedStorage {
27
34
  getSession(): Promise<Session | null>;
@@ -53,6 +60,7 @@ interface AuthResult {
53
60
  walletId: string;
54
61
  provider?: string;
55
62
  userInfo?: Record<string, any>;
63
+ accountDerivationIndex?: number;
56
64
  }
57
65
  interface PhantomConnectOptions {
58
66
  organizationId: string;
@@ -168,6 +176,8 @@ declare class EmbeddedProvider {
168
176
  private handleJWTAuth;
169
177
  private handleRedirectAuth;
170
178
  private completeAuthConnection;
179
+ private ensureValidAuthenticator;
180
+ private renewAuthenticator;
171
181
  private initializeClientFromSession;
172
182
  }
173
183
 
@@ -179,4 +189,20 @@ declare function generateSessionId(): string;
179
189
 
180
190
  declare function retryWithBackoff<T>(operation: () => Promise<T>, operationName: string, logger: DebugLogger, maxRetries?: number, baseDelay?: number): Promise<T>;
181
191
 
182
- export { AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedProviderEvent, EmbeddedStorage, EventCallback, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, StamperInfo, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };
192
+ /**
193
+ * Constants for authenticator lifecycle management
194
+ */
195
+ /**
196
+ * How long an authenticator is valid before it expires (in milliseconds)
197
+ * Default: 7 days
198
+ * For testing: Use smaller values like 5 * 60 * 1000 (5 minutes)
199
+ */
200
+ declare const AUTHENTICATOR_EXPIRATION_TIME_MS: number;
201
+ /**
202
+ * How long before expiration should we attempt to renew the authenticator (in milliseconds)
203
+ * Default: 2 days before expiration
204
+ * For testing: Use smaller values like 2 * 60 * 1000 (2 minutes)
205
+ */
206
+ declare const AUTHENTICATOR_RENEWAL_WINDOW_MS: number;
207
+
208
+ export { AUTHENTICATOR_EXPIRATION_TIME_MS, AUTHENTICATOR_RENEWAL_WINDOW_MS, AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedProviderEvent, EmbeddedStorage, EventCallback, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, StamperInfo, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };
package/dist/index.d.ts CHANGED
@@ -10,6 +10,8 @@ interface Keypair {
10
10
  interface StamperInfo {
11
11
  keyId: string;
12
12
  publicKey: string;
13
+ createdAt?: number;
14
+ authenticatorId?: string;
13
15
  }
14
16
  interface Session {
15
17
  sessionId: string;
@@ -22,6 +24,11 @@ interface Session {
22
24
  status: "pending" | "completed" | "failed";
23
25
  createdAt: number;
24
26
  lastUsed: number;
27
+ authenticatorCreatedAt: number;
28
+ authenticatorExpiresAt: number;
29
+ lastRenewalAttempt?: number;
30
+ username: string;
31
+ accountDerivationIndex?: number;
25
32
  }
26
33
  interface EmbeddedStorage {
27
34
  getSession(): Promise<Session | null>;
@@ -53,6 +60,7 @@ interface AuthResult {
53
60
  walletId: string;
54
61
  provider?: string;
55
62
  userInfo?: Record<string, any>;
63
+ accountDerivationIndex?: number;
56
64
  }
57
65
  interface PhantomConnectOptions {
58
66
  organizationId: string;
@@ -168,6 +176,8 @@ declare class EmbeddedProvider {
168
176
  private handleJWTAuth;
169
177
  private handleRedirectAuth;
170
178
  private completeAuthConnection;
179
+ private ensureValidAuthenticator;
180
+ private renewAuthenticator;
171
181
  private initializeClientFromSession;
172
182
  }
173
183
 
@@ -179,4 +189,20 @@ declare function generateSessionId(): string;
179
189
 
180
190
  declare function retryWithBackoff<T>(operation: () => Promise<T>, operationName: string, logger: DebugLogger, maxRetries?: number, baseDelay?: number): Promise<T>;
181
191
 
182
- export { AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedProviderEvent, EmbeddedStorage, EventCallback, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, StamperInfo, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };
192
+ /**
193
+ * Constants for authenticator lifecycle management
194
+ */
195
+ /**
196
+ * How long an authenticator is valid before it expires (in milliseconds)
197
+ * Default: 7 days
198
+ * For testing: Use smaller values like 5 * 60 * 1000 (5 minutes)
199
+ */
200
+ declare const AUTHENTICATOR_EXPIRATION_TIME_MS: number;
201
+ /**
202
+ * How long before expiration should we attempt to renew the authenticator (in milliseconds)
203
+ * Default: 2 days before expiration
204
+ * For testing: Use smaller values like 2 * 60 * 1000 (2 minutes)
205
+ */
206
+ declare const AUTHENTICATOR_RENEWAL_WINDOW_MS: number;
207
+
208
+ export { AUTHENTICATOR_EXPIRATION_TIME_MS, AUTHENTICATOR_RENEWAL_WINDOW_MS, AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedProviderEvent, EmbeddedStorage, EventCallback, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, StamperInfo, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };
package/dist/index.js CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ AUTHENTICATOR_EXPIRATION_TIME_MS: () => AUTHENTICATOR_EXPIRATION_TIME_MS,
34
+ AUTHENTICATOR_RENEWAL_WINDOW_MS: () => AUTHENTICATOR_RENEWAL_WINDOW_MS,
33
35
  EmbeddedProvider: () => EmbeddedProvider,
34
36
  JWTAuth: () => JWTAuth,
35
37
  generateSessionId: () => generateSessionId,
@@ -43,6 +45,10 @@ var import_base64url = require("@phantom/base64url");
43
45
  var import_bs58 = __toESM(require("bs58"));
44
46
  var import_parsers = require("@phantom/parsers");
45
47
 
48
+ // src/constants.ts
49
+ var AUTHENTICATOR_EXPIRATION_TIME_MS = 7 * 24 * 60 * 60 * 1e3;
50
+ var AUTHENTICATOR_RENEWAL_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
51
+
46
52
  // src/auth/jwt-auth.ts
47
53
  var JWTAuth = class {
48
54
  async authenticate(options) {
@@ -209,14 +215,17 @@ var EmbeddedProvider = class {
209
215
  }
210
216
  }
211
217
  async getAndFilterWalletAddresses(walletId) {
218
+ const session = await this.storage.getSession();
219
+ const derivationIndex = session?.accountDerivationIndex ?? 0;
212
220
  const addresses = await retryWithBackoff(
213
- () => this.client.getWalletAddresses(walletId),
221
+ () => this.client.getWalletAddresses(walletId, void 0, derivationIndex),
214
222
  "getWalletAddresses",
215
223
  this.logger
216
224
  ).catch(async (error) => {
217
225
  this.logger.error("EMBEDDED_PROVIDER", "getWalletAddresses failed after retries, disconnecting", {
218
226
  walletId,
219
- error: error.message
227
+ error: error.message,
228
+ derivationIndex
220
229
  });
221
230
  await this.storage.clearSession();
222
231
  this.client = null;
@@ -257,6 +266,14 @@ var EmbeddedProvider = class {
257
266
  return null;
258
267
  }
259
268
  }
269
+ if (session.status === "completed" && !this.isSessionValid(session)) {
270
+ this.logger.warn("EMBEDDED_PROVIDER", "Session invalid due to authenticator expiration", {
271
+ sessionId: session.sessionId,
272
+ authenticatorExpiresAt: session.authenticatorExpiresAt
273
+ });
274
+ await this.storage.clearSession();
275
+ return null;
276
+ }
260
277
  return session;
261
278
  }
262
279
  /*
@@ -291,6 +308,7 @@ var EmbeddedProvider = class {
291
308
  walletId: this.walletId,
292
309
  addressCount: this.addresses.length
293
310
  });
311
+ await this.ensureValidAuthenticator();
294
312
  const result = {
295
313
  walletId: this.walletId,
296
314
  addresses: this.addresses,
@@ -320,8 +338,9 @@ var EmbeddedProvider = class {
320
338
  }
321
339
  }
322
340
  /*
323
- * We use this method to validate if a session is still valid and can be used for auto-connect.
324
- * This checks session status, expiration, and required fields.
341
+ * We use this method to validate if a session is still valid.
342
+ * This checks session status, required fields, and authenticator expiration.
343
+ * Sessions never expire by age - only authenticators expire.
325
344
  */
326
345
  isSessionValid(session) {
327
346
  if (!session) {
@@ -339,20 +358,23 @@ var EmbeddedProvider = class {
339
358
  this.logger.log("EMBEDDED_PROVIDER", "Session not completed", { status: session.status });
340
359
  return false;
341
360
  }
342
- const sessionAge = Date.now() - session.lastUsed;
343
- const maxSessionAge = 7 * 24 * 60 * 60 * 1e3;
344
- if (sessionAge > maxSessionAge) {
345
- this.logger.log("EMBEDDED_PROVIDER", "Session expired", {
346
- sessionAge,
347
- maxSessionAge,
348
- lastUsed: new Date(session.lastUsed).toISOString()
361
+ if (!session.authenticatorExpiresAt) {
362
+ this.logger.log("EMBEDDED_PROVIDER", "Session invalid - missing authenticator timing", {
363
+ sessionId: session.sessionId
364
+ });
365
+ return false;
366
+ }
367
+ if (Date.now() >= session.authenticatorExpiresAt) {
368
+ this.logger.log("EMBEDDED_PROVIDER", "Authenticator expired, session invalid", {
369
+ authenticatorExpiresAt: new Date(session.authenticatorExpiresAt).toISOString(),
370
+ now: (/* @__PURE__ */ new Date()).toISOString()
349
371
  });
350
372
  return false;
351
373
  }
352
374
  this.logger.log("EMBEDDED_PROVIDER", "Session is valid", {
353
375
  sessionId: session.sessionId,
354
376
  walletId: session.walletId,
355
- lastUsed: new Date(session.lastUsed).toISOString()
377
+ authenticatorExpires: new Date(session.authenticatorExpiresAt).toISOString()
356
378
  });
357
379
  return true;
358
380
  }
@@ -421,9 +443,11 @@ var EmbeddedProvider = class {
421
443
  platform: platformName
422
444
  });
423
445
  const base64urlPublicKey = (0, import_base64url.base64urlEncode)(import_bs58.default.decode(stamperInfo.publicKey));
446
+ const expiresAtMs = Date.now() + AUTHENTICATOR_EXPIRATION_TIME_MS;
447
+ const username = `user-${shortPubKey}`;
424
448
  const { organizationId } = await tempClient.createOrganization(organizationName, [
425
449
  {
426
- username: `user-${shortPubKey}`,
450
+ username,
427
451
  role: "ADMIN",
428
452
  authenticators: [
429
453
  {
@@ -431,12 +455,14 @@ var EmbeddedProvider = class {
431
455
  authenticatorKind: "keypair",
432
456
  publicKey: base64urlPublicKey,
433
457
  algorithm: "Ed25519"
458
+ // Commented for now until KMS supports fully expirable organizations
459
+ // expiresAtMs: expiresAtMs,
434
460
  }
435
461
  ]
436
462
  }
437
463
  ]);
438
464
  this.logger.info("EMBEDDED_PROVIDER", "Organization created", { organizationId });
439
- return { organizationId, stamperInfo };
465
+ return { organizationId, stamperInfo, expiresAtMs, username };
440
466
  }
441
467
  async connect(authOptions) {
442
468
  try {
@@ -465,8 +491,8 @@ var EmbeddedProvider = class {
465
491
  }
466
492
  this.validateAuthOptions(authOptions);
467
493
  this.logger.info("EMBEDDED_PROVIDER", "No existing connection, creating new auth flow");
468
- const { organizationId, stamperInfo } = await this.createOrganizationAndStamper();
469
- const session = await this.handleAuthFlow(organizationId, stamperInfo, authOptions);
494
+ const { organizationId, stamperInfo, expiresAtMs, username } = await this.createOrganizationAndStamper();
495
+ const session = await this.handleAuthFlow(organizationId, stamperInfo, authOptions, expiresAtMs, username);
470
496
  if (!session) {
471
497
  return {
472
498
  addresses: [],
@@ -478,6 +504,7 @@ var EmbeddedProvider = class {
478
504
  await this.storage.saveSession(session);
479
505
  }
480
506
  await this.initializeClientFromSession(session);
507
+ await this.ensureValidAuthenticator();
481
508
  const result = {
482
509
  walletId: this.walletId,
483
510
  addresses: this.addresses,
@@ -543,15 +570,19 @@ var EmbeddedProvider = class {
543
570
  if (!this.client || !this.walletId) {
544
571
  throw new Error("Not connected");
545
572
  }
573
+ await this.ensureValidAuthenticator();
546
574
  this.logger.info("EMBEDDED_PROVIDER", "Signing message", {
547
575
  walletId: this.walletId,
548
576
  message: params.message
549
577
  });
550
578
  const parsedMessage = (0, import_parsers.parseMessage)(params.message);
579
+ const session = await this.storage.getSession();
580
+ const derivationIndex = session?.accountDerivationIndex ?? 0;
551
581
  const rawResponse = await this.client.signMessage({
552
582
  walletId: this.walletId,
553
583
  message: parsedMessage.base64url,
554
- networkId: params.networkId
584
+ networkId: params.networkId,
585
+ derivationIndex
555
586
  });
556
587
  this.logger.info("EMBEDDED_PROVIDER", "Message signed successfully", {
557
588
  walletId: this.walletId,
@@ -563,19 +594,24 @@ var EmbeddedProvider = class {
563
594
  if (!this.client || !this.walletId) {
564
595
  throw new Error("Not connected");
565
596
  }
597
+ await this.ensureValidAuthenticator();
566
598
  this.logger.info("EMBEDDED_PROVIDER", "Signing and sending transaction", {
567
599
  walletId: this.walletId,
568
600
  networkId: params.networkId
569
601
  });
570
602
  const parsedTransaction = await (0, import_parsers.parseTransaction)(params.transaction, params.networkId);
603
+ const session = await this.storage.getSession();
604
+ const derivationIndex = session?.accountDerivationIndex ?? 0;
571
605
  this.logger.log("EMBEDDED_PROVIDER", "Parsed transaction for signing", {
572
606
  walletId: this.walletId,
573
- transaction: parsedTransaction
607
+ transaction: parsedTransaction,
608
+ derivationIndex
574
609
  });
575
610
  const rawResponse = await this.client.signAndSendTransaction({
576
611
  walletId: this.walletId,
577
612
  transaction: parsedTransaction.base64url,
578
- networkId: params.networkId
613
+ networkId: params.networkId,
614
+ derivationIndex
579
615
  });
580
616
  this.logger.info("EMBEDDED_PROVIDER", "Transaction signed and sent successfully", {
581
617
  walletId: this.walletId,
@@ -596,20 +632,20 @@ var EmbeddedProvider = class {
596
632
  * It handles app-wallet creation directly or routes to JWT/redirect authentication for user-wallets.
597
633
  * Returns null for redirect flows since they don't complete synchronously.
598
634
  */
599
- async handleAuthFlow(organizationId, stamperInfo, authOptions) {
635
+ async handleAuthFlow(organizationId, stamperInfo, authOptions, expiresAtMs, username) {
600
636
  if (this.config.embeddedWalletType === "user-wallet") {
601
637
  this.logger.info("EMBEDDED_PROVIDER", "Creating user-wallet, routing authentication", {
602
638
  authProvider: authOptions?.provider || "phantom-connect"
603
639
  });
604
640
  if (authOptions?.provider === "jwt") {
605
- return await this.handleJWTAuth(organizationId, stamperInfo, authOptions);
641
+ return await this.handleJWTAuth(organizationId, stamperInfo, authOptions, expiresAtMs, username);
606
642
  } else {
607
643
  this.logger.info("EMBEDDED_PROVIDER", "Starting redirect-based authentication flow", {
608
644
  organizationId,
609
645
  parentOrganizationId: this.config.organizationId,
610
646
  provider: authOptions?.provider
611
647
  });
612
- return await this.handleRedirectAuth(organizationId, stamperInfo, authOptions);
648
+ return await this.handleRedirectAuth(organizationId, stamperInfo, authOptions, username);
613
649
  }
614
650
  } else {
615
651
  this.logger.info("EMBEDDED_PROVIDER", "Creating app-wallet", {
@@ -632,9 +668,15 @@ var EmbeddedProvider = class {
632
668
  stamperInfo,
633
669
  authProvider: "app-wallet",
634
670
  userInfo: { embeddedWalletType: this.config.embeddedWalletType },
671
+ accountDerivationIndex: 0,
672
+ // App wallets default to index 0
635
673
  status: "completed",
636
674
  createdAt: now,
637
- lastUsed: now
675
+ lastUsed: now,
676
+ authenticatorCreatedAt: now,
677
+ authenticatorExpiresAt: expiresAtMs,
678
+ lastRenewalAttempt: void 0,
679
+ username
638
680
  };
639
681
  await this.storage.saveSession(session);
640
682
  this.logger.info("EMBEDDED_PROVIDER", "App-wallet created successfully", { walletId, organizationId });
@@ -645,7 +687,7 @@ var EmbeddedProvider = class {
645
687
  * We use this method to handle JWT-based authentication for user-wallets.
646
688
  * It authenticates using the provided JWT token and creates a completed session.
647
689
  */
648
- async handleJWTAuth(organizationId, stamperInfo, authOptions) {
690
+ async handleJWTAuth(organizationId, stamperInfo, authOptions, expiresAtMs, username) {
649
691
  this.logger.info("EMBEDDED_PROVIDER", "Using JWT authentication flow");
650
692
  if (!authOptions.jwtToken) {
651
693
  this.logger.error("EMBEDDED_PROVIDER", "JWT token missing for JWT authentication");
@@ -668,9 +710,14 @@ var EmbeddedProvider = class {
668
710
  stamperInfo,
669
711
  authProvider: authResult.provider,
670
712
  userInfo: authResult.userInfo,
713
+ accountDerivationIndex: authResult.accountDerivationIndex,
671
714
  status: "completed",
672
715
  createdAt: now,
673
- lastUsed: now
716
+ lastUsed: now,
717
+ authenticatorCreatedAt: now,
718
+ authenticatorExpiresAt: expiresAtMs,
719
+ lastRenewalAttempt: void 0,
720
+ username
674
721
  };
675
722
  this.logger.log("EMBEDDED_PROVIDER", "Saving JWT session");
676
723
  await this.storage.saveSession(session);
@@ -681,7 +728,7 @@ var EmbeddedProvider = class {
681
728
  * It saves a temporary session before redirecting to prevent losing state during the redirect flow.
682
729
  * Session timestamp is updated before redirect to prevent race conditions.
683
730
  */
684
- async handleRedirectAuth(organizationId, stamperInfo, authOptions) {
731
+ async handleRedirectAuth(organizationId, stamperInfo, authOptions, username) {
685
732
  this.logger.info("EMBEDDED_PROVIDER", "Using Phantom Connect authentication flow (redirect-based)", {
686
733
  provider: authOptions?.provider,
687
734
  hasRedirectUrl: !!this.config.authOptions?.redirectUrl,
@@ -697,9 +744,15 @@ var EmbeddedProvider = class {
697
744
  stamperInfo,
698
745
  authProvider: "phantom-connect",
699
746
  userInfo: { provider: authOptions?.provider },
747
+ accountDerivationIndex: void 0,
748
+ // Will be set when redirect completes
700
749
  status: "pending",
701
750
  createdAt: now,
702
- lastUsed: now
751
+ lastUsed: now,
752
+ authenticatorCreatedAt: now,
753
+ authenticatorExpiresAt: now + AUTHENTICATOR_EXPIRATION_TIME_MS,
754
+ lastRenewalAttempt: void 0,
755
+ username: username || `user-${stamperInfo.keyId.substring(0, 8)}`
703
756
  };
704
757
  this.logger.log("EMBEDDED_PROVIDER", "Saving temporary session before redirect", {
705
758
  sessionId: tempSession.sessionId,
@@ -731,6 +784,7 @@ var EmbeddedProvider = class {
731
784
  });
732
785
  tempSession.walletId = authResult.walletId;
733
786
  tempSession.authProvider = authResult.provider || tempSession.authProvider;
787
+ tempSession.accountDerivationIndex = authResult.accountDerivationIndex;
734
788
  tempSession.status = "completed";
735
789
  tempSession.lastUsed = Date.now();
736
790
  await this.storage.saveSession(tempSession);
@@ -746,16 +800,119 @@ var EmbeddedProvider = class {
746
800
  }
747
801
  session.walletId = authResult.walletId;
748
802
  session.authProvider = authResult.provider || session.authProvider;
803
+ session.accountDerivationIndex = authResult.accountDerivationIndex;
749
804
  session.status = "completed";
750
805
  session.lastUsed = Date.now();
751
806
  await this.storage.saveSession(session);
752
807
  await this.initializeClientFromSession(session);
808
+ await this.ensureValidAuthenticator();
753
809
  return {
754
810
  walletId: this.walletId,
755
811
  addresses: this.addresses,
756
812
  status: "completed"
757
813
  };
758
814
  }
815
+ /*
816
+ * Ensures the authenticator is valid and performs renewal if needed.
817
+ * The renewal of the authenticator can only happen meanwhile the previous authenticator is still valid.
818
+ */
819
+ async ensureValidAuthenticator() {
820
+ const session = await this.storage.getSession();
821
+ if (!session) {
822
+ throw new Error("No active session found");
823
+ }
824
+ const now = Date.now();
825
+ if (!session.authenticatorExpiresAt) {
826
+ this.logger.warn("EMBEDDED_PROVIDER", "Session missing authenticator timing - treating as invalid session");
827
+ await this.disconnect();
828
+ throw new Error("Invalid session - missing authenticator timing");
829
+ }
830
+ const timeUntilExpiry = session.authenticatorExpiresAt - now;
831
+ this.logger.log("EMBEDDED_PROVIDER", "Checking authenticator expiration", {
832
+ expiresAt: new Date(session.authenticatorExpiresAt).toISOString(),
833
+ timeUntilExpiry
834
+ });
835
+ if (timeUntilExpiry <= 0) {
836
+ this.logger.error("EMBEDDED_PROVIDER", "Authenticator has expired, disconnecting");
837
+ await this.disconnect();
838
+ throw new Error("Authenticator expired");
839
+ }
840
+ const renewalWindow = AUTHENTICATOR_RENEWAL_WINDOW_MS;
841
+ if (timeUntilExpiry <= renewalWindow) {
842
+ this.logger.info("EMBEDDED_PROVIDER", "Authenticator needs renewal", {
843
+ expiresAt: new Date(session.authenticatorExpiresAt).toISOString(),
844
+ timeUntilExpiry,
845
+ renewalWindow
846
+ });
847
+ try {
848
+ await this.renewAuthenticator(session);
849
+ this.logger.info("EMBEDDED_PROVIDER", "Authenticator renewed successfully");
850
+ } catch (error) {
851
+ this.logger.error("EMBEDDED_PROVIDER", "Failed to renew authenticator", {
852
+ error: error instanceof Error ? error.message : String(error)
853
+ });
854
+ }
855
+ }
856
+ }
857
+ /*
858
+ * We use this method to perform silent authenticator renewal.
859
+ * It generates a new keypair, creates a new authenticator, and switches to it.
860
+ */
861
+ async renewAuthenticator(session) {
862
+ if (!this.client) {
863
+ throw new Error("Client not initialized");
864
+ }
865
+ this.logger.info("EMBEDDED_PROVIDER", "Starting authenticator renewal");
866
+ try {
867
+ const newKeyInfo = await this.stamper.rotateKeyPair();
868
+ this.logger.log("EMBEDDED_PROVIDER", "Generated new keypair for renewal", {
869
+ newKeyId: newKeyInfo.keyId,
870
+ newPublicKey: newKeyInfo.publicKey
871
+ });
872
+ const base64urlPublicKey = (0, import_base64url.base64urlEncode)(import_bs58.default.decode(newKeyInfo.publicKey));
873
+ const expiresAtMs = Date.now() + AUTHENTICATOR_EXPIRATION_TIME_MS;
874
+ let authenticatorResult;
875
+ try {
876
+ authenticatorResult = await this.client.createAuthenticator({
877
+ organizationId: session.organizationId,
878
+ username: session.username,
879
+ authenticatorName: `auth-${newKeyInfo.keyId.substring(0, 8)}`,
880
+ authenticator: {
881
+ authenticatorName: `auth-${newKeyInfo.keyId.substring(0, 8)}`,
882
+ authenticatorKind: "keypair",
883
+ publicKey: base64urlPublicKey,
884
+ algorithm: "Ed25519"
885
+ // Commented for now until KMS supports fully expiring organizations
886
+ // expiresAtMs: expiresAtMs,
887
+ },
888
+ replaceExpirable: true
889
+ });
890
+ } catch (error) {
891
+ this.logger.error("EMBEDDED_PROVIDER", "Failed to create new authenticator", {
892
+ error: error instanceof Error ? error.message : String(error)
893
+ });
894
+ await this.stamper.rollbackRotation();
895
+ throw new Error(`Failed to create new authenticator: ${error instanceof Error ? error.message : String(error)}`);
896
+ }
897
+ this.logger.info("EMBEDDED_PROVIDER", "Created new authenticator", {
898
+ authenticatorId: authenticatorResult.id
899
+ });
900
+ await this.stamper.commitRotation(authenticatorResult.id || "unknown");
901
+ const now = Date.now();
902
+ session.stamperInfo = newKeyInfo;
903
+ session.authenticatorCreatedAt = now;
904
+ session.authenticatorExpiresAt = expiresAtMs;
905
+ session.lastRenewalAttempt = now;
906
+ await this.storage.saveSession(session);
907
+ this.logger.info("EMBEDDED_PROVIDER", "Authenticator renewal completed successfully", {
908
+ newKeyId: newKeyInfo.keyId,
909
+ expiresAt: new Date(expiresAtMs).toISOString()
910
+ });
911
+ } catch (error) {
912
+ await this.stamper.rollbackRotation();
913
+ throw error;
914
+ }
915
+ }
759
916
  /*
760
917
  * We use this method to initialize the PhantomClient and fetch wallet addresses from a completed session.
761
918
  * This is the final step that sets up the provider's client state and retrieves available addresses.
@@ -781,6 +938,8 @@ var EmbeddedProvider = class {
781
938
  };
782
939
  // Annotate the CommonJS export names for ESM import in node:
783
940
  0 && (module.exports = {
941
+ AUTHENTICATOR_EXPIRATION_TIME_MS,
942
+ AUTHENTICATOR_RENEWAL_WINDOW_MS,
784
943
  EmbeddedProvider,
785
944
  JWTAuth,
786
945
  generateSessionId,