@account-kit/signer 4.16.1-alpha.3 → 4.18.0-alpha.3

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 (80) hide show
  1. package/dist/esm/base.d.ts +16 -32
  2. package/dist/esm/base.js +127 -122
  3. package/dist/esm/base.js.map +1 -1
  4. package/dist/esm/client/base.d.ts +3 -36
  5. package/dist/esm/client/base.js +15 -20
  6. package/dist/esm/client/base.js.map +1 -1
  7. package/dist/esm/client/index.d.ts +27 -40
  8. package/dist/esm/client/index.js +41 -164
  9. package/dist/esm/client/index.js.map +1 -1
  10. package/dist/esm/client/types.d.ts +27 -65
  11. package/dist/esm/client/types.js.map +1 -1
  12. package/dist/esm/errors.d.ts +0 -6
  13. package/dist/esm/errors.js +0 -18
  14. package/dist/esm/errors.js.map +1 -1
  15. package/dist/esm/index.d.ts +1 -1
  16. package/dist/esm/index.js +1 -1
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/metrics.d.ts +3 -0
  19. package/dist/esm/metrics.js.map +1 -1
  20. package/dist/esm/oauth.d.ts +5 -4
  21. package/dist/esm/oauth.js +16 -6
  22. package/dist/esm/oauth.js.map +1 -1
  23. package/dist/esm/session/manager.d.ts +0 -1
  24. package/dist/esm/session/manager.js +4 -0
  25. package/dist/esm/session/manager.js.map +1 -1
  26. package/dist/esm/session/types.d.ts +1 -1
  27. package/dist/esm/session/types.js.map +1 -1
  28. package/dist/esm/signer.d.ts +8 -5
  29. package/dist/esm/signer.js.map +1 -1
  30. package/dist/esm/types.d.ts +2 -9
  31. package/dist/esm/types.js +1 -5
  32. package/dist/esm/types.js.map +1 -1
  33. package/dist/esm/version.d.ts +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/dist/esm/version.js.map +1 -1
  36. package/dist/types/base.d.ts +16 -32
  37. package/dist/types/base.d.ts.map +1 -1
  38. package/dist/types/client/base.d.ts +3 -36
  39. package/dist/types/client/base.d.ts.map +1 -1
  40. package/dist/types/client/index.d.ts +27 -40
  41. package/dist/types/client/index.d.ts.map +1 -1
  42. package/dist/types/client/types.d.ts +27 -65
  43. package/dist/types/client/types.d.ts.map +1 -1
  44. package/dist/types/errors.d.ts +0 -6
  45. package/dist/types/errors.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +1 -1
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/metrics.d.ts +3 -0
  49. package/dist/types/metrics.d.ts.map +1 -1
  50. package/dist/types/oauth.d.ts +5 -4
  51. package/dist/types/oauth.d.ts.map +1 -1
  52. package/dist/types/session/manager.d.ts +0 -1
  53. package/dist/types/session/manager.d.ts.map +1 -1
  54. package/dist/types/session/types.d.ts +1 -1
  55. package/dist/types/session/types.d.ts.map +1 -1
  56. package/dist/types/signer.d.ts +8 -5
  57. package/dist/types/signer.d.ts.map +1 -1
  58. package/dist/types/types.d.ts +2 -9
  59. package/dist/types/types.d.ts.map +1 -1
  60. package/dist/types/version.d.ts +1 -1
  61. package/package.json +4 -4
  62. package/src/base.ts +120 -166
  63. package/src/client/base.ts +26 -65
  64. package/src/client/index.ts +46 -174
  65. package/src/client/types.ts +31 -75
  66. package/src/errors.ts +1 -11
  67. package/src/index.ts +1 -5
  68. package/src/metrics.ts +2 -1
  69. package/src/oauth.ts +22 -8
  70. package/src/session/manager.ts +8 -7
  71. package/src/session/types.ts +1 -1
  72. package/src/signer.ts +13 -7
  73. package/src/types.ts +1 -9
  74. package/src/version.ts +1 -1
  75. package/dist/esm/utils/parseMfaError.d.ts +0 -2
  76. package/dist/esm/utils/parseMfaError.js +0 -15
  77. package/dist/esm/utils/parseMfaError.js.map +0 -1
  78. package/dist/types/utils/parseMfaError.d.ts +0 -3
  79. package/dist/types/utils/parseMfaError.d.ts.map +0 -1
  80. package/src/utils/parseMfaError.ts +0 -15
