@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.
- package/CHANGELOG.md +1 -7
- package/dist/KycController-method-action-types.cjs.map +1 -1
- package/dist/KycController-method-action-types.d.cts +6 -12
- package/dist/KycController-method-action-types.d.cts.map +1 -1
- package/dist/KycController-method-action-types.d.mts +6 -12
- package/dist/KycController-method-action-types.d.mts.map +1 -1
- package/dist/KycController-method-action-types.mjs.map +1 -1
- package/dist/KycController.cjs +72 -313
- package/dist/KycController.cjs.map +1 -1
- package/dist/KycController.d.cts +7 -27
- package/dist/KycController.d.cts.map +1 -1
- package/dist/KycController.d.mts +7 -27
- package/dist/KycController.d.mts.map +1 -1
- package/dist/KycController.mjs +73 -314
- package/dist/KycController.mjs.map +1 -1
- package/dist/KycService-method-action-types.cjs.map +1 -1
- package/dist/KycService-method-action-types.d.cts +31 -60
- package/dist/KycService-method-action-types.d.cts.map +1 -1
- package/dist/KycService-method-action-types.d.mts +31 -60
- package/dist/KycService-method-action-types.d.mts.map +1 -1
- package/dist/KycService-method-action-types.mjs.map +1 -1
- package/dist/KycService.cjs +75 -140
- package/dist/KycService.cjs.map +1 -1
- package/dist/KycService.d.cts +74 -181
- package/dist/KycService.d.cts.map +1 -1
- package/dist/KycService.d.mts +74 -181
- package/dist/KycService.d.mts.map +1 -1
- package/dist/KycService.mjs +75 -140
- package/dist/KycService.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +4 -49
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +4 -49
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ukyc/jwtChain.cjs +1 -1
- package/dist/ukyc/jwtChain.cjs.map +1 -1
- package/dist/ukyc/jwtChain.d.cts +2 -2
- package/dist/ukyc/jwtChain.d.mts +2 -2
- package/dist/ukyc/jwtChain.mjs +1 -1
- package/dist/ukyc/jwtChain.mjs.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.cjs +7 -16
- package/dist/ukyc/wrapEncryptionKey.cjs.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.d.cts +16 -23
- package/dist/ukyc/wrapEncryptionKey.d.cts.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.d.mts +16 -23
- package/dist/ukyc/wrapEncryptionKey.d.mts.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.mjs +7 -16
- package/dist/ukyc/wrapEncryptionKey.mjs.map +1 -1
- package/package.json +1 -1
package/dist/KycController.cjs
CHANGED
|
@@ -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,
|
|
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.
|
|
923
|
-
* 2. verifies
|
|
924
|
-
*
|
|
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
|
|
928
|
-
*
|
|
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
|
|
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
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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
|
-
|
|
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:
|
|
1188
|
-
*
|
|
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
|
|
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:
|
|
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;
|