@getpara/core-sdk 3.4.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -124,6 +124,24 @@ if (typeof global !== "undefined") {
124
124
  self.global = self.global || self;
125
125
  }
126
126
  const { pki, jsbn } = import_node_forge.default;
127
+ const DEFAULT_SIGNING_RESULT_ERROR_MESSAGE = "Signing failed before a signature was produced.";
128
+ function getPendingTransactionId(signRes) {
129
+ return typeof signRes.pendingTransactionId === "string" && signRes.pendingTransactionId.length > 0 ? signRes.pendingTransactionId : void 0;
130
+ }
131
+ function requireSuccessfulSignatureResult(signRes) {
132
+ if (typeof signRes.signature === "string" && signRes.signature.length > 0) {
133
+ return { signature: signRes.signature };
134
+ }
135
+ throw new import_errors.SigningResultError(
136
+ typeof signRes.message === "string" && signRes.message.length > 0 ? signRes.message : DEFAULT_SIGNING_RESULT_ERROR_MESSAGE,
137
+ {
138
+ code: signRes.code,
139
+ status: signRes.status,
140
+ responseURL: signRes.responseURL,
141
+ data: signRes.data
142
+ }
143
+ );
144
+ }
127
145
  const _ParaCore = class _ParaCore {
128
146
  constructor(envOrApiKey, apiKeyOrOpts, opts) {
129
147
  __privateAdd(this, _ParaCore_instances);
@@ -1825,6 +1843,37 @@ const _ParaCore = class _ParaCore {
1825
1843
  yield this.ctx.client.enable2FA(userId, verificationCode);
1826
1844
  });
1827
1845
  }
1846
+ /**
1847
+ * Login-time 2FA (ENG-6906): enroll a TOTP second factor for the current session.
1848
+ * Distinct from setup2fa/enable2fa, which configure the recovery-gating 2FA. This
1849
+ * is authenticated by the active session (no recovery config gate); the backend
1850
+ * 404s the surface when the login-MFA flag is off.
1851
+ *
1852
+ * @returns {Object} `{ uri, backupCodes }` — otpauth URI to render as a QR, plus
1853
+ * one-time backup codes to show the user exactly once.
1854
+ */
1855
+ enrollMfa() {
1856
+ return __async(this, null, function* () {
1857
+ yield __privateMethod(this, _ParaCore_instances, assertPartner_fn).call(this);
1858
+ return yield __privateGet(this, _authService).enrollMfa();
1859
+ });
1860
+ }
1861
+ /**
1862
+ * Login-time 2FA (ENG-6906): verify the second factor for the current login. On
1863
+ * success the backend stamps the session and this re-polls the login status so the
1864
+ * flow advances toward `done`; a wrong code resolves to `{ ok:false, attemptsRemaining }`
1865
+ * so the UI can re-prompt without treating it as a hard error.
1866
+ *
1867
+ * @param {Object} opts the options object.
1868
+ * @param {string} opts.code the TOTP or backup code entered by the user.
1869
+ * @returns {Object} `{ ok: true } | { ok: false; attemptsRemaining? }`
1870
+ */
1871
+ verifyMfa(_0) {
1872
+ return __async(this, arguments, function* ({ code }) {
1873
+ yield __privateMethod(this, _ParaCore_instances, assertPartner_fn).call(this);
1874
+ return yield __privateGet(this, _authService).verifyMfa({ code });
1875
+ });
1876
+ }
1828
1877
  /**
1829
1878
  * Resend a verification email for the current user.
1830
1879
  */
@@ -2386,6 +2435,14 @@ const _ParaCore = class _ParaCore {
2386
2435
  return result.url;
2387
2436
  });
2388
2437
  }
