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
5 changes: 5 additions & 0 deletions packages/kyc-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ([#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

- Bump `@metamask/base-controller` from `^10.0.0` to `^10.1.0` ([#10864](https://github.com/MetaMask/core/pull/10864))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions packages/kyc-controller/src/KycController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ describe('KycController', () => {
residenceCountry: 'USA',
vendor: 'iron',
aal2Token: AAL2_TOKEN,
partnerIdentityToken: unsignedJwt({
ext: { email: 'a@b.co', email_verified: true },
}),
}),
);
expect(
Expand Down Expand Up @@ -508,6 +511,7 @@ describe('KycController', () => {
'KycController missing partner identity token for vendor',
);
expect(handlers.createVendorCustomer).not.toHaveBeenCalled();
expect(handlers.createUkycSession).not.toHaveBeenCalled();
},
);
});
Expand Down Expand Up @@ -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 },
}),
}),
);
});
});

Expand Down
11 changes: 8 additions & 3 deletions packages/kyc-controller/src/KycController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<KycSessionStatus> {
// 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
Expand All @@ -748,6 +752,7 @@ export class KycController extends BaseController<
residenceCountry: params.geoCountry,
vendor: params.vendor,
aal2Token: params.aal2Token,
partnerIdentityToken: params.partnerIdentityToken,
});

return this.#setAuthorizations({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
38 changes: 38 additions & 0 deletions packages/kyc-controller/src/KycService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>) =>
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<string, unknown>) => !('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(
Expand Down
11 changes: 11 additions & 0 deletions packages/kyc-controller/src/KycService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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(
Expand All @@ -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 },
Expand Down
Loading