From 3d8ec67907bbab35d81d04ee580945693fc75916 Mon Sep 17 00:00:00 2001 From: Jiexi Luan-Huang Date: Fri, 16 Oct 2026 11:01:49 -0700 Subject: [PATCH 1/2] add partnerIdentityToken param to session creation --- packages/kyc-controller/CHANGELOG.md | 5 +++ .../src/KycController-method-action-types.ts | 4 +- .../kyc-controller/src/KycController.test.ts | 12 ++++++ packages/kyc-controller/src/KycController.ts | 11 ++++-- .../src/KycService-method-action-types.ts | 2 + .../kyc-controller/src/KycService.test.ts | 38 +++++++++++++++++++ packages/kyc-controller/src/KycService.ts | 11 ++++++ 7 files changed, 78 insertions(+), 5 deletions(-) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 0693ed6d7b7..845038ca3fc 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add optional `partnerIdentityToken` to `CreateUkycSessionParams`; `KycService.createUkycSession` sends it on `POST /sessions` when provided ([#XXXXX](https://github.com/MetaMask/core/pull/XXXXX)) + - `KycController.startSession` now forwards the partner identity token minted for the selected vendor when creating a UKYC session + ### Changed - Bump `@metamask/base-controller` from `^10.0.0` to `^10.1.0` ([#10864](https://github.com/MetaMask/core/pull/10864)) diff --git a/packages/kyc-controller/src/KycController-method-action-types.ts b/packages/kyc-controller/src/KycController-method-action-types.ts index d447e4ec06d..f4e5237eed6 100644 --- a/packages/kyc-controller/src/KycController-method-action-types.ts +++ b/packages/kyc-controller/src/KycController-method-action-types.ts @@ -11,8 +11,8 @@ import type { KycController } from './KycController.js'; * * The verified email stored on controller state comes from the partner * identity token. When that request returns HTTP 422, a stored email is - * kept for an existing session. Creating a vendor customer requires the - * token and sends it as `partnerIdentityToken`. + * kept for an existing session. Creating a vendor customer and UKYC session + * requires the token, which both requests send as `partnerIdentityToken`. * * @param params - The session parameters. * @param params.vendor - Identity vendor for the session. diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 5da004987db..2a528338b9f 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -128,6 +128,9 @@ describe('KycController', () => { residenceCountry: 'USA', vendor: 'iron', aal2Token: AAL2_TOKEN, + partnerIdentityToken: unsignedJwt({ + ext: { email: 'a@b.co', email_verified: true }, + }), }), ); expect( @@ -508,6 +511,7 @@ describe('KycController', () => { 'KycController missing partner identity token for vendor', ); expect(handlers.createVendorCustomer).not.toHaveBeenCalled(); + expect(handlers.createUkycSession).not.toHaveBeenCalled(); }, ); }); @@ -540,6 +544,14 @@ describe('KycController', () => { ext: { email: 'moonpay@example.com', email_verified: true }, }), }); + expect(handlers.createUkycSession).toHaveBeenCalledWith( + expect.objectContaining({ + vendor: 'moonpay', + partnerIdentityToken: unsignedJwt({ + ext: { email: 'moonpay@example.com', email_verified: true }, + }), + }), + ); }); }); diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index c5f397a7d47..d08cc583939 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -415,8 +415,8 @@ export class KycController extends BaseController< * * The verified email stored on controller state comes from the partner * identity token. When that request returns HTTP 422, a stored email is - * kept for an existing session. Creating a vendor customer requires the - * token and sends it as `partnerIdentityToken`. + * kept for an existing session. Creating a vendor customer and UKYC session + * requires the token, which both requests send as `partnerIdentityToken`. * * @param params - The session parameters. * @param params.vendor - Identity vendor for the session. @@ -501,6 +501,7 @@ export class KycController extends BaseController< vendor: params.vendor, geoCountry, aal2Token: params.aal2Token, + partnerIdentityToken: partnerIdentity.partnerIdentityToken, }); } const sessionStatus = await this.messenger.call( @@ -716,17 +717,20 @@ export class KycController extends BaseController< * kyc-api returns the existing session if one with the same vendor and * canonicalUserId already exists. * - * @param params - Vendor, country, and AAL2 token used to create the session. + * @param params - Vendor, country, and tokens used to create the session. * @param params.vendor - Identity vendor for the UKYC session. * @param params.geoCountry - ISO 3166-1 alpha-3 country of residence. * @param params.aal2Token - AAL2 authentication token sent as the * Authorization bearer value for session creation. + * @param params.partnerIdentityToken - Partner identity token minted for + * the vendor's audience. * @returns The created or refreshed session status. */ async #createUkycSession(params: { vendor: KycVendor; geoCountry: string; aal2Token: string; + partnerIdentityToken: string; }): Promise { // Establish a per-session X25519 keypair used to seal both secrets. The // private half stays on the device; the public half is registered on the @@ -748,6 +752,7 @@ export class KycController extends BaseController< residenceCountry: params.geoCountry, vendor: params.vendor, aal2Token: params.aal2Token, + partnerIdentityToken: params.partnerIdentityToken, }); return this.#setAuthorizations({ diff --git a/packages/kyc-controller/src/KycService-method-action-types.ts b/packages/kyc-controller/src/KycService-method-action-types.ts index dcfd2a0f36f..c6d03b302d2 100644 --- a/packages/kyc-controller/src/KycService-method-action-types.ts +++ b/packages/kyc-controller/src/KycService-method-action-types.ts @@ -168,6 +168,8 @@ export type KycServiceFetchIdosRelayJwksAction = { * @param params - The session parameters. * @param params.aal2Token - AAL2 authentication token used as the * Authorization bearer value. + * @param params.partnerIdentityToken - Optional partner identity token for + * the session's vendor audience, sent as `partnerIdentityToken`. * @returns The UKYC session id and encryption schemas. */ export type KycServiceCreateUkycSessionAction = { diff --git a/packages/kyc-controller/src/KycService.test.ts b/packages/kyc-controller/src/KycService.test.ts index b3088d82b84..bf0efa87d2e 100644 --- a/packages/kyc-controller/src/KycService.test.ts +++ b/packages/kyc-controller/src/KycService.test.ts @@ -286,6 +286,44 @@ describe('KycService', () => { ).toStrictEqual(response); }); + it('sends the partner identity token when provided', async () => { + nock(MOCK_API_URL) + .post( + '/sessions', + (body: Record) => + body.partnerIdentityToken === 'partner-identity-token', + ) + .reply(200, response); + const { service } = getService(); + + expect( + await service.createUkycSession({ + sessionClientPublicKey: SESSION_CLIENT_PUBLIC_KEY, + residenceCountry: RESIDENCE_COUNTRY, + aal2Token: AAL2_TOKEN, + partnerIdentityToken: 'partner-identity-token', + }), + ).toStrictEqual(response); + }); + + it('omits the partner identity token when not provided', async () => { + nock(MOCK_API_URL) + .post( + '/sessions', + (body: Record) => !('partnerIdentityToken' in body), + ) + .reply(200, response); + const { service } = getService(); + + expect( + await service.createUkycSession({ + sessionClientPublicKey: SESSION_CLIENT_PUBLIC_KEY, + residenceCountry: RESIDENCE_COUNTRY, + aal2Token: AAL2_TOKEN, + }), + ).toStrictEqual(response); + }); + it('authenticates session creation with the aal2 token', async () => { nock(MOCK_API_URL) .post( diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts index 5d8d143e82d..d6ac4c34c16 100644 --- a/packages/kyc-controller/src/KycService.ts +++ b/packages/kyc-controller/src/KycService.ts @@ -341,6 +341,12 @@ export type CreateUkycSessionParams = { * token. */ aal2Token: string; + /** + * Partner identity token minted by + * `AuthenticationController:getPartnerIdentityToken` for the session's + * vendor audience. Omitted from the request body when not provided. + */ + partnerIdentityToken?: string; }; /** @@ -759,6 +765,8 @@ export class KycService extends BaseDataService< * @param params - The session parameters. * @param params.aal2Token - AAL2 authentication token used as the * Authorization bearer value. + * @param params.partnerIdentityToken - Optional partner identity token for + * the session's vendor audience, sent as `partnerIdentityToken`. * @returns The UKYC session id and encryption schemas. */ async createUkycSession( @@ -781,6 +789,9 @@ export class KycService extends BaseDataService< sessionClientPublicKey: params.sessionClientPublicKey, residenceCountry: params.residenceCountry, vendorMetadata: params.vendorMetadata ?? {}, + ...(params.partnerIdentityToken && { + partnerIdentityToken: params.partnerIdentityToken, + }), }), }, { bearerToken: params.aal2Token }, From 717f20da5755436acc3bf62ad301b8d5b02c957c Mon Sep 17 00:00:00 2001 From: Jiexi Luan-Huang Date: Fri, 16 Oct 2026 11:06:05 -0700 Subject: [PATCH 2/2] changelog --- packages/kyc-controller/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 845038ca3fc..57bd78c4ffa 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add optional `partnerIdentityToken` to `CreateUkycSessionParams`; `KycService.createUkycSession` sends it on `POST /sessions` when provided ([#XXXXX](https://github.com/MetaMask/core/pull/XXXXX)) +- Add optional `partnerIdentityToken` to `CreateUkycSessionParams`; `KycService.createUkycSession` sends it on `POST /sessions` when provided ([#10872](https://github.com/MetaMask/core/pull/10872)) - `KycController.startSession` now forwards the partner identity token minted for the selected vendor when creating a UKYC session ### Changed