@metamask-previews/kyc-controller 0.0.0-preview-eb263b0ee → 0.0.0-preview-5b293ee50

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 (56) hide show
  1. package/CHANGELOG.md +1 -7
  2. package/dist/KycController-method-action-types.cjs.map +1 -1
  3. package/dist/KycController-method-action-types.d.cts +6 -12
  4. package/dist/KycController-method-action-types.d.cts.map +1 -1
  5. package/dist/KycController-method-action-types.d.mts +6 -12
  6. package/dist/KycController-method-action-types.d.mts.map +1 -1
  7. package/dist/KycController-method-action-types.mjs.map +1 -1
  8. package/dist/KycController.cjs +72 -313
  9. package/dist/KycController.cjs.map +1 -1
  10. package/dist/KycController.d.cts +7 -27
  11. package/dist/KycController.d.cts.map +1 -1
  12. package/dist/KycController.d.mts +7 -27
  13. package/dist/KycController.d.mts.map +1 -1
  14. package/dist/KycController.mjs +73 -314
  15. package/dist/KycController.mjs.map +1 -1
  16. package/dist/KycService-method-action-types.cjs.map +1 -1
  17. package/dist/KycService-method-action-types.d.cts +31 -60
  18. package/dist/KycService-method-action-types.d.cts.map +1 -1
  19. package/dist/KycService-method-action-types.d.mts +31 -60
  20. package/dist/KycService-method-action-types.d.mts.map +1 -1
  21. package/dist/KycService-method-action-types.mjs.map +1 -1
  22. package/dist/KycService.cjs +75 -140
  23. package/dist/KycService.cjs.map +1 -1
  24. package/dist/KycService.d.cts +74 -181
  25. package/dist/KycService.d.cts.map +1 -1
  26. package/dist/KycService.d.mts +74 -181
  27. package/dist/KycService.d.mts.map +1 -1
  28. package/dist/KycService.mjs +75 -140
  29. package/dist/KycService.mjs.map +1 -1
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +3 -3
  32. package/dist/index.d.cts.map +1 -1
  33. package/dist/index.d.mts +3 -3
  34. package/dist/index.d.mts.map +1 -1
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/types.cjs.map +1 -1
  37. package/dist/types.d.cts +4 -49
  38. package/dist/types.d.cts.map +1 -1
  39. package/dist/types.d.mts +4 -49
  40. package/dist/types.d.mts.map +1 -1
  41. package/dist/types.mjs.map +1 -1
  42. package/dist/ukyc/jwtChain.cjs +1 -1
  43. package/dist/ukyc/jwtChain.cjs.map +1 -1
  44. package/dist/ukyc/jwtChain.d.cts +2 -2
  45. package/dist/ukyc/jwtChain.d.mts +2 -2
  46. package/dist/ukyc/jwtChain.mjs +1 -1
  47. package/dist/ukyc/jwtChain.mjs.map +1 -1
  48. package/dist/ukyc/wrapEncryptionKey.cjs +7 -16
  49. package/dist/ukyc/wrapEncryptionKey.cjs.map +1 -1
  50. package/dist/ukyc/wrapEncryptionKey.d.cts +16 -23
  51. package/dist/ukyc/wrapEncryptionKey.d.cts.map +1 -1
  52. package/dist/ukyc/wrapEncryptionKey.d.mts +16 -23
  53. package/dist/ukyc/wrapEncryptionKey.d.mts.map +1 -1
  54. package/dist/ukyc/wrapEncryptionKey.mjs +7 -16
  55. package/dist/ukyc/wrapEncryptionKey.mjs.map +1 -1
  56. package/package.json +1 -1
@@ -10,13 +10,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _KycController_instances, _KycController_sumsubLauncher, _KycController_keypair, _KycController_authClientToken, _KycController_generation, _KycController_sessionStatusPollIntervalMs, _KycController_pollTimer, _KycController_pollToken, _KycController_userStatusPollIntervalMs, _KycController_userStatusPollTimer, _KycController_userStatusPolling, _KycController_userStatusPollToken, _KycController_localUserSecretStore, _KycController_startConsentsSession, _KycController_recordSessionDisclaimers, _KycController_createSession, _KycController_clearAcceptedTerms, _KycController_clearMoonPaySession, _KycController_dropTermsUnlessForVendor, _KycController_hasTermsForVendor, _KycController_handleCheckOutcome, _KycController_handleAuthOutcome, _KycController_continueAfterAuthentication, _KycController_requireTermsReacceptance, _KycController_buildUkycSessionVendorFields, _KycController_createUkycSession, _KycController_fetchAndApplyUserStatus, _KycController_applyUserStatus, _KycController_ensureUserStatusPolling, _KycController_stopUserStatusPolling, _KycController_startSessionStatusPolling, _KycController_pollSessionStatusOnce, _KycController_stopPolling, _KycController_cancelPendingSession, _KycController_updateIfCurrent, _KycController_applyUpdate, _KycController_assertAttestedServerPublicKey, _KycController_fail;
13
+ var _KycController_instances, _KycController_sumsubLauncher, _KycController_keypair, _KycController_authClientToken, _KycController_generation, _KycController_sessionStatusPollIntervalMs, _KycController_pollTimer, _KycController_pollToken, _KycController_userStatusPollIntervalMs, _KycController_userStatusPollTimer, _KycController_userStatusPolling, _KycController_userStatusPollToken, _KycController_localUserSecretStore, _KycController_startConsentsSession, _KycController_createSession, _KycController_clearAcceptedTerms, _KycController_clearMoonPaySession, _KycController_dropTermsUnlessForVendor, _KycController_hasTermsForVendor, _KycController_handleCheckOutcome, _KycController_handleAuthOutcome, _KycController_continueAfterAuthentication, _KycController_requireTermsReacceptance, _KycController_buildUkycSessionVendorFields, _KycController_fetchAndApplyUserStatus, _KycController_applyUserStatus, _KycController_ensureUserStatusPolling, _KycController_stopUserStatusPolling, _KycController_startSessionStatusPolling, _KycController_pollSessionStatusOnce, _KycController_stopPolling, _KycController_cancelPendingSession, _KycController_updateIfCurrent, _KycController_applyUpdate, _KycController_fail;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.KycController = exports.getDefaultKycControllerState = exports.controllerName = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
17
- const utils_1 = require("@metamask/utils");
18
17
  const ed25519_1 = require("@noble/curves/ed25519");
19
18
  const crypto_js_1 = require("./crypto.cjs");
19
+ const encoding_js_1 = require("./encoding.cjs");
20
20
  const deriveClientMaterial_js_1 = require("./ukyc/deriveClientMaterial.cjs");
21
21
  const jwtChain_js_1 = require("./ukyc/jwtChain.cjs");
22
22
  const localUserSecret_js_1 = require("./ukyc/localUserSecret.cjs");
@@ -148,12 +148,6 @@ const kycControllerMetadata = {
148
148
  persist: true,
149
149
  usedInUi: false,
150
150
  },
