Skip to content

feat: KYC controller#9615

Open
Akaryatrh wants to merge 12 commits into
mainfrom
feat/kyc-controller
Open

feat: KYC controller#9615
Akaryatrh wants to merge 12 commits 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.
    • Product-scoped auto-continuation: an optional product (ramps | card) passed to initialize / acceptTermsAndStartSession is stored as activeProduct. Once authentication reaches the form phase, the controller automatically runs the KYC-required check and, when KYC is required, chains into the SumSub sub-flow — with no extra consumer calls. Without a product, the flow stops at form for manual control (checkKycRequired / startSumSub).
    • 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. It also accepts an optional baseUrl to override the env-derived URL for local/staging APIs.

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 (KycController.ts at 100% coverage, including the new auto-continuation paths).

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. initialize / acceptTermsAndStartSession accept an optional product to enable the automatic post-authentication continuation.
  • 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 placeholders (MOCK_JWT_TOKEN and a hardcoded vendorUserId), 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/KYC orchestration handles auth tokens, frame crypto, and profile-sync bearer tokens; SumSub paths still rely on mock UKYC credentials until production wiring lands.

Overview
Introduces @metamask/kyc-controller, a new monorepo package that centralizes KYC / identity verification for MetaMask clients behind a vendor-neutral ramps / card API.

KycController orchestrates the full flow (terms → session → MoonPay Check/Auth frames → KYC-required check → SumSub via an injected KycSumSubLauncher), including X25519/AES-GCM frame credential decryption, messenger-exposed actions, and persisted per-product KYC-required cache. Optional product on initialize / acceptTermsAndStartSession sets activeProduct so that after authentication the controller can automatically run checkKycRequired and chain into document verification when needed; without a product, behavior stops at form for manual control.

KycService is a stateless UKYC HTTP client (superstruct validation, service policy, auth bearer token and geolocation via messenger) with an optional baseUrl override for non-production APIs.

The root README, tsconfig project references, package scaffolding, docs, and Jest suites (100% coverage target on the controller) are included. Note: SumSub UKYC integration still uses placeholder JWT / vendor user id values called out for replacement before production.

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

Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Comment thread packages/kyc-controller/src/KycService.ts
Comment thread packages/kyc-controller/src/KycController.ts
Comment thread packages/kyc-controller/src/KycController.ts
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
…b flow opening

Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
@Akaryatrh Akaryatrh changed the title feat: init KYC controller and add logic from mobile repo feat: KYC controller Jul 23, 2026
Comment thread packages/kyc-controller/src/KycController.ts Outdated
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
@Akaryatrh
Akaryatrh force-pushed the feat/kyc-controller branch from ac34b99 to a61e704 Compare July 23, 2026 15:11
Comment thread packages/kyc-controller/src/KycController.ts
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
@socket-security

socket-security Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedjest@​29.7.01001006889100
Added@​types/​jest@​29.5.141001007780100
Addedchokidar-cli@​3.0.09910010083100

View full report

@socket-security

socket-security Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Warn Low
Potential code anomaly (AI signal): npm y18n is 68.0% likely to have a medium risk anomaly

Notes: The code is a standard local i18n helper that caches locale strings and writes updates to disk. It does not contain obvious malware or backdoors. However, there are security concerns related to unvalidated locale inputs, potential path traversal when locale is attacker-controlled, and race conditions in multi-process environments. If used in attack-prone contexts, these issues should be mitigated by validating locale values, constraining file paths within the intended directory, and optionally disabling auto-write-back (updateFiles=false) in high-risk deployments.

Confidence: 0.68

Severity: 0.58

From: ?npm/chokidar-cli@3.0.0npm/y18n@4.0.3

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/y18n@4.0.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm yargs is 61.0% likely to have a medium risk anomaly

Notes: The module implements a standard extends resolution with circular-extends protection and recursive merging. Primary security concerns are the potential execution of arbitrary code when extends points to a JS module and possible disclosure of local files via path-based extends. Treat untrusted inputs as a high-risk factor; consider enforcing whitelisting of allowed extends or sandboxing JS module extends. Overall risk is moderate due to code execution potential from untrusted configuration sources.

Confidence: 0.61

Severity: 0.58

From: ?npm/chokidar-cli@3.0.0npm/yargs@13.3.2

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@13.3.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm yargs is 68.0% likely to have a medium risk anomaly

Notes: The provided code consists of harmless-looking shell completion templates used to generate Bash/Zsh completions by querying the application for possible commands via --get-yargs-completions. There is no malicious behavior evident in the template code itself. The main risk is dependency on the integrity of the app_path binary that provides completions; if that binary is compromised, it could influence completions or run unintended commands. Overall, low likelihood of malware in these templates themselves.

Confidence: 0.68

Severity: 0.60

From: ?npm/chokidar-cli@3.0.0npm/yargs@13.3.2

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@13.3.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Ignoring alerts on:

  • chokidar-cli@3.0.0
  • create-jest@29.7.0
  • yargs@17.7.3
  • istanbul-lib-source-maps@4.0.1
  • babel-plugin-istanbul@6.1.1

View full report

Comment thread packages/kyc-controller/src/KycController.ts
Comment thread packages/kyc-controller/src/KycController.ts
… been reset

Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
* (disclaimers, kyc-required) expect alpha-3 codes (e.g. "USA"). This map
* bridges the two.
*/
export const ALPHA2_TO_ALPHA3: Record<string, string> = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thoughts on this living in a different package or in the client for now?

I think the KYC Controller interface should expect the country code to be in 3 character format

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

Copy link
Copy Markdown
Contributor Author

@SocketSecurity ignore npm/yargs@17.7.3
@SocketSecurity ignore npm/chokidar-cli@3.0.0
@SocketSecurity ignore npm/babel-plugin-istanbul@6.1.1
@SocketSecurity ignore npm/create-jest@29.7.0
@SocketSecurity ignore npm/istanbul-lib-source-maps@4.0.1

@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 c1c5b57. Configure here.

state.statusMessage =
'Session creation failed — accept the terms to try again.';
state.phase = 'terms';
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale product after session failure

Medium Severity

When vendor session creation fails, the controller clears persisted terms and returns to the terms phase but leaves activeProduct unchanged. A later acceptTermsAndStartSession call that omits product can still auto-run the KYC check and SumSub chain for the old product, unlike initialize, which explicitly clears activeProduct when no product is passed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c1c5b57. Configure here.

state.phase = 'form';
state.statusMessage = 'Already authenticated. Review to submit.';
});
await this.#continueAfterAuthentication();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate complete retriggers auto flow

Medium Severity

Check and Auth frame handlers always call #continueAfterAuthentication on every active completion with an access token, with no guard for phase or an in-flight auto-continuation. A duplicate or late complete message can run checkKycRequired and startSumSub again while a prior run is still active.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c1c5b57. Configure here.

'KycService:submitWrappedKey',
exchange,
);
return refreshed.applicantAccessToken;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Token refresh ignores flow reset

Medium Severity

The SumSub onTokenExpiration callback always calls KycService:submitWrappedKey with the captured exchange object and never checks #generation or whether the flow was reset. After reset(), a still-open SDK can refresh tokens against a stale UKYC session.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c1c5b57. Configure here.

Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
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.

2 participants