2438
+ getDeniedSignatureResultWithUrl(pendingTransactionId) {
2439
+ return __async(this, null, function* () {
2440
+ return {
2441
+ pendingTransactionId,
2442
+ transactionReviewUrl: yield this.getTransactionReviewUrl(pendingTransactionId)
2443
+ };
2444
+ });
2445
+ }
2389
2446
  getOnRampTransactionUrl(_0) {
2390
2447
  return __async(this, arguments, function* ({
2391
2448
  purchaseId
@@ -2448,11 +2505,9 @@ const _ParaCore = class _ParaCore {
2448
2505
  );
2449
2506
  let timeStart = Date.now();
2450
2507
  const effectiveTimeoutMs = Math.max(timeoutMs, constants.TRANSACTION_REVIEW_TIMEOUT_MS);
2451
- if (signRes.pendingTransactionId) {
2452
- const reviewUrl = yield this.getTransactionReviewUrl(
2453
- signRes.pendingTransactionId,
2454
- effectiveTimeoutMs
2455
- );
2508
+ let pendingTransactionId = getPendingTransactionId(signRes);
2509
+ if (pendingTransactionId) {
2510
+ const reviewUrl = yield this.getTransactionReviewUrl(pendingTransactionId, effectiveTimeoutMs);
2456
2511
  if (onTransactionReviewUrl) {
2457
2512
  onTransactionReviewUrl(reviewUrl);
2458
2513
  } else {
@@ -2461,8 +2516,9 @@ const _ParaCore = class _ParaCore {
2461
2516
  });
2462
2517
  }
2463
2518
  } else {
2464
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, signRes);
2465
- return signRes;
2519
+ const successfulSignRes2 = requireSuccessfulSignatureResult(signRes);
2520
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, successfulSignRes2);
2521
+ return successfulSignRes2;
2466
2522
  }
2467
2523
  while (true) {
2468
2524
  if (isCanceled() || Date.now() - timeStart > effectiveTimeoutMs) {
@@ -2476,15 +2532,19 @@ const _ParaCore = class _ParaCore {
2476
2532
  "tx.review.poll",
2477
2533
  (span) => __async(this, null, function* () {
2478
2534
  var _a;
2479
- const res = (_a = (yield this.ctx.client.getPendingTransaction(this.userId, signRes.pendingTransactionId)).data) == null ? void 0 : _a.pendingTransaction;
2535
+ const res = (_a = (yield this.ctx.client.getPendingTransaction(this.userId, pendingTransactionId)).data) == null ? void 0 : _a.pendingTransaction;
2480
2536
  span.setAttribute("polling.status", (res == null ? void 0 : res.approvedAt) ? "approved" : "pending");
2481
2537
  return res;
2482
2538
  }),
2483
- { "pending_transaction.id": signRes.pendingTransactionId }
2539
+ { "pending_transaction.id": pendingTransactionId }
2484
2540
  );
2485
2541
  } catch (e) {
2486
2542
  const error = new import_errors.TransactionReviewDenied();
2487
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, signRes, error.message);
2543
+ (0, import_utils2.dispatchEvent)(
2544
+ import_types.ParaEvent.SIGN_MESSAGE_EVENT,
2545
+ yield this.getDeniedSignatureResultWithUrl(pendingTransactionId),
2546
+ error.message
2547
+ );
2488
2548
  throw error;
2489
2549
  }
2490
2550
  if (!(pendingTransaction == null ? void 0 : pendingTransaction.approvedAt)) {
@@ -2499,16 +2559,16 @@ const _ParaCore = class _ParaCore {
2499
2559
  break;
2500
2560
  }
2501
2561
  }
2502
- if (signRes.pendingTransactionId) {
2503
- const error = new import_errors.TransactionReviewTimeout(
2504
- yield this.getTransactionReviewUrl(signRes.pendingTransactionId),
2505
- signRes.pendingTransactionId
2506
- );
2507
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, signRes, error.message);
2562
+ pendingTransactionId = getPendingTransactionId(signRes);
2563
+ if (pendingTransactionId) {
2564
+ const deniedSignRes = yield this.getDeniedSignatureResultWithUrl(pendingTransactionId);
2565
+ const error = new import_errors.TransactionReviewTimeout(deniedSignRes.transactionReviewUrl, pendingTransactionId);
2566
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, deniedSignRes, error.message);
2508
2567
  throw error;
2509
2568
  }
2510
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, signRes);
2511
- return signRes;
2569
+ const successfulSignRes = requireSuccessfulSignatureResult(signRes);
2570
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_MESSAGE_EVENT, successfulSignRes);
2571
+ return successfulSignRes;
2512
2572
  });
2513
2573
  }
2514
2574
  signMessageInner(_0) {
@@ -2587,19 +2647,18 @@ const _ParaCore = class _ParaCore {
2587
2647
  );
2588
2648
  let timeStart = Date.now();
2589
2649
  const effectiveTimeoutMs = Math.max(timeoutMs, constants.TRANSACTION_REVIEW_TIMEOUT_MS);
2590
- if (signRes.pendingTransactionId) {
2591
- const reviewUrl = yield this.getTransactionReviewUrl(
2592
- signRes.pendingTransactionId,
2593
- effectiveTimeoutMs
2594
- );
2650
+ let pendingTransactionId = getPendingTransactionId(signRes);
2651
+ if (pendingTransactionId) {
2652
+ const reviewUrl = yield this.getTransactionReviewUrl(pendingTransactionId, effectiveTimeoutMs);
2595
2653
  if (onTransactionReviewUrl) {
2596
2654
  onTransactionReviewUrl(reviewUrl);
2597
2655
  } else {
2598
2656
  yield this.platformUtils.openPopup(reviewUrl, { type: import_types.PopupType.SIGN_TRANSACTION_REVIEW });
2599
2657
  }
2600
2658
  } else {
2601
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, signRes);
2602
- return signRes;
2659
+ const successfulSignRes2 = requireSuccessfulSignatureResult(signRes);
2660
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, successfulSignRes2);
2661
+ return successfulSignRes2;
2603
2662
  }
2604
2663
  while (true) {
2605
2664
  if (isCanceled() || Date.now() - timeStart > effectiveTimeoutMs) {
@@ -2607,7 +2666,7 @@ const _ParaCore = class _ParaCore {
2607
2666
  break;
2608
2667
  }
2609
2668
  yield new Promise((resolve) => setTimeout(resolve, constants.POLLING_INTERVAL_MS));
2610
- const pendingTxId = signRes.pendingTransactionId;
2669
+ const pendingTxId = pendingTransactionId;
2611
2670
  let pendingTransaction;
2612
2671
  try {
2613
2672
  pendingTransaction = yield (0, import_tracer.wrapWithSpan)(
@@ -2622,7 +2681,11 @@ const _ParaCore = class _ParaCore {
2622
2681
  );
2623
2682
  } catch (e) {
2624
2683
  const error = new import_errors.TransactionReviewDenied();
2625
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, signRes, error.message);
2684
+ (0, import_utils2.dispatchEvent)(
2685
+ import_types.ParaEvent.SIGN_TRANSACTION_EVENT,
2686
+ yield this.getDeniedSignatureResultWithUrl(pendingTxId),
2687
+ error.message
2688
+ );
2626
2689
  throw error;
2627
2690
  }
2628
2691
  if (!(pendingTransaction == null ? void 0 : pendingTransaction.approvedAt)) {
@@ -2646,16 +2709,16 @@ const _ParaCore = class _ParaCore {
2646
2709
  break;
2647
2710
  }
2648
2711
  }
2649
- if (signRes.pendingTransactionId) {
2650
- const error = new import_errors.TransactionReviewTimeout(
2651
- yield this.getTransactionReviewUrl(signRes.pendingTransactionId),
2652
- signRes.pendingTransactionId
2653
- );
2654
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, signRes, error.message);
2712
+ pendingTransactionId = getPendingTransactionId(signRes);
2713
+ if (pendingTransactionId) {
2714
+ const deniedSignRes = yield this.getDeniedSignatureResultWithUrl(pendingTransactionId);
2715
+ const error = new import_errors.TransactionReviewTimeout(deniedSignRes.transactionReviewUrl, pendingTransactionId);
2716
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, deniedSignRes, error.message);
2655
2717
  throw error;
2656
2718
  }
2657
- (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, signRes);
2658
- return signRes;
2719
+ const successfulSignRes = requireSuccessfulSignatureResult(signRes);
2720
+ (0, import_utils2.dispatchEvent)(import_types.ParaEvent.SIGN_TRANSACTION_EVENT, successfulSignRes);
2721
+ return successfulSignRes;
2659
2722
  });
2660
2723
  }
2661
2724
  isProviderModalDisabled() {
@@ -46,7 +46,7 @@ __export(constants_exports, {
46
46
  TRANSACTION_REVIEW_TIMEOUT_MS: () => TRANSACTION_REVIEW_TIMEOUT_MS
47
47
  });
48
48
  module.exports = __toCommonJS(constants_exports);
49
- const PARA_CORE_VERSION = "3.4.0";
49
+ const PARA_CORE_VERSION = "3.5.1";
50
50
  const PREFIX = "@CAPSULE/";
51
51
  const PARA_PREFIX = "@PARA/";
52
52
  const LOCAL_STORAGE_AUTH_INFO = `${PREFIX}authInfo`;
@@ -18,6 +18,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var errors_exports = {};
19
19
  __export(errors_exports, {
20
20
  PartnerConfigError: () => PartnerConfigError,
21
+ SigningResultError: () => SigningResultError,
21
22
  TransactionReviewDenied: () => TransactionReviewDenied,
22
23
  TransactionReviewError: () => TransactionReviewError,
23
24
  TransactionReviewTimeout: () => TransactionReviewTimeout
@@ -44,6 +45,16 @@ class TransactionReviewTimeout extends Error {
44
45
  this.pendingTransactionId = pendingTransactionId;
45
46
  }
46
47
  }
48
+ class SigningResultError extends Error {
49
+ constructor(message, options = {}) {
50
+ super(message);
51
+ this.name = "SigningResultError";
52
+ this.code = options.code;
53
+ this.status = options.status;
54
+ this.responseURL = options.responseURL;
55
+ this.data = options.data;
56
+ }
57
+ }
47
58
  class PartnerConfigError extends Error {
48
59
  constructor(code, message, detail) {
49
60
  super(message);
@@ -55,6 +66,7 @@ class PartnerConfigError extends Error {
55
66
  // Annotate the CommonJS export names for ESM import in node:
56
67
  0 && (module.exports = {
57
68
  PartnerConfigError,
69
+ SigningResultError,
58
70
  TransactionReviewDenied,
59
71
  TransactionReviewError,
60
72
  TransactionReviewTimeout
@@ -238,6 +238,11 @@ class AuthService {
238
238
  JSON.stringify(isSLOPossible)
239
239
  );
240
240
  return serverAuthState;
241
+ // No preparation needed
242
+ case "mfa":
243
+ return serverAuthState;
244
+ default:
245
+ return serverAuthState;
241
246
  }
242
247
  });
243
248
  this.prepareLoginState = (_0, _1) => __async(this, [_0, _1], function* (loginState, { useShortUrls: shorten = false, portalTheme, sessionLookupId }) {
@@ -489,10 +494,46 @@ class AuthService {
489
494
  return yield (0, import_stateListener.waitForAuthStateChange)({
490
495
  stateManager: __privateGet(this, _stateManager),
491
496
  resolvePhases: [
492
- { phase: "awaiting_session_start", onPhase: (state) => state.authStateResult }
497
+ { phase: "awaiting_session_start", onPhase: (state) => state.authStateResult },
498
+ // Login-time 2FA (ENG-6906): the OAuth poll surfaced an mfa challenge instead
499
+ // of completing. Resolve here so the caller stops blocking; the challenge is
500
+ // read from the consumer auth state (AuthStateInfo.mfa) and satisfied via the
501
+ // enroll/verify hooks, which fire RESOLVE_2FA to continue the flow.
502
+ { phase: "awaiting_2fa", onPhase: (state) => state.authStateResult },
503
+ { phase: "awaiting_2fa_enrollment", onPhase: (state) => state.authStateResult }
493
504
  ]
494
505
  });
495
506
  });
507
+ /**
508
+ * Login-time 2FA (ENG-6906): after the second factor has been satisfied/enrolled
509
+ * server-side, re-poll the login status to advance the flow. Fires RESOLVE_2FA,
510
+ * which re-enters verify-oauth polling (now returning stage:'done') and routes
511
+ * onward to awaiting_session_start / authenticated.
512
+ */
513
+ this.resolveMfa = (pollingCallbacks) => __async(this, null, function* () {
514
+ __privateGet(this, _stateManager).send({ type: "RESOLVE_2FA", data: pollingCallbacks });
515
+ return yield (0, import_stateListener.waitForAuthStateChange)({
516
+ stateManager: __privateGet(this, _stateManager),
517
+ resolvePhases: [
518
+ { phase: "awaiting_session_start", onPhase: (state) => state.authStateResult },
519
+ { phase: "authenticated", onPhase: (state) => state.authStateResult }
520
+ ],
521
+ // If re-polling after the second factor lands in the auth error phase, reject fast
522
+ // with a clear message instead of stalling until the listener's default timeout. The
523
+ // caller stays parked at awaiting_2fa and can re-prompt/retry. (ENG-6906)
524
+ rejectPhases: [{ phase: "error", onPhase: () => new Error("Login 2FA resolution failed") }]
525
+ });
526
+ });
527
+ this.enrollMfa = () => __async(this, null, function* () {
528
+ return yield __privateGet(this, _paraCoreInterface).ctx.client.enrollMfa();
529
+ });
530
+ this.verifyMfa = (_0) => __async(this, [_0], function* ({ code }) {
531
+ const result = yield __privateGet(this, _paraCoreInterface).ctx.client.verifyMfa(code);
532
+ if (result.ok) {
533
+ yield this.resolveMfa();
534
+ }
535
+ return result;
536
+ });
496
537
  this.authenticateWithOAuth = (params) => __async(this, null, function* () {
497
538
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
498
539
  switch (params.method) {
@@ -115,7 +115,7 @@ const _CoreStateManager = class _CoreStateManager {
115
115
  * Extracts all data needed by UI consumers from authStateResult.
116
116
  */
117
117
  computeAuthStateInfo(authContext, walletContext) {
118
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L;
118
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q;
119
119
  const defaultAuthStateInfo = {
120
120
  userId: null,
121
121
  isPasskeySupported: false,
@@ -137,6 +137,7 @@ const _CoreStateManager = class _CoreStateManager {
137
137
  fallbackUsed: false,
138
138
  fallbackChannel: null,
139
139
  externalWalletVerification: null,
140
+ mfa: null,
140
141
  recoverySecret: null,
141
142
  isNewUser: false
142
143
  };
@@ -178,6 +179,7 @@ const _CoreStateManager = class _CoreStateManager {
178
179
  let deliveryChannel = null;
179
180
  let fallbackUsed = false;
180
181
  let fallbackChannel = null;
182
+ let mfa = null;
181
183
  if (stage === "login") {
182
184
  const loginState = authStateResult;
183
185
  const authMethods = (_j = loginState.loginAuthMethods) != null ? _j : [];
@@ -214,6 +216,17 @@ const _CoreStateManager = class _CoreStateManager {
214
216
  deliveryChannel = (_E = verifyState.deliveryChannel) != null ? _E : null;
215
217
  fallbackUsed = (_F = verifyState.fallbackUsed) != null ? _F : false;
216
218
  fallbackChannel = (_G = verifyState.fallbackChannel) != null ? _G : null;
219
+ } else if (stage === "mfa") {
220
+ const { mfa: challenge } = authStateResult;
221
+ mfa = {
222
+ mode: challenge.mode,
223
+ step: challenge.step,
224
+ methods: (_H = challenge.methods) != null ? _H : null,
225
+ attemptsRemaining: (_I = challenge.attemptsRemaining) != null ? _I : null,
226
+ lockedUntil: (_J = challenge.lockedUntil) != null ? _J : null,
227
+ secretUri: (_K = challenge.secretUri) != null ? _K : null,
228
+ backupCodes: (_L = challenge.backupCodes) != null ? _L : null
229
+ };
217
230
  }
218
231
  return {
219
232
  userId,
@@ -236,10 +249,11 @@ const _CoreStateManager = class _CoreStateManager {
236
249
  fallbackUsed,
237
250
  fallbackChannel,
238
251
  externalWalletVerification: externalWalletVerification ? {
239
- signatureVerificationMessage: (_H = externalWalletVerification.signatureVerificationMessage) != null ? _H : "",
240
- walletAddress: (_J = (_I = externalWalletVerification.externalWallet) == null ? void 0 : _I.address) != null ? _J : "",
241
- walletType: (_L = (_K = externalWalletVerification.externalWallet) == null ? void 0 : _K.type) != null ? _L : ""
252
+ signatureVerificationMessage: (_M = externalWalletVerification.signatureVerificationMessage) != null ? _M : "",
253
+ walletAddress: (_O = (_N = externalWalletVerification.externalWallet) == null ? void 0 : _N.address) != null ? _O : "",
254
+ walletType: (_Q = (_P = externalWalletVerification.externalWallet) == null ? void 0 : _P.type) != null ? _Q : ""
242
255
  } : null,
256
+ mfa,
243
257
  recoverySecret,
244
258
  isNewUser
245
259
  };
@@ -300,10 +314,19 @@ const _CoreStateManager = class _CoreStateManager {
300
314
  }
301
315
  authStateInfoEqual(a, b) {
302
316
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
303
- return a.userId === b.userId && a.isPasskeySupported === b.isPasskeySupported && a.hasPasskey === b.hasPasskey && a.hasPassword === b.hasPassword && a.hasPin === b.hasPin && a.passkeyUrl === b.passkeyUrl && a.passkeyFullUrl === b.passkeyFullUrl && a.passkeyKnownDeviceUrl === b.passkeyKnownDeviceUrl && a.passwordUrl === b.passwordUrl && a.passwordFullUrl === b.passwordFullUrl && a.pinUrl === b.pinUrl && a.pinFullUrl === b.pinFullUrl && a.passkeyId === b.passkeyId && a.verificationUrl === b.verificationUrl && a.verificationFullUrl === b.verificationFullUrl && a.deliveryChannel === b.deliveryChannel && a.fallbackUsed === b.fallbackUsed && a.fallbackChannel === b.fallbackChannel && a.recoverySecret === b.recoverySecret && a.isNewUser === b.isNewUser && ((_a = a.externalWalletVerification) == null ? void 0 : _a.signatureVerificationMessage) === ((_b = b.externalWalletVerification) == null ? void 0 : _b.signatureVerificationMessage) && ((_c = a.externalWalletVerification) == null ? void 0 : _c.walletAddress) === ((_d = b.externalWalletVerification) == null ? void 0 : _d.walletAddress) && ((_e = a.externalWalletVerification) == null ? void 0 : _e.walletType) === ((_f = b.externalWalletVerification) == null ? void 0 : _f.walletType) && ((_g = a.passkeyHints) == null ? void 0 : _g.length) === ((_h = b.passkeyHints) == null ? void 0 : _h.length) && ((_i = a.passkeyHints) != null ? _i : []).every(
317
+ return a.userId === b.userId && a.isPasskeySupported === b.isPasskeySupported && a.hasPasskey === b.hasPasskey && a.hasPassword === b.hasPassword && a.hasPin === b.hasPin && a.passkeyUrl === b.passkeyUrl && a.passkeyFullUrl === b.passkeyFullUrl && a.passkeyKnownDeviceUrl === b.passkeyKnownDeviceUrl && a.passwordUrl === b.passwordUrl && a.passwordFullUrl === b.passwordFullUrl && a.pinUrl === b.pinUrl && a.pinFullUrl === b.pinFullUrl && a.passkeyId === b.passkeyId && a.verificationUrl === b.verificationUrl && a.verificationFullUrl === b.verificationFullUrl && a.deliveryChannel === b.deliveryChannel && a.fallbackUsed === b.fallbackUsed && a.fallbackChannel === b.fallbackChannel && a.recoverySecret === b.recoverySecret && a.isNewUser === b.isNewUser && ((_a = a.externalWalletVerification) == null ? void 0 : _a.signatureVerificationMessage) === ((_b = b.externalWalletVerification) == null ? void 0 : _b.signatureVerificationMessage) && ((_c = a.externalWalletVerification) == null ? void 0 : _c.walletAddress) === ((_d = b.externalWalletVerification) == null ? void 0 : _d.walletAddress) && ((_e = a.externalWalletVerification) == null ? void 0 : _e.walletType) === ((_f = b.externalWalletVerification) == null ? void 0 : _f.walletType) && this.mfaEqual(a.mfa, b.mfa) && ((_g = a.passkeyHints) == null ? void 0 : _g.length) === ((_h = b.passkeyHints) == null ? void 0 : _h.length) && ((_i = a.passkeyHints) != null ? _i : []).every(
304
318
  (hint, i) => hint.useragent === b.passkeyHints[i].useragent && hint.aaguid === b.passkeyHints[i].aaguid
305
319
  );
306
320
  }
321
+ mfaEqual(a, b) {
322
+ if (a === b) return true;
323
+ if (!a || !b) return false;
324
+ const sameStringArray = (x, y) => {
325
+ var _a, _b;
326
+ return ((_a = x == null ? void 0 : x.length) != null ? _a : -1) === ((_b = y == null ? void 0 : y.length) != null ? _b : -1) && (x != null ? x : []).every((v, i) => v === y[i]);
327
+ };
328
+ return a.mode === b.mode && a.step === b.step && a.attemptsRemaining === b.attemptsRemaining && a.lockedUntil === b.lockedUntil && a.secretUri === b.secretUri && sameStringArray(a.methods, b.methods) && sameStringArray(a.backupCodes, b.backupCodes);
329
+ }
307
330
  };
308
331
  _CoreStateManager.CORE_LOADING_STATES = /* @__PURE__ */ new Set([
309
332
  "setting_up",
@@ -317,7 +340,11 @@ _CoreStateManager.AUTH_LOADING_STATES = /* @__PURE__ */ new Set([
317
340
  "authenticating_farcaster",
318
341
  "processing_authentication",
319
342
  "authenticating_telegram",
320
- "checking_state"
343
+ "checking_state",
344
+ // Re-polling the login status after the second factor is satisfied (ENG-6906).
345
+ // awaiting_2fa / awaiting_2fa_enrollment are deliberately NOT loading — they are
346
+ // user-input holds where the UI shows the MFA prompt.
347
+ "resolving_2fa"
321
348
  ]);
322
349
  _CoreStateManager.WALLET_LOADING_STATES = /* @__PURE__ */ new Set([
323
350
  "checking_wallet_state",
@@ -756,18 +756,18 @@ function createAuthStateMachine(paraCoreInterface) {
756
756
  guard: (0, import_xstate.and)([
757
757
  ({ event }) => {
758
758
  const output = event.output;
759
- return !!output.externalWallet;
759
+ return !!(output == null ? void 0 : output.externalWallet);
760
760
  },
761
761
  (0, import_xstate.or)([
762
762
  ({ event }) => {
763
763
  var _a;
764
764
  const output = event.output;
765
- return output.stage === "done" && !((_a = output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
765
+ return (output == null ? void 0 : output.stage) === "done" && !((_a = output == null ? void 0 : output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
766
766
  },
767
767
  ({ event }) => {
768
768
  var _a, _b;
769
769
  const output = event.output;
770
- return output.stage === "verify" && !((_a = output.externalWallet) == null ? void 0 : _a.withFullParaAuth) && !((_b = output.externalWallet) == null ? void 0 : _b.withVerification);
770
+ return (output == null ? void 0 : output.stage) === "verify" && !((_a = output == null ? void 0 : output.externalWallet) == null ? void 0 : _a.withFullParaAuth) && !((_b = output == null ? void 0 : output.externalWallet) == null ? void 0 : _b.withVerification);
771
771
  }
772
772
  ])
773
773
  ])
@@ -784,12 +784,12 @@ function createAuthStateMachine(paraCoreInterface) {
784
784
  guard: (0, import_xstate.and)([
785
785
  ({ event }) => {
786
786
  const output = event.output;
787
- return !!output.externalWallet;
787
+ return !!(output == null ? void 0 : output.externalWallet);
788
788
  },
789
789
  ({ event }) => {
790
790
  var _a, _b;
791
791
  const output = event.output;
792
- return output.stage === "verify" && !((_a = output.externalWallet) == null ? void 0 : _a.withFullParaAuth) && ((_b = output.externalWallet) == null ? void 0 : _b.withVerification);
792
+ return (output == null ? void 0 : output.stage) === "verify" && !((_a = output == null ? void 0 : output.externalWallet) == null ? void 0 : _a.withFullParaAuth) && ((_b = output == null ? void 0 : output.externalWallet) == null ? void 0 : _b.withVerification);
793
793
  }
794
794
  ])
795
795
  },
@@ -805,12 +805,12 @@ function createAuthStateMachine(paraCoreInterface) {
805
805
  guard: (0, import_xstate.and)([
806
806
  ({ event }) => {
807
807
  const output = event.output;
808
- return !!output.externalWallet;
808
+ return !!(output == null ? void 0 : output.externalWallet);
809
809
  },
810
810
  ({ event }) => {
811
811
  var _a;
812
812
  const output = event.output;
813
- return output.stage === "verify" && !!((_a = output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
813
+ return (output == null ? void 0 : output.stage) === "verify" && !!((_a = output == null ? void 0 : output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
814
814
  }
815
815
  ])
816
816
  },
@@ -824,11 +824,37 @@ function createAuthStateMachine(paraCoreInterface) {
824
824
  ({ event }) => {
825
825
  var _a;
826
826
  const output = event.output;
827
- return output.stage === "verify" && !((_a = output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
827
+ return (output == null ? void 0 : output.stage) === "verify" && !((_a = output == null ? void 0 : output.externalWallet) == null ? void 0 : _a.withFullParaAuth);
828
828
  },
829
829
  () => !paraCoreInterface.authService.isEnclaveUser
830
830
  ])
831
831
  },
832
+ // Login-time 2FA (ENG-6906): the server gated the login behind a second
833
+ // factor. Enrollment (no factor yet) and verification (existing factor)
834
+ // are surfaced as distinct phases so a custom UI can show the right step.
835
+ {
836
+ target: "awaiting_2fa_enrollment",
837
+ actions: [
838
+ "storeAuthStateResult",
839
+ { type: "logTransition", params: { to: "awaiting_2fa_enrollment", detail: "mfa enroll" } }
840
+ ],
841
+ guard: ({ event }) => {
842
+ var _a;
843
+ const output = event.output;
844
+ return (output == null ? void 0 : output.stage) === "mfa" && ((_a = output == null ? void 0 : output.mfa) == null ? void 0 : _a.mode) === "enroll";
845
+ }
846
+ },
847
+ {
848
+ target: "awaiting_2fa",
849
+ actions: [
850
+ "storeAuthStateResult",
851
+ { type: "logTransition", params: { to: "awaiting_2fa", detail: "mfa verify" } }
852
+ ],
853
+ guard: ({ event }) => {
854
+ const output = event.output;
855
+ return (output == null ? void 0 : output.stage) === "mfa";
856
+ }
857
+ },
832
858
  {
833
859
  target: "awaiting_session_start",
834
860
  actions: [
@@ -871,6 +897,77 @@ function createAuthStateMachine(paraCoreInterface) {
871
897
  CANCEL: { target: "unauthenticated", actions: ["resetState"] }
872
898
  }
873
899
  },
900
+ // Login-time 2FA hold states (ENG-6906). The SDK satisfies the second factor
901
+ // out-of-band (enrollMfa/verifyMfa against /internal/v1/auth/2fa/*); once the
902
+ // backend has stamped the session, a RESOLVE_2FA event re-polls the login
903
+ // status, which now returns stage:'done'.
904
+ awaiting_2fa: {
905
+ on: {
906
+ RESOLVE_2FA: { target: "resolving_2fa", actions: ["setPollingCallbacks", "resetError"] },
907
+ CANCEL: { target: "unauthenticated", actions: ["resetState"] }
908
+ }
909
+ },
910
+ awaiting_2fa_enrollment: {
911
+ on: {
912
+ RESOLVE_2FA: { target: "resolving_2fa", actions: ["setPollingCallbacks", "resetError"] },
913
+ CANCEL: { target: "unauthenticated", actions: ["resetState"] }
914
+ }
915
+ },
916
+ resolving_2fa: {
917
+ entry: [{ type: "logTransition", params: { to: "resolving_2fa" } }],
918
+ invoke: {
919
+ src: "polling",
920
+ input: ({ context }) => {
921
+ var _a;
922
+ let resolvedAuthState = null;
923
+ return {
924
+ checkCondition: () => __async(this, null, function* () {
925
+ try {
926
+ const result = yield paraCoreInterface.pollingService.waitForOAuth({
927
+ onSuccess: (serverAuthState) => {
928
+ resolvedAuthState = serverAuthState;
929
+ }
930
+ })();
931
+ return { finished: result.finished && !!resolvedAuthState, data: { authState: resolvedAuthState } };
932
+ } catch (error) {
933
+ throw new Error(`2FA resolution polling failed: ${(0, import_stateErrorHelpers.extractErrorMessage)(error, "Unknown error")}`);
934
+ }
935
+ }),
936
+ onPoll: () => {
937
+ var _a2, _b;
938
+ return (_b = (_a2 = context.pollingCallbacks) == null ? void 0 : _a2.onPoll) == null ? void 0 : _b.call(_a2);
939
+ },
940
+ onCancel: () => {
941
+ var _a2, _b;
942
+ return (_b = (_a2 = context.pollingCallbacks) == null ? void 0 : _a2.onCancel) == null ? void 0 : _b.call(_a2);
943
+ },
944
+ isCanceled: ((_a = context.pollingCallbacks) == null ? void 0 : _a.isCanceled) || (() => false),
945
+ id: "resolving2fa"
946
+ };
947
+ },
948
+ onError: {
949
+ target: "error",
950
+ actions: (0, import_authStateMachine_helpers.setAuthErrorAssign)("2FA resolution failed")
951
+ }
952
+ },
953
+ on: (0, import_authStateMachine_helpers.makePollingOnHandlers)("processing_authentication", "2FA resolution failed", [
954
+ (0, import_xstate.assign)({
955
+ serverAuthStateResult: ({ event }) => {
956
+ var _a;
957
+ const authState = ((_a = event.data) == null ? void 0 : _a.authState) || null;
958
+ return {
959
+ authState,
960
+ opts: import_authStateMachine_helpers.DEFAULT_AUTH_OPTS
961
+ };
962
+ },
963
+ isNewUser: ({ event }) => {
964
+ var _a;
965
+ const authState = (_a = event.data) == null ? void 0 : _a.authState;
966
+ return authState ? (0, import_authStateMachine_helpers.computeIsNewUser)(authState) : false;
967
+ }
968
+ })
969
+ ])
970
+ },
874
971
  awaiting_session_start: {
875
972
  on: {
876
973
  WAIT_FOR_SESSION: {
@@ -219,6 +219,14 @@ function createCoreStateMachine(paraCoreInterface) {
219
219
  "WAIT_FOR_WALLET_CREATION": {
220
220
  actions: ({ context, event }) => context.authMachineRef.send(event)
221
221
  },
222
+ // Login-time 2FA (ENG-6906): after the second factor is satisfied, verifyMfa
223
+ // fires RESOLVE_2FA so the auth machine re-polls verify-oauth and advances the
224
+ // login. Without forwarding it here the parent silently drops the event, the
225
+ // auth child stays parked at awaiting_2fa[_enrollment], and the user never gets
226
+ // a wallet.
227
+ "RESOLVE_2FA": {
228
+ actions: ({ context, event }) => context.authMachineRef.send(event)
229
+ },
222
230
  "INITIALIZE_GUEST_MODE": {
223
231
  actions: ({ context, event }) => context.authMachineRef.send(event)
224
232
  },
@@ -39,6 +39,8 @@ const PARA_CORE_METHODS = [
39
39
  "setup2fa",
40
40
  "enable2fa",
41
41
  "verify2fa",
42
+ "enrollMfa",
43
+ "verifyMfa",
42
44
  "logout",
43
45
  "clearStorage",
44
46
  "isSessionActive",