151
- credentialReusabilityConsentGiven: {
152
- includeInDebugSnapshot: true,
153
- includeInStateLogs: true,
154
- persist: false,
155
- usedInUi: false,
156
- },
157
151
  disclaimers: {
158
152
  includeInDebugSnapshot: false,
159
153
  includeInStateLogs: false,
@@ -166,12 +160,6 @@ const kycControllerMetadata = {
166
160
  persist: false,
167
161
  usedInUi: true,
168
162
  },
169
- sessionDisclaimers: {
170
- includeInDebugSnapshot: false,
171
- includeInStateLogs: false,
172
- persist: false,
173
- usedInUi: true,
174
- },
175
163
  geoCountry: {
176
164
  includeInDebugSnapshot: true,
177
165
  includeInStateLogs: true,
@@ -261,10 +249,8 @@ function getDefaultKycControllerState() {
261
249
  termsAcceptedVendor: null,
262
250
  sumsubTncAccepted: null,
263
251
  idosTncAccepted: null,
264
- credentialReusabilityConsentGiven: null,
265
252
  disclaimers: [],
266
253
  disclaimersError: null,
267
- sessionDisclaimers: null,
268
254
  geoCountry: null,
269
255
  sessionToken: null,
270
256
  accessToken: null,
@@ -298,60 +284,6 @@ exports.getDefaultKycControllerState = getDefaultKycControllerState;
298
284
  function isSessionAlreadyCompletedError(error) {
299
285
  return String(error).includes(SESSION_NOT_IN_VALID_STATE);
300
286
  }
301
- /**
302
- * Whether recording session disclaimers failed because those document
303
- * versions were already consented for the session (`409 Conflict`).
304
- *
305
- * @param error - The caught error.
306
- * @returns `true` when the error is an HTTP 409.
307
- */
308
- function isConsentConflictError(error) {
309
- return (typeof error === 'object' &&
310
- error !== null &&
311
- typeof error.httpStatus === 'number' &&
312
- error.httpStatus === 409);
313
- }
314
- /**
315
- * Maps a session-disclaimer catalog into the `{ key, version }` records the
316
- * record-consents API expects, or an empty list when the user declined that
317
- * category.
318
- *
319
- * @param documents - Catalog documents for one consent category.
320
- * @param accepted - Whether the user accepted that category.
321
- * @returns Consent records, or `[]` when not accepted.
322
- */
323
- function consentRecordsFromCatalog(documents, accepted) {
324
- if (!accepted) {
325
- return [];
326
- }
327
- return documents
328
- .filter((document) => !document.consented)
329
- .map(({ key, version }) => ({ key, version }));
330
- }
331
- /**
332
- * Whether an accepted T&C2 category has no catalog documents. An empty list
333
- * would otherwise skip the POST and count as success.
334
- *
335
- * @param documents - Catalog documents for one consent category.
336
- * @param accepted - Whether the user accepted that category.
337
- * @returns `true` when the user accepted and the catalog is empty.
338
- */
339
- function isAcceptedCategoryEmpty(documents, accepted) {
340
- return accepted && documents.length === 0;
341
- }
342
- /**
343
- * Whether an accepted category is still missing consent after a 409 re-GET:
344
- * empty catalog or any document still unconsented.
345
- *
346
- * @param documents - Latest catalog documents for one consent category.
347
- * @param accepted - Whether the user accepted that category.
348
- * @returns `true` when accepted documents are not fully consented.
349
- */
350
- function acceptedCategoryStillMissing(documents, accepted) {
351
- return (accepted &&
352
- (documents.length === 0 ||
353
- documents.some((document) => !document.consented)));
354
- }
355
287
  /**
356
288
  * Vendors other than MoonPay skip Check/Auth frames and use the empty-shell
357
289
  * customer + consents path instead.
@@ -563,7 +495,6 @@ class KycController extends base_controller_1.BaseController {
563
495
  await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startConsentsSession).call(this, {
564
496
  sumsubTncSigned,
565
497
  idosTncSigned,
566
- credentialReusabilityConsentGiven: this.state.credentialReusabilityConsentGiven ?? false,
567
498
  });
568
499
  }
569
500
  else {
@@ -670,9 +601,6 @@ class KycController extends base_controller_1.BaseController {
670
601
  * Required for every vendor so callers explicitly declare acceptance.
671
602
  * @param params.idosTncSigned - Whether idOS T&C were accepted (T&C2).
672
603
  * Required for every vendor so callers explicitly declare acceptance.
673
- * @param params.credentialReusabilityConsentGiven - Whether the customer
674
- * consented to reuse existing idOS credentials. Used when recording
675
- * session-scoped disclaimers on the consents path. Defaults to `false`.
676
604
  */
677
605
  async acceptTermsAndStartSession(params) {
678
606
  const sumsubTncSigned = params?.sumsubTncSigned;
@@ -682,7 +610,6 @@ class KycController extends base_controller_1.BaseController {
682
610
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing T&C2 acceptance flags.');
683
611
  return;
684
612
  }
685
- const credentialReusabilityConsentGiven = params.credentialReusabilityConsentGiven ?? false;
686
613
  const termsAcceptedAt = new Date().toISOString();
687
614
  const disclaimerIds = this.state.disclaimers.map((disclaimer) => disclaimer.id);
688
615
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
@@ -697,15 +624,9 @@ class KycController extends base_controller_1.BaseController {
697
624
  state.termsAcceptedVendor = state.activeVendor;
698
625
  state.sumsubTncAccepted = sumsubTncSigned;
699
626
  state.idosTncAccepted = idosTncSigned;
700
- state.credentialReusabilityConsentGiven =
701
- credentialReusabilityConsentGiven;
702
627
  });
703
628
  if (usesConsentsFlow(this.state.activeVendor)) {
704
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startConsentsSession).call(this, {
705
- sumsubTncSigned,
706
- idosTncSigned,
707
- credentialReusabilityConsentGiven,
708
- });
629
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startConsentsSession).call(this, { sumsubTncSigned, idosTncSigned });
709
630
  return;
710
631
  }
711
632
  await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
@@ -919,20 +840,17 @@ class KycController extends base_controller_1.BaseController {
919
840
  /**
920
841
  * Runs the SumSub document-verification sub-flow end to end:
921
842
  *
922
- * 1. creates a UKYC session, receiving per-secret encryption schemas;
923
- * 2. verifies each schema's `jwtChain` against the Fractal JWKS and confirms
924
- * the attested session server public key;
843
+ * 1. requests a per-session wrapping key from the UKYC backend;
844
+ * 2. verifies its `jwtChain` against the Fractal JWKS and confirms the
845
+ * attested session server public key;
925
846
  * 3. derives the `data_encryption_key` from the wallet's UKYC
926
847
  * `local_user_secret` and wraps it for the session server;
927
- * 4. mints a client-signed, read-only `ukyc_capability_token`, wraps it the
928
- * same way as the encryption key, and submits both via authorizations;
848
+ * 4. mints a client-signed, read-only `ukyc_capability_token` and creates
849
+ * the UKYC session (handing over the wrapped key and the token);
929
850
  * 5. fetches the SumSub applicant access token; and
930
851
  * 6. presents the SDK via the injected launcher.
931
852
  *
932
- * If a UKYC session already exists (the consents path creates it before
933
- * recording session disclaimers), steps 1–4 are skipped.
934
- *
935
- * If authorizations report the applicant is already approved on the relay
853
+ * If session creation reports the applicant is already approved on the relay
936
854
  * while the vendor is still finalizing (`kycStatus: approved`,
937
855
  * `finalStatus: pending`), the sub-flow stops at step 4 with a
938
856
  * `vendorProcessing` status and a message rather than launching the SDK.
@@ -958,32 +876,65 @@ class KycController extends base_controller_1.BaseController {
958
876
  // presenting the SDK) on a controller that is now idle.
959
877
  const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
960
878
  try {
961
- if (!this.state.sumsub.sessionId) {
962
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
963
- state.sumsub.status = 'creatingSession';
964
- state.sumsub.result = null;
965
- state.sumsub.sessionStatus = null;
879
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
880
+ state.sumsub.status = 'creatingSession';
881
+ state.sumsub.result = null;
882
+ state.sumsub.sessionStatus = null;
883
+ });
884
+ const jwtToken = MOCK_JWT_TOKEN;
885
+ // Establish a per-session X25519 keypair and exchange our public half for
886
+ // the server's wrapping key. The private half stays on the device and is
887
+ // used to derive the shared secret that seals the data_encryption_key.
888
+ const sessionClientPrivateKey = ed25519_1.x25519.utils.randomSecretKey();
889
+ const sessionClientPublicKey = ed25519_1.x25519.getPublicKey(sessionClientPrivateKey);
890
+ const wrappingKey = await this.messenger.call('KycService:getWrappingKey', { sessionClientPublicKey: (0, encoding_js_1.toBase64Url)(sessionClientPublicKey) });
891
+ // Verify the jwtChain against Fractal's JWKS, then confirm the returned
892
+ // sessionServerPublicKey matches the value attested inside the verified
893
+ // JWT payload before trusting it for key wrapping.
894
+ const { keys } = await this.messenger.call('KycService:fetchJwks');
895
+ const jwtChainPayload = (0, jwtChain_js_1.verifyJwtChain)(keys, wrappingKey.jwtChain);
896
+ if (jwtChainPayload.sessionServerPublicKeyX !==
897
+ wrappingKey.sessionServerPublicKey.x) {
898
+ throw new Error('sessionServerPublicKey does not match the verified jwtChain payload (sessionServerPublicKeyX).');
899
+ }
900
+ // Derive the data_encryption_key from the local_user_secret and wrap it
901
+ // for the session server. Only the wrapped (encrypted) key ever leaves
902
+ // the device.
903
+ const localUserSecret = await (0, localUserSecret_js_1.getOrCreateLocalUserSecret)(__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_localUserSecretStore).call(this));
904
+ const clientMaterial = (0, deriveClientMaterial_js_1.deriveClientMaterial)(localUserSecret);
905
+ const wrappedEncryptionKey = {
906
+ sessionId: wrappingKey.id,
907
+ ...(0, wrapEncryptionKey_js_1.wrapEncryptionKey)(sessionClientPrivateKey, wrappingKey.sessionServerPublicKey.x, clientMaterial.dataEncryptionKey),
908
+ };
909
+ // Mint a read-only `ukyc_capability_token` for the session. Only the
910
+ // client holds the signing key derived from `local_user_secret`, so only
911
+ // the client can mint it; scoping it to `read` means it authorizes later
912
+ // storage reads without granting write or delete access.
913
+ const ukycCapabilityToken = (0, storageAccessToken_js_1.signStorageAccessToken)({
914
+ material: clientMaterial,
915
+ operations: ['read'],
916
+ expiresAt: new Date(Date.now() + UKYC_CAPABILITY_TOKEN_TTL_MS),
917
+ });
918
+ const { sessionId, kycStatus, finalStatus } = await this.messenger.call('KycService:createUkycSession', {
919
+ jwtToken,
920
+ ...__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_buildUkycSessionVendorFields).call(this),
921
+ wrappedEncryptionKey,
922
+ ukycCapabilityToken,
923
+ });
924
+ // A user who already finished the journey can return to a session the
925
+ // relay has already approved (`kycStatus`) while the vendor is still
926
+ // finalizing its own decision (`finalStatus`). There is nothing left to
927
+ // verify, so stop here and surface a message rather than launching the
928
+ // SDK again.
929
+ if (kycStatus === KYC_STATUSES.approved &&
930
+ finalStatus === KYC_STATUSES.pending) {
931
+ const stillCurrent = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
932
+ state.sumsub.status = 'vendorProcessing';
933
+ state.sumsub.sessionId = sessionId;
934
+ state.statusMessage = VENDOR_PROCESSING_MESSAGE;
966
935
  });
967
- const created = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createUkycSession).call(this, generation);
968
- if (!created) {
969
- return {};
970
- }
971
- // A user who already finished the journey can return to a session the
972
- // relay has already approved (`kycStatus`) while the vendor is still
973
- // finalizing its own decision (`finalStatus`). There is nothing left to
974
- // verify, so stop here and surface a message rather than launching the
975
- // SDK again.
976
- if (created.vendorProcessing) {
977
- return {
978
- kycStatus: created.kycStatus,
979
- finalStatus: created.finalStatus,
980
- };
981
- }
936
+ return stillCurrent ? { kycStatus, finalStatus } : {};
982
937
  }
983
- // Empty string is a valid "no id to poll" session id used by tests and
984
- // must not be coalesced away as missing.
985
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
986
- const sessionId = this.state.sumsub.sessionId || '';
987
938
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
988
939
  state.sumsub.status = 'fetchingToken';
989
940
  state.sumsub.sessionId = sessionId;
@@ -1145,8 +1096,6 @@ class KycController extends base_controller_1.BaseController {
1145
1096
  state.error = null;
1146
1097
  state.disclaimers = [];
1147
1098
  state.disclaimersError = null;
1148
- state.sessionDisclaimers = null;
1149
- state.credentialReusabilityConsentGiven = null;
1150
1099
  state.sessionToken = null;
1151
1100
  state.accessToken = null;
1152
1101
  state.moonpayCustomerId = null;
@@ -1184,15 +1133,12 @@ _KycController_sumsubLauncher = new WeakMap(), _KycController_keypair = new Weak
1184
1133
  };
1185
1134
  }, _KycController_startConsentsSession =
1186
1135
  /**
1187
- * Consents-path vendors: record vendor T&Cs, create a UKYC session,
1188
- * record session-scoped idOS / KYC-provider disclaimers, then launch
1189
- * SumSub — skipping MoonPay Check/Auth frames.
1136
+ * Consents-path vendors: post vendor signings + Sumsub/idOS ack, then
1137
+ * launch SumSub — skipping MoonPay Check/Auth frames.
1190
1138
  *
1191
- * @param consents - T&C2 flags mapped onto the session disclaimer catalog.
1139
+ * @param consents - T&C2 boolean flags.
1192
1140
  * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1193
1141
  * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1194
- * @param consents.credentialReusabilityConsentGiven - Whether credential
1195
- * reuse was accepted.
1196
1142
  */
1197
1143
  async function _KycController_startConsentsSession(consents) {
1198
1144
  const { email, acceptedDisclaimerIds } = this.state;
@@ -1209,44 +1155,18 @@ async function _KycController_startConsentsSession(consents) {
1209
1155
  state.error = null;
1210
1156
  state.phase = 'session';
1211
1157
  state.statusMessage = 'Submitting consents...';
1212
- state.sumsub.status = 'creatingSession';
1213
- state.sumsub.result = null;
1214
- state.sumsub.sessionStatus = null;
1215
1158
  // Consents-path vendors have no MoonPay session/access tokens.
1216
1159
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearMoonPaySession).call(this, state);
1217
1160
  });
1218
1161
  try {
1219
- await this.messenger.call('KycService:submitVendorDisclaimers', {
1220
- vendor: this.state.activeVendor,
1162
+ await this.messenger.call('KycService:submitConsents', {
1221
1163
  disclaimerIds: acceptedDisclaimerIds,
1164
+ sumsubTncSigned: consents.sumsubTncSigned,
1165
+ idosTncSigned: consents.idosTncSigned,
1222
1166
  });
1223
1167
  if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1224
1168
  return;
1225
1169
  }
1226
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1227
- state.statusMessage = 'Creating session...';
1228
- });
1229
- const created = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createUkycSession).call(this, generation);
1230
- if (!created || __classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1231
- return;
1232
- }
1233
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_recordSessionDisclaimers).call(this, created.sessionId, consents, generation);
1234
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1235
- return;
1236
- }
1237
- if (created.vendorProcessing) {
1238
- try {
1239
- await this.refreshKycStatus();
1240
- }
1241
- catch (statusError) {
1242
- console.error('KYC status refresh failed:', statusError);
1243
- }
1244
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1245
- state.phase = 'done';
1246
- state.statusMessage = VENDOR_PROCESSING_MESSAGE;
1247
- });
1248
- return;
1249
- }
1250
1170
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1251
1171
  state.phase = 'submit';
