Skip to content

feat: init KYC controller and add logic from mobile repo#9615

Open
Akaryatrh wants to merge 1 commit into
mainfrom
feat/kyc-controller
Open

feat: init KYC controller and add logic from mobile repo#9615
Akaryatrh wants to merge 1 commit into
mainfrom
feat/kyc-controller

Conversation

@Akaryatrh

@Akaryatrh Akaryatrh commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@metamask/kyc-controller

Overview

@metamask/kyc-controller is a shared, platform-agnostic KYC / identity-verification package for MetaMask clients (mobile, extension, web). It centralizes all KYC orchestration logic in the core monorepo so every client consumes one implementation instead of maintaining its own.

What's included

Core modules

  • KycController (src/KycController.ts) — A BaseController that owns the entire identity flow:

    • The flow state machine: idle → terms → session → check → auth → form → submit → done / error.
    • The Check/Auth frame message protocol for communicating with MoonPay's hosted frames (blocks.moonpay.com) over dedicated channels (ch_1, ch_2, ch_reset).
    • X25519 credential decryption and key-pair generation.
    • SumSub orchestration through an injected KycSumSubLauncher adapter, keeping the controller SDK-free while each client injects its own launcher.
    • A per-product "is KYC required" cache and security-aware state metadata (session/access tokens are marked non-persisted and non-logged).
  • KycService (src/KycService.ts) — A data service that performs the Universal KYC (UKYC) HTTP calls via an injected fetch. It wraps requests with createServicePolicy for resilience, validates responses with superstruct, and sources the auth bearer token (from profile-sync-controller) and geolocation (from geolocation-controller) through the messenger.

Supporting modules

  • crypto.ts — X25519 key-pair generation and encrypted-credential decryption.
  • countryCodes.ts — ISO 3166-1 alpha-2 → alpha-3 country-code mapping.
  • selectors.ts — Memoized state selectors (selectKycPhase, selectKycSumSub, selectIsKycRequiredForProduct).
  • types.ts — Shared vendor-neutral types (KycPhase, KycProduct, KycVendor, KycSumSubLauncher, KycDisclaimer, etc.).
  • KycController-method-action-types.ts / KycService-method-action-types.ts — Messenger action type definitions for the controller and service.
  • index.ts — Explicit (non-barrel) exports defining the package's public API.

Tests

Comprehensive Jest test suites covering the controller, service, crypto, country codes, selectors, and the index/public API.

Package scaffolding & monorepo wiring

  • Standard package files: package.json, tsconfig.json, tsconfig.build.json, jest.config.js, typedoc.json, CHANGELOG.md, README.md, and the LICENSE, LICENSE.APACHE2, and LICENSE.MIT files.
  • Registered in the root tsconfig.json / tsconfig.build.json project references, added to the root README.md package list, and reflected in yarn.lock.

Public API

The package explicitly exports:

  • Controller: KycController, getDefaultKycControllerState, controllerName, plus its messenger, options, state, action, and event types.
  • Service: KycService, serviceName, plus its messenger, options, params, response, action, and event types.
  • Selectors: selectIsKycRequiredForProduct, selectKycPhase, selectKycSumSub.
  • Utilities: alpha2ToAlpha3, ALPHA2_TO_ALPHA3, decryptCredentials, generateKeyPair, and related crypto/domain types.

Note

The SumSub sub-flow currently uses a placeholder MOCK_JWT_TOKEN, which must be replaced with real UKYC-issued credentials before production use.

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

High Risk
New identity-verification surface handling auth tokens, encrypted credentials, and profile-sync auth integration; some UKYC/SumSub paths still use explicit mock credentials before production hardening.

Overview
Introduces @metamask/kyc-controller, a new monorepo package that centralizes identity/KYC orchestration for MetaMask clients (ported from mobile), wired into the root README, TypeScript project references, and dependency graph.

KycController drives the end-to-end flow: geolocation and disclaimers, persisted terms acceptance, UKYC session creation, MoonPay Check/Auth/Reset frame URLs and postMessage handling, X25519 + AES-GCM decryption of frame credentials, per-product KYC-required checks (ramps, card), and a SumSub sub-flow via an injected KycSumSubLauncher. Messenger action types and reselect selectors are exported for consumers.

KycService is the stateless HTTP layer against the Universal KYC API (prod/dev URLs, optional baseUrl override), using wallet bearer tokens and geolocation from other controllers. Responses are validated with superstruct.

The package ships with broad unit tests (100% coverage threshold), licenses, changelog, and build tooling (chokidar watch). Note: startSumSub / UKYC session creation still use placeholder JWT and vendorUserId values called out in source as pre-production.

Reviewed by Cursor Bugbot for commit 505b840. Bugbot is set up for automated code reviews on this repo. Configure here.

Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 505b840. Configure here.

vendorUserId: 'mockedId',
jwtToken: params.jwtToken,
vendorMetadata: params.vendorMetadata,
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded vendor user id

High Severity

createUkycSession always sends vendorUserId as the literal mockedId, while KycController already supplies the MoonPay customer id in vendorMetadata.moonPayUserId. UKYC session creation is tied to the wrong user even when identity frames succeeded.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 505b840. Configure here.

this.update((state) => {
state.geoCountry = country;
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Override country not cached

Medium Severity

When loadDisclaimers is called with an explicit country, disclaimers load for that code but geoCountry stays unset because state is only updated when no cached country existed. Later checkKycRequired without a override fails with missing country.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 505b840. Configure here.

}
if (!termsAcceptedAt || acceptedDisclaimerIds.length === 0) {
this.#fail('Missing terms acceptance for session creation.');
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terms timestamp before validation

Medium Severity

acceptTermsAndStartSession writes termsAcceptedAt and acceptedDisclaimerIds before #createSession validates them. If no disclaimers were loaded, it errors but leaves a persisted termsAcceptedAt with empty disclaimer ids.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 505b840. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant