Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions packages/kyc-controller/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`):

`getGeoCountry`, `fetchVendorDisclaimers`, `createSession`, `checkKycRequired`,
`createVendorCustomer`, `submitVendorDisclaimers`, `fetchSessionDisclaimersByCountry`, `fetchSessionDisclaimersBySessionId`, `submitSessionDisclaimers`,
`fetchKycStatus`, `fetchIdosEnclaveJwks`, `fetchIdosRelayJwks`, `createUkycSession`, `setAuthorizations`,
`fetchIdosEnclaveJwks`, `fetchIdosRelayJwks`, `createUkycSession`, `setAuthorizations`,
`createJourney`, `getSessionStatus`.

Endpoints:
Expand All @@ -139,12 +139,12 @@ Endpoints:
| `fetchSessionDisclaimersByCountry` | `GET` | `/disclaimers?country=` | Global idOS + KYC-provider catalog (no consent state) |
| `fetchSessionDisclaimersBySessionId` | `GET` | `/sessions/{id}/disclaimers` | Session-scoped catalog, with `consented` flags + credential-reuse flag |
| `submitSessionDisclaimers` | `POST` | `/sessions/{id}/disclaimers` | Record `{ idOS, kycProvider, credentialReusabilityConsentGiven }` consents |
| `fetchKycStatus` | `GET` | `/kyc/status` | User-keyed simplified KYC status |
| `fetchIdosEnclaveJwks` | `GET` | `{idosEnclaveBaseUrl}/.well-known/jwks.json` | idOS enclave JWKS for `encryptionDataKey` attestation |
| `fetchIdosRelayJwks` | `GET` | `{idosRelayBaseUrl}/.well-known/jwks.json` | idOS relay JWKS for `ukycCapabilityToken` attestation |
| `createUkycSession` | `POST` | `/sessions` | Start SumSub sub-flow; registers session client public key; returns encryption schemas |
| `setAuthorizations` | `POST` | `/sessions/{id}/authorizations` | Submit wrapped `data_encryption_key` and wrapped `ukyc_capability_token` |
| `createJourney` | `POST` | `/sessions/{id}/journey` | Create verification journey → applicant token |
| `getSessionStatus` | `GET` | `/sessions/{id}/status` | UKYC session status payload (`KycSessionStatusResponse`; stored on `sessionStatus`) |

### 2.3 `crypto.ts`

Expand Down Expand Up @@ -187,12 +187,13 @@ classDiagram
+KycProduct activeProduct
+Record kycRequiredByProduct [persisted]
+string lastCheckedAt [persisted]
+string sessionId [persisted]
+KycSessionStatusResponse sessionStatus
+SumSubState sumsub
}
class SumSubState {
+KycSumSubStatus status
+Json result
+string sessionId
+string applicantAccessToken
}
KycControllerState --> SumSubState : sumsub
Expand All @@ -206,10 +207,11 @@ State metadata highlights (`kycControllerMetadata`):

- **Persisted** (`persist: true`): `vendorDisclaimersAccepted`,
`providerDisclaimersAccepted`, `idosDisclaimersAccepted`,
`kycRequiredByProduct`, `lastCheckedAt`. These survive restarts so the flow
can skip already-accepted terms and reuse cached results. Session-scoped
`sessionDisclaimers` and `credentialReusabilityConsentGiven` are in-memory
only (`persist: false`) and are cleared on `reset()`.
`kycRequiredByProduct`, `lastCheckedAt`, `sessionId`. These survive restarts
so the flow can skip already-accepted terms, reuse cached results, and
resume session-status refresh. Session-scoped `sessionDisclaimers` and
`credentialReusabilityConsentGiven` are in-memory only (`persist: false`)
and are cleared on `reset()`.
Acceptance is vendor-scoped: `initialize` (and `createVendorCustomer`) drops
the stored acceptance when it belongs to a different vendor, so one vendor's
disclaimer ids are never submitted to another. The drop waits until the
Expand Down
16 changes: 16 additions & 0 deletions packages/kyc-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **BREAKING:** Move the active UKYC `sessionId` and `sessionStatus` from `sumsub` to the root of `KycControllerState`. ([#10276](https://github.com/MetaMask/core/pull/10276))
- Read `state.sessionId` / `state.sessionStatus` instead of `state.sumsub.sessionId` / `state.sumsub.sessionStatus`. `sessionId` is persisted; `sessionStatus` is not.
- **BREAKING:** `KycController.refreshKycStatus` now loads status from `GET /sessions/{id}/status` (`getSessionStatus`) instead of `GET /kyc/status`. ([#10276](https://github.com/MetaMask/core/pull/10276))
- Requires an active `sessionId` (throws if missing). Returns and publishes the UKYC `sessionStatus` payload as-is (`null` when none is recorded).
- **BREAKING:** Replace `KycUserStatus` with `KycSessionStatus` (`new` | `pending` | `approved` | `rejected` | `retry`). ([#10276](https://github.com/MetaMask/core/pull/10276))
- **BREAKING:** Rename the `GET /sessions/{id}/status` payload type from `KycSessionStatus` to `KycSessionStatusResponse`. ([#10276](https://github.com/MetaMask/core/pull/10276))
- **BREAKING:** Remove `userStatus`, `userStatusSumsubSessionId`, and `userStatusErrorCode` from `KycControllerState`. ([#10276](https://github.com/MetaMask/core/pull/10276))
- Read `state.sessionStatus`, or use `refreshKycStatus` / `KycController:statusChanged`.
- **BREAKING:** Combine the session-status and user-status poll loops onto one timer. ([#10276](https://github.com/MetaMask/core/pull/10276))
- Both post-SDK decision waits and `refreshKycStatus` pending polls use `sessionStatusPollIntervalMs` (default 15s) against `GET /sessions/{id}/status`.
- Bump `@metamask/profile-sync-controller` from `^32.1.0` to `^32.1.1` ([#10220](https://github.com/MetaMask/core/pull/10220))

### Removed

- **BREAKING:** Remove `KycService.fetchKycStatus` and the `KycService:fetchKycStatus` messenger action. ([#10276](https://github.com/MetaMask/core/pull/10276))
- **BREAKING:** Remove `KycControllerOptions.userStatusPollIntervalMs`. Use `sessionStatusPollIntervalMs` instead. ([#10276](https://github.com/MetaMask/core/pull/10276))
- **BREAKING:** Remove `KycUserStatusResponse`. Use `KycControllerStatusChangedEvent` / `refreshKycStatus`'s return payload instead. ([#10276](https://github.com/MetaMask/core/pull/10276))

## [0.3.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,18 @@ export type KycControllerStartSumSubAction = {
};

/**
* Refreshes the user-keyed simplified KYC status from `GET /kyc/status`,
* stores it on state, publishes {@link KycControllerStatusChangedEvent}, and
* schedules short-interval polling while the status is `pending`.
* Refreshes KYC status from the active UKYC session
* (`GET /sessions/{id}/status`), stores it on state, publishes
* {@link KycControllerStatusChangedEvent}, and schedules short-interval
* polling while the status is not terminal.
*
* Skipped when `userStatus` is already `completed`: a follow-up
* `GET /kyc/status` can still read a stale `pending` (for example after
* `session_not_in_valid_state`) and must not undo that decision.
* Throws without an active `sessionId`. Skipped when the recorded
* session status is already successful (`approved` / `completed`): a
* follow-up session status can still read a stale `pending` (for example
* after `session_not_in_valid_state`) and must not undo that decision.
*
* @returns The latest status payload.
* @returns The recorded session status, or `null` if none.
* @throws If there is no active UKYC session to query.
*/
export type KycControllerRefreshKycStatusAction = {
type: `KycController:refreshKycStatus`;
Expand Down Expand Up @@ -270,7 +273,7 @@ export type KycControllerResetAction = {
/**
* Restores the controller to its default state, discarding everything
* {@link reset} deliberately keeps: the session email, the persisted terms
* acceptance, the per-product KYC-required cache and the user-keyed status.
* acceptance, and the per-product KYC-required cache.
*
* Intended for a full wallet reset, where no trace of the previous
* customer may survive into the next wallet.
Expand Down
Loading
Loading