1252
1172
  state.statusMessage = 'Starting document verification...';
@@ -1285,24 +1205,6 @@ async function _KycController_startConsentsSession(consents) {
1285
1205
  });
1286
1206
  }
1287
1207
  catch (error) {
1288
- if (isSessionAlreadyCompletedError(error)) {
1289
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1290
- return;
1291
- }
1292
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, {
1293
- status: 'completed',
1294
- sumsubSessionId: null,
1295
- errorCode: null,
1296
- });
1297
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1298
- state.sumsub.status = 'complete';
1299
- state.sumsub.result = { alreadyCompleted: true };
1300
- state.statusMessage = 'KYC already completed.';
1301
- state.phase = 'done';
1302
- state.error = null;
1303
- });
1304
- return;
1305
- }
1306
1208
  console.error('Consents session failed:', error);
1307
1209
  if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1308
1210
  return;
@@ -1310,10 +1212,6 @@ async function _KycController_startConsentsSession(consents) {
1310
1212
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1311
1213
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1312
1214
  state.activeProduct = null;
1313
- state.sessionDisclaimers = null;
1314
- // Session create ran before recording disclaimers. Drop the leftover
1315
- // UKYC session so a later `startSumSub` cannot skip consent recording.
1316
- state.sumsub = { ...getDefaultKycControllerState().sumsub };
1317
1215
  state.error = `Consents session failed: ${String(error)}`;
1318
1216
  state.statusMessage =
1319
1217
  'Consent / verification failed — accept the terms to try again.';
@@ -1321,74 +1219,6 @@ async function _KycController_startConsentsSession(consents) {
1321
1219
  });