@@ -12,6 +12,12 @@ export interface BaseAlchemySignerParams<TClient extends BaseSignerClient> {
12
12
  sessionConfig?: Omit<SessionManagerParams, "client">;
13
13
  initialError?: ErrorInfo;
14
14
  }
15
+ export type EmailConfig = {
16
+ mode?: "MAGIC_LINK" | "OTP";
17
+ };
18
+ export type SignerConfig = {
19
+ email: EmailConfig;
20
+ };
15
21
  /**
16
22
  * Base abstract class for Alchemy Signer, providing authentication and session management for smart accounts.
17
23
  * Implements the `SmartAccountAuthenticator` interface and handles various signer events.
@@ -21,6 +27,7 @@ export declare abstract class BaseAlchemySigner<TClient extends BaseSignerClient
21
27
  inner: TClient;
22
28
  private sessionManager;
23
29
  private store;
30
+ private config;
24
31
  /**
25
32
  * Initializes an instance with the provided client and session configuration.
26
33
  * This function sets up the internal store, initializes the session manager,
@@ -274,36 +281,6 @@ export declare abstract class BaseAlchemySigner<TClient extends BaseSignerClient
274
281
  * @returns {Promise<Authorization<number, true>> | undefined} a promise that resolves to the authorization with the signature
275
282
  */
276
283
  signAuthorization: (unsignedAuthorization: Authorization<number, false>) => Promise<Authorization<number, true>>;
277
- /**
278
- * Gets the current MFA status
279
- *
280
- * @example
281
- * ```ts
282
- * import { AlchemyWebSigner } from "@account-kit/signer";
283
- *
284
- * const signer = new AlchemyWebSigner({
285
- * client: {
286
- * connection: {
287
- * rpcUrl: "/api/rpc",
288
- * },
289
- * iframeConfig: {
290
- * iframeContainerId: "alchemy-signer-iframe-container",
291
- * },
292
- * },
293
- * });
294
- *
295
- * const mfaStatus = signer.getMfaStatus();
296
- * if (mfaStatus === AlchemyMfaStatus.REQUIRED) {
297
- * // Handle MFA requirement
298
- * }
299
- * ```
300
- *
301
- * @returns {{ mfaRequired: boolean; mfaFactorId?: string }} The current MFA status
302
- */
303
- getMfaStatus: () => {
304
- mfaRequired: boolean;
305
- mfaFactorId?: string;
306
- };
307
284
  private unpackSignRawMessageBytes;
308
285
  /**
309
286
  * Unauthenticated call to look up a user's organizationId by email
@@ -439,11 +416,18 @@ export declare abstract class BaseAlchemySigner<TClient extends BaseSignerClient
439
416
  private authenticateWithEmail;
440
417
  private authenticateWithPasskey;
441
418
  private authenticateWithOauth;
419
+ private authenticateWithJwt;
442
420
  private authenticateWithOtp;
443
421
  private handleOauthReturn;
444
422
  private getExpirationSeconds;
445
423
  private registerListeners;
446
424
  private emitNewUserEvent;
447
- private initOrCreateEmailUser;
448
- private completeEmailAuth;
425
+ protected initConfig: () => Promise<SignerConfig>;
426
+ /**
427
+ * Returns the signer configuration while fetching it if it's not already initialized.
428
+ *
429
+ * @returns {Promise<SignerConfig>} A promise that resolves to the signer configuration
430
+ */
431
+ getConfig: () => Promise<SignerConfig>;
432
+ protected fetchConfig: () => Promise<SignerConfig>;
449
433
  }
package/dist/esm/base.js CHANGED
@@ -50,6 +50,12 @@ export class BaseAlchemySigner {
50
50
  writable: true,
51
51
  value: void 0
52
52
  });