1322
1220
  await this.loadDisclaimers();
1323
1221
  }
1324
- }, _KycController_recordSessionDisclaimers =
1325
- /**
1326
- * Fetches the session-scoped disclaimer catalog and records consents
1327
- * derived from the T&C2 flags. Already-consented catalog rows are omitted
1328
- * from the POST. A 409 is re-checked with a GET: continue only when every
1329
- * accepted document is now consented, otherwise fail closed.
1330
- *
1331
- * @param sessionId - The UKYC session id.
1332
- * @param consents - T&C2 flags mapped onto catalog documents.
1333
- * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1334
- * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1335
- * @param consents.credentialReusabilityConsentGiven - Whether credential
1336
- * reuse was accepted.
1337
- * @param generation - Flow generation captured by the caller.
1338
- */
1339
- async function _KycController_recordSessionDisclaimers(sessionId, consents, generation) {
1340
- const catalog = await this.messenger.call('KycService:fetchSessionDisclaimers', { sessionId });
1341
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1342
- return;
1343
- }
1344
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1345
- state.sessionDisclaimers = catalog;
1346
- state.statusMessage = 'Submitting consents...';
1347
- });
1348
- if (isAcceptedCategoryEmpty(catalog.idOS, consents.idosTncSigned) ||
1349
- isAcceptedCategoryEmpty(catalog.kycProvider, consents.sumsubTncSigned)) {
1350
- throw new Error('Session disclaimer catalog is missing documents for an accepted category.');
1351
- }
1352
- const idOS = consentRecordsFromCatalog(catalog.idOS, consents.idosTncSigned);
1353
- const kycProvider = consentRecordsFromCatalog(catalog.kycProvider, consents.sumsubTncSigned);
1354
- const reuseUnchanged = catalog.credentialReusabilityConsentGiven ===
1355
- consents.credentialReusabilityConsentGiven;
1356
- if (idOS.length === 0 && kycProvider.length === 0 && reuseUnchanged) {
1357
- return;
1358
- }
1359
- try {
1360
- const recorded = await this.messenger.call('KycService:submitSessionDisclaimers', {
1361
- sessionId,
1362
- idOS,
1363
- kycProvider,
1364
- credentialReusabilityConsentGiven: consents.credentialReusabilityConsentGiven,
1365
- });
1366
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1367
- state.sessionDisclaimers = recorded;
1368
- });
1369
- }
1370
- catch (error) {
1371
- if (!isConsentConflictError(error)) {
1372
- throw error;
1373
- }
1374
- // 409 means some document version was already recorded. Re-fetch and
1375
- // continue only when every document the user accepted is now consented;
1376
- // otherwise fail closed so a version bump cannot skip new docs.
1377
- const latest = await this.messenger.call('KycService:fetchSessionDisclaimers', { sessionId });
1378
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1379
- return;
1380
- }
1381
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1382
- state.sessionDisclaimers = latest;
1383
- });
1384
- const stillMissingIdos = acceptedCategoryStillMissing(latest.idOS, consents.idosTncSigned);
1385
- const stillMissingProvider = acceptedCategoryStillMissing(latest.kycProvider, consents.sumsubTncSigned);
1386
- const stillMissingReuse = consents.credentialReusabilityConsentGiven &&
1387
- !latest.credentialReusabilityConsentGiven;
1388
- if (stillMissingIdos || stillMissingProvider || stillMissingReuse) {
1389
- throw error;
1390
- }
1391
- }
1392
1222
  }, _KycController_createSession =
1393
1223
  /**
1394
1224
  * Creates a vendor session from the currently stored terms + email.
@@ -1460,7 +1290,6 @@ async function _KycController_createSession() {
1460
1290
  state.termsAcceptedVendor = null;
1461
1291
  state.sumsubTncAccepted = null;
1462
1292
  state.idosTncAccepted = null;
1463
- state.credentialReusabilityConsentGiven = null;
1464
1293
  }, _KycController_clearMoonPaySession = function _KycController_clearMoonPaySession(state) {
1465
1294
  state.moonpayCustomerId = null;
1466
1295
  state.sessionToken = null;
@@ -1587,71 +1416,6 @@ async function _KycController_continueAfterAuthentication() {
1587
1416
  };
1588
1417
  }
1589
1418
  return { vendor: this.state.activeVendor };
1590
- }, _KycController_createUkycSession =
1591
- /**
1592
- * Creates a UKYC session, wraps the `data_encryption_key` and
1593
- * `ukyc_capability_token` against the returned encryption schemas, and
1594
- * submits both via authorizations. Stores `sumsub.sessionId`. Returns `null`
1595
- * when a `reset()` superseded the flow.
1596
- *
1597
- * @param generation - Flow generation captured by the caller.
1598
- * @returns The created session, or `null` if superseded.
1599
- */
1600
- async function _KycController_createUkycSession(generation) {
1601
- const jwtToken = MOCK_JWT_TOKEN;
1602
- // Establish a per-session X25519 keypair used to seal both secrets. The
1603
- // private half stays on the device; each encryption schema from session
1604
- // creation supplies the matching server public key.
1605
- const sessionClientPrivateKey = ed25519_1.x25519.utils.randomSecretKey();
1606
- const { sessionId, encryptionDataKey, ukycCapabilityToken: capabilityTokenSchema, } = await this.messenger.call('KycService:createUkycSession', {
1607
- jwtToken,
1608
- ...__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_buildUkycSessionVendorFields).call(this),
1609
- });
1610
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1611
- return null;
1612
- }
1613
- // Verify each jwtChain against Fractal's JWKS, then confirm the returned
1614
- // server public key matches the value attested inside the verified JWT
1615
- // payload before trusting it for wrapping.
1616
- const { keys } = await this.messenger.call('KycService:fetchJwks');
1617
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_assertAttestedServerPublicKey).call(this, keys, encryptionDataKey);
1618
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_assertAttestedServerPublicKey).call(this, keys, capabilityTokenSchema);
1619
- // Derive the data_encryption_key from the local_user_secret, mint a
1620
- // read-only capability token, and wrap both for the session server. Only
1621
- // the wrapped (encrypted) material ever leaves the device.
1622
- const localUserSecret = await (0, localUserSecret_js_1.getOrCreateLocalUserSecret)(__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_localUserSecretStore).call(this));
1623
- const clientMaterial = (0, deriveClientMaterial_js_1.deriveClientMaterial)(localUserSecret);
1624
- const wrappedEncryptionDataKey = (0, wrapEncryptionKey_js_1.wrapEncryptionKey)(sessionClientPrivateKey, encryptionDataKey.serverPublicKey.x, clientMaterial.dataEncryptionKey);
1625
- // Only the client holds the signing key derived from `local_user_secret`,
1626
- // so only the client can mint the token; scoping it to `read` means it
1627
- // authorizes later storage reads without granting write or delete access.
1628
- const ukycCapabilityToken = (0, storageAccessToken_js_1.signStorageAccessToken)({
1629
- material: clientMaterial,
1630
- operations: ['read'],
1631
- expiresAt: new Date(Date.now() + UKYC_CAPABILITY_TOKEN_TTL_MS),
1632
- });
1633
- const wrappedUkycCapabilityToken = (0, wrapEncryptionKey_js_1.wrapEncryptionKey)(sessionClientPrivateKey, capabilityTokenSchema.serverPublicKey.x, (0, utils_1.stringToBytes)((0, storageAccessToken_js_1.encodeStorageAccessTokenForHeader)(ukycCapabilityToken)));
1634
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1635
- return null;
1636
- }
1637
- const { kycStatus, finalStatus } = await this.messenger.call('KycService:setAuthorizations', {
1638
- sessionId,
1639
- wrappedEncryptionDataKey,
1640
- wrappedUkycCapabilityToken,
1641
- });
1642
- const vendorProcessing = kycStatus === KYC_STATUSES.approved &&
1643
- finalStatus === KYC_STATUSES.pending;
1644
- const stillCurrent = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1645
- state.sumsub.sessionId = sessionId;
1646
- if (vendorProcessing) {
1647
- state.sumsub.status = 'vendorProcessing';
1648
- state.statusMessage = VENDOR_PROCESSING_MESSAGE;
1649
- }
1650
- });
1651
- if (!stillCurrent) {
1652
- return null;
1653
- }
1654
- return { sessionId, kycStatus, finalStatus, vendorProcessing };
1655
1419
  }, _KycController_fetchAndApplyUserStatus =
1656
1420
  /**
1657
1421
  * Fetches `GET /kyc/status` and applies it to state without managing the
@@ -1829,11 +1593,6 @@ async function _KycController_pollSessionStatusOnce(sessionId, token) {
1829
1593
  // @ts-expect-error Avoid "type instantiation is excessively deep".
1830
1594
  updater(state);
1831
1595
  });
1832
- }, _KycController_assertAttestedServerPublicKey = function _KycController_assertAttestedServerPublicKey(keys, schema) {
1833
- const jwtChainPayload = (0, jwtChain_js_1.verifyJwtChain)(keys, schema.jwtChain);
1834
- if (jwtChainPayload.sessionServerPublicKeyX !== schema.serverPublicKey.x) {
1835
- throw new Error('sessionServerPublicKey does not match the verified jwtChain payload (sessionServerPublicKeyX).');
1836
- }
1837
1596
  }, _KycController_fail = function _KycController_fail(message) {
1838
1597
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1839
1598
  state.error = message;