53
+ Object.defineProperty(this, "config", {
54
+ enumerable: true,
55
+ configurable: true,
56
+ writable: true,
57
+ value: void 0
58
+ });
53
59
  /**
54
60
  * Allows you to subscribe to events emitted by the signer
55
61
  *
@@ -81,8 +87,6 @@ export class BaseAlchemySigner {
81
87
  if (isNewUser)
82
88
  listener();
83
89
  }, { fireImmediately: true });
84
- case "mfaStatusChanged":
85
- return this.store.subscribe(({ mfaStatus }) => mfaStatus, (mfaStatus) => listener(mfaStatus), { fireImmediately: true });
86
90
  default:
87
91
  assertNever(event, `Unknown event type ${event}`);
88
92
  }
@@ -168,6 +172,8 @@ export class BaseAlchemySigner {
168
172
  return this.handleOauthReturn(params);
169
173
  case "otp":
170
174
  return this.authenticateWithOtp(params);
175
+ case "custom-jwt":
176
+ return this.authenticateWithJwt(params);
171
177
  default:
172
178
  assertNever(type, `Unknown auth type: ${type}`);
173
179
  }
@@ -217,6 +223,16 @@ export class BaseAlchemySigner {
217
223
  });
218
224
  return;
219
225
  }
226
+ case "custom-jwt": {
227
+ SignerLogger.trackEvent({
228
+ name: "signer_authnticate",
229
+ data: {
230
+ authType: "custom-jwt",
231
+ provider: params.authProviderId,
232
+ },
233
+ });
234
+ return;
235
+ }
220
236
  case "oauth":
221
237
  SignerLogger.trackEvent({
222
238
  name: "signer_authnticate",
@@ -479,40 +495,6 @@ export class BaseAlchemySigner {
479
495
  return { ...unsignedAuthorization, ...signature };
480
496
  })
481
497
  });
482
- /**
483
- * Gets the current MFA status
484
- *
485
- * @example
486
- * ```ts
487
- * import { AlchemyWebSigner } from "@account-kit/signer";
488
- *
489
- * const signer = new AlchemyWebSigner({
490
- * client: {
491
- * connection: {
492
- * rpcUrl: "/api/rpc",
493
- * },
494
- * iframeConfig: {
495
- * iframeContainerId: "alchemy-signer-iframe-container",
496
- * },
497
- * },
498
- * });
499
- *
500
- * const mfaStatus = signer.getMfaStatus();
501
- * if (mfaStatus === AlchemyMfaStatus.REQUIRED) {
502
- * // Handle MFA requirement
503
- * }
504
- * ```
505
- *
506
- * @returns {{ mfaRequired: boolean; mfaFactorId?: string }} The current MFA status
507
- */
508
- Object.defineProperty(this, "getMfaStatus", {
509
- enumerable: true,
510
- configurable: true,
511
- writable: true,
512
- value: () => {
513
- return this.store.getState().mfaStatus;
514
- }
515
- });
516
498
  Object.defineProperty(this, "unpackSignRawMessageBytes", {
517
499
  enumerable: true,
518
500
  configurable: true,
@@ -713,36 +695,61 @@ export class BaseAlchemySigner {
713
695
  configurable: true,
714
696
  writable: true,
715
697
  value: async (params) => {
716
- if ("bundle" in params) {
717
- return this.completeEmailAuth(params);
718
- }
719
- if (!("email" in params)) {
720
- throw new Error("Email is required");
698
+ if ("email" in params) {
699
+ const existingUser = await this.getUser(params.email);
700
+ const expirationSeconds = this.getExpirationSeconds();
701
+ const { orgId, otpId } = existingUser
702
+ ? await this.inner.initEmailAuth({
703
+ email: params.email,
704
+ emailMode: params.emailMode,
705
+ expirationSeconds,
706
+ redirectParams: params.redirectParams,
707
+ })
708
+ : await this.inner.createAccount({
709
+ type: "email",
710
+ email: params.email,
711
+ emailMode: params.emailMode,
712
+ expirationSeconds,
713
+ redirectParams: params.redirectParams,
714
+ });
715
+ this.sessionManager.setTemporarySession({
716
+ orgId,
717
+ isNewUser: !existingUser,
718
+ });
719
+ this.store.setState({
720
+ status: AlchemySignerStatus.AWAITING_EMAIL_AUTH,
721
+ otpId,
722
+ error: null,
723
+ });
724
+ // We wait for the session manager to emit a connected event if
725
+ // cross tab sessions are permitted
726
+ return new Promise((resolve) => {
727
+ const removeListener = this.sessionManager.on("connected", (session) => {
728
+ resolve(session.user);
729
+ removeListener();
730
+ });
731
+ });
721
732
  }
722
- const { orgId, otpId, multiFactors, isNewUser } = await this.initOrCreateEmailUser(params.email, params.emailMode ?? "otp", params.multiFactors, params.redirectParams);
723
- const isMfaRequired = multiFactors ? multiFactors?.length > 0 : false;
724
- this.sessionManager.setTemporarySession({
725
- orgId,
726
- isNewUser,
727
- isMfaRequired,
728
- });
729
- this.store.setState({
730
- status: AlchemySignerStatus.AWAITING_EMAIL_AUTH,
731
- otpId,
732
- error: null,
733
- mfaStatus: {
734
- mfaRequired: isMfaRequired,
735
- mfaFactorId: multiFactors?.[0]?.multiFactorId,
736
- },
737
- });
738
- // We wait for the session manager to emit a connected event if
739
- // cross tab sessions are permitted
740
- return new Promise((resolve) => {
741
- const removeListener = this.sessionManager.on("connected", (session) => {
742
- resolve(session.user);
743
- removeListener();
733
+ else {
734
+ const temporarySession = params.orgId
735
+ ? { orgId: params.orgId }
736
+ : this.sessionManager.getTemporarySession();
737
+ if (!temporarySession) {
738
+ this.store.setState({
739
+ status: AlchemySignerStatus.DISCONNECTED,
740
+ });
741
+ throw new Error("Could not find email auth init session!");
742
+ }
743
+ const user = await this.inner.completeAuthWithBundle({
744
+ bundle: params.bundle,
745
+ orgId: temporarySession.orgId,
746
+ connectedEventName: "connectedEmail",
747
+ authenticatingType: "email",
744
748
  });
745
- });
749
+ // fire new user event
750
+ this.emitNewUserEvent(params.isNewUser);
751
+ return user;
752
+ }
746
753
  }
747
754
  });
748
755
  Object.defineProperty(this, "authenticateWithPasskey", {
@@ -797,13 +804,32 @@ export class BaseAlchemySigner {
797
804
  }
798
805
  }
799
806
  });
807
+ Object.defineProperty(this, "authenticateWithJwt", {
808
+ enumerable: true,
809
+ configurable: true,
810
+ writable: true,
811
+ value: async (args) => {
812
+ const { credentialBundle, orgId, isSignUp } = await this.inner.submitJwt({
813
+ jwt: args.jwt,
814
+ authProvider: args.authProviderId,
815
+ });
816
+ const user = await this.inner.completeAuthWithBundle({
817
+ bundle: credentialBundle,
818
+ orgId: orgId,
819
+ connectedEventName: "connectedJwt",
820
+ authenticatingType: "custom-jwt",
821
+ });
822
+ this.emitNewUserEvent(isSignUp);
823
+ return user;
824
+ }
825
+ });
800
826
  Object.defineProperty(this, "authenticateWithOtp", {
801
827
  enumerable: true,
802
828
  configurable: true,
803
829
  writable: true,
804
830
  value: async (args) => {
805
831
  const tempSession = this.sessionManager.getTemporarySession();
806
- const { orgId, isNewUser, isMfaRequired } = tempSession ?? {};
832
+ const { orgId, isNewUser } = tempSession ?? {};
807
833
  const { otpId } = this.store.getState();
808
834
  if (!orgId) {
809
835
  throw new Error("orgId not found in session");
@@ -811,15 +837,11 @@ export class BaseAlchemySigner {
811
837
  if (!otpId) {
812
838
  throw new Error("otpId not found in session");
813
839
  }
814
- if (isMfaRequired && !args.multiFactors) {
815
- throw new Error(`MFA is required.`);
816
- }
817
840
  const { bundle } = await this.inner.submitOtpCode({
818
841
  orgId,
819
842
  otpId,
820
843
  otpCode: args.otpCode,
821
844
  expirationSeconds: this.getExpirationSeconds(),
822
- multiFactors: args.multiFactors,
823
845
  });
824
846
  const user = await this.inner.completeAuthWithBundle({
825
847
  bundle,
@@ -904,6 +926,8 @@ export class BaseAlchemySigner {
904
926
  case "otp":
905
927
  case "otpVerify":
906
928
  return AlchemySignerStatus.AWAITING_OTP_AUTH;
929
+ case "custom-jwt":
930
+ return AlchemySignerStatus.AUTHENTICATING_JWT;
907
931
  default:
908
932
  assertNever(type, "unhandled authenticating type");
909
933
  }
@@ -929,15 +953,44 @@ export class BaseAlchemySigner {
929
953
  this.store.setState({ isNewUser });
930
954
  }
931
955
  });
956
+ Object.defineProperty(this, "initConfig", {
957
+ enumerable: true,
958
+ configurable: true,
959
+ writable: true,
960
+ value: async () => {
961
+ this.config = this.fetchConfig();
962
+ return this.config;
963
+ }
964
+ });
965
+ /**
966
+ * Returns the signer configuration while fetching it if it's not already initialized.
967
+ *
968
+ * @returns {Promise<SignerConfig>} A promise that resolves to the signer configuration
969
+ */
970
+ Object.defineProperty(this, "getConfig", {
971
+ enumerable: true,
972
+ configurable: true,
973
+ writable: true,
974
+ value: async () => {
975
+ if (!this.config) {
976
+ return this.initConfig();
977
+ }
978
+ return this.config;
979
+ }
980
+ });
981
+ Object.defineProperty(this, "fetchConfig", {
982
+ enumerable: true,
983
+ configurable: true,
984
+ writable: true,
985
+ value: async () => {
986
+ return this.inner.request("/v1/signer-config", {});
987
+ }
988
+ });
932
989
  this.inner = client;
933
990
  this.store = createStore(subscribeWithSelector(() => ({
934
991
  user: null,
935
992
  status: AlchemySignerStatus.INITIALIZING,
936
993
  error: initialError ?? null,
937
- mfaStatus: {
938
- mfaRequired: false,
939
- mfaFactorId: undefined,
940
- },
941
994
  })));
942
995
  // NOTE: it's important that the session manager share a client
943
996
  // with the signer. The SessionManager leverages the Signer's client
@@ -950,55 +1003,7 @@ export class BaseAlchemySigner {
950
1003
  this.registerListeners();
951
1004
  // then initialize so that we can catch those events
952
1005
  this.sessionManager.initialize();
953
- }
954
- async initOrCreateEmailUser(email, emailMode, multiFactors, redirectParams) {
955
- const existingUser = await this.getUser(email);
956
- const expirationSeconds = this.getExpirationSeconds();
957
- if (existingUser) {
958
- const { orgId, otpId, multiFactors: mfaFactors, } = await this.inner.initEmailAuth({
959
- email: email,
960
- emailMode: emailMode,
961
- expirationSeconds,
962
- redirectParams: redirectParams,
963
- multiFactors,
964
- });
965
- return {
966
- orgId,
967
- otpId,
968
- multiFactors: mfaFactors,
969
- isNewUser: false,
970
- };
971
- }
972
- const { orgId, otpId } = await this.inner.createAccount({
973
- type: "email",
974
- email,
975
- emailMode,
976
- expirationSeconds,
977
- redirectParams,
978
- });
979
- return {
980
- orgId,
981
- otpId,
982
- isNewUser: true,
983
- };
984
- }
985
- async completeEmailAuth(params) {
986
- const temporarySession = params.orgId
987
- ? { orgId: params.orgId }
988
- : this.sessionManager.getTemporarySession();
989
- if (!temporarySession) {
990
- this.store.setState({ status: AlchemySignerStatus.DISCONNECTED });
991
- throw new Error("Could not find email auth init session!");
992
- }
993
- const user = await this.inner.completeAuthWithBundle({
994
- bundle: params.bundle,
995
- orgId: temporarySession.orgId,
996
- connectedEventName: "connectedEmail",
997
- authenticatingType: "email",
998
- });
999
- // fire new user event
1000
- this.emitNewUserEvent(params.isNewUser);
1001
- return user;
1006
+ this.config = this.fetchConfig();
1002
1007
  }
1003
1008
  }
1004
1009
  function toErrorInfo(error) {