From 0781e515fdc2443be43fd0bc260cb85774b70a5e Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 14:47:38 +0800 Subject: [PATCH 01/10] feat: add PasskeyController to wallet initialization instances --- packages/wallet/package.json | 1 + .../src/initialization/instances/index.ts | 1 + .../passkey-controller/passkey-controller.ts | 26 +++++++++++++++++++ .../instances/passkey-controller/types.ts | 4 +++ packages/wallet/src/types.ts | 2 ++ packages/wallet/tsconfig.build.json | 1 + packages/wallet/tsconfig.json | 3 +++ yarn.lock | 3 ++- 8 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/types.ts diff --git a/packages/wallet/package.json b/packages/wallet/package.json index a31271b98a0..b4bdf30864d 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -65,6 +65,7 @@ "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^2.0.0", "@metamask/network-controller": "^34.0.0", + "@metamask/passkey-controller": "^2.1.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index a15db956032..6db88c600d2 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -4,6 +4,7 @@ export { approvalController } from './approval-controller/approval-controller'; export { connectivityController } from './connectivity-controller/connectivity-controller'; export { keyringController } from './keyring-controller/keyring-controller'; export { networkController } from './network-controller/network-controller'; +export { passkeyController } from './passkey-controller/passkey-controller'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; export { storageService } from './storage-service/storage-service'; export { transactionController } from './transaction-controller/transaction-controller'; diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts new file mode 100644 index 00000000000..a1b90b50156 --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -0,0 +1,26 @@ +import { Messenger } from '@metamask/messenger'; +import { InitializationConfiguration } from '../../types'; +import { PasskeyController, PasskeyControllerMessenger } from '@metamask/passkey-controller'; + +const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; +const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; +const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; + +export const passkeyController: InitializationConfiguration = { + name: 'PasskeyController', + init: ({ state, messenger, options }) => new PasskeyController({ + messenger, + state, + rpId: undefined, + rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, + expectedRPID: options.expectedRPID, + expectedOrigin: options.expectedOrigin, + userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, + userDisplayName: options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, + }), + getMessenger: (parent) => + new Messenger({ + namespace: 'PasskeyController', + parent, + }), +} diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts new file mode 100644 index 00000000000..f900ad2a79e --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -0,0 +1,4 @@ +export type PasskeyControllerInstanceOptions = { + expectedRPID: string; + expectedOrigin: string; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 1f330e9bafd..6f531159fd5 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -13,6 +13,7 @@ import type { RemoteFeatureFlagControllerInstanceOptions } from './initializatio import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; +import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; export type WalletOptions = { messenger?: RootMessenger; @@ -29,6 +30,7 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; + passkeyController: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index cc15d40bc18..214a1cf6720 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -15,6 +15,7 @@ { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../passkey-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" }, { "path": "../transaction-controller/tsconfig.build.json" } diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index ce310304ecc..e2a7b3ef81b 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -31,6 +31,9 @@ { "path": "../network-controller" }, + { + "path": "../passkey-controller" + }, { "path": "../remote-feature-flag-controller" }, diff --git a/yarn.lock b/yarn.lock index 91de6e9c442..29de7cca7a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7929,7 +7929,7 @@ __metadata: languageName: node linkType: hard -"@metamask/passkey-controller@workspace:packages/passkey-controller": +"@metamask/passkey-controller@npm:^2.1.0, @metamask/passkey-controller@workspace:packages/passkey-controller": version: 0.0.0-use.local resolution: "@metamask/passkey-controller@workspace:packages/passkey-controller" dependencies: @@ -9162,6 +9162,7 @@ __metadata: "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" + "@metamask/passkey-controller": "npm:^2.1.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/storage-service": "npm:^1.0.2" From d795cf64f89e6759a9fc88a9128b0fed5ab09b01 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 15:08:34 +0800 Subject: [PATCH 02/10] feat: add SeedlessOnboardingController to wallet init --- packages/wallet/package.json | 1 + .../src/initialization/instances/index.ts | 1 + .../instances/passkey-controller/types.ts | 37 ++++++++++++++++++- .../seedless-onboarding-controller.ts | 26 +++++++++++++ .../seedless-onboarding-controller/types.ts | 13 +++++++ packages/wallet/src/types.ts | 2 + packages/wallet/tsconfig.build.json | 1 + packages/wallet/tsconfig.json | 3 ++ yarn.lock | 3 +- 9 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b4bdf30864d..7754effd742 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -68,6 +68,7 @@ "@metamask/passkey-controller": "^2.1.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", + "@metamask/seedless-onboarding-controller": "^10.0.3", "@metamask/storage-service": "^1.0.2", "@metamask/transaction-controller": "^69.0.0", "@metamask/utils": "^11.11.0" diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index 6db88c600d2..3f2be223691 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -6,5 +6,6 @@ export { keyringController } from './keyring-controller/keyring-controller'; export { networkController } from './network-controller/network-controller'; export { passkeyController } from './passkey-controller/passkey-controller'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; +export { seedlessOnboardingController } from './seedless-onboarding-controller/seedless-onboarding-controller'; export { storageService } from './storage-service/storage-service'; export { transactionController } from './transaction-controller/transaction-controller'; diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index f900ad2a79e..f74c75fedb0 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,4 +1,37 @@ export type PasskeyControllerInstanceOptions = { - expectedRPID: string; - expectedOrigin: string; + expectedRPID: string | string[]; + + /** + * Allowed value(s) for the WebAuthn client origin. + */ + expectedOrigin: string | string[]; + + /** + * Relying party ID(s) for verification (SHA-256 hash match in authenticator data). Pass a string or array of strings; an empty array skips RP ID + * allowlist checks in {@link verifyRegistrationResponse} / {@link verifyAuthenticationResponse}. + * + * @default undefined + */ + rpId?: string; + + /** + * Relying party name shown in the platform passkey UI. + * + * @default 'MetaMask' + */ + rpName?: string; + + /** + * Optional passkey user name; defaults to `rpName`. + * + * @default 'MetaMask Wallet' + */ + userName?: string; + + /** + * Optional display name; defaults to `rpName`. + * + * @default 'MetaMask Wallet' + */ + userDisplayName?: string; }; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts new file mode 100644 index 00000000000..03ceec4e2e1 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -0,0 +1,26 @@ +import { SeedlessOnboardingController, SeedlessOnboardingControllerMessenger } from '@metamask/seedless-onboarding-controller'; +import { InitializationConfiguration } from '../../types'; +import { encryptorFactory } from '../keyring-controller/encryptor'; +import { Messenger } from '@metamask/messenger'; +import { EncryptionKey } from '@metamask/browser-passworder'; +import { Encryptor } from '@metamask/keyring-controller'; + +export const seedlessOnboardingController: InitializationConfiguration< + SeedlessOnboardingController, + SeedlessOnboardingControllerMessenger +> = { + name: 'SeedlessOnboardingController', + init: ({ state, messenger, options }) => (new SeedlessOnboardingController({ + ...options, + state, + messenger, + encryptor: (options.encryptor ?? encryptorFactory(600_000)) as Encryptor< + EncryptionKey + >, + })), + getMessenger: (parent) => + new Messenger({ + namespace: 'SeedlessOnboardingController', + parent, + }), +}; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts new file mode 100644 index 00000000000..2ed619c43a5 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts @@ -0,0 +1,13 @@ +import type { SeedlessOnboardingControllerOptions } from '@metamask/seedless-onboarding-controller'; +import { GenericEncryptor } from '../keyring-controller/encryptor'; + +export type SeedlessOnboardingControllerInstanceOptions = Omit< + SeedlessOnboardingControllerOptions, + 'messenger' | 'state' | 'encryptor' +> & { + /** + * Encryptor used to protect the seedless onboarding vault. Defaults to a PBKDF2 encryptor + * configured with 600,000 iterations. + */ + encryptor?: GenericEncryptor; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 6f531159fd5..1bddde41ee8 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -14,6 +14,7 @@ import type { StorageServiceInstanceOptions } from './initialization/instances/s import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; +import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; export type WalletOptions = { messenger?: RootMessenger; @@ -32,6 +33,7 @@ export type InstanceSpecificOptions = { networkController: NetworkControllerInstanceOptions; passkeyController: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; + seedlessOnboardingController: SeedlessOnboardingControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; }; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index 214a1cf6720..d4caa50ec1c 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -17,6 +17,7 @@ { "path": "../network-controller/tsconfig.build.json" }, { "path": "../passkey-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, + { "path": "../seedless-onboarding-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" }, { "path": "../transaction-controller/tsconfig.build.json" } ], diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index e2a7b3ef81b..62f5137fc5b 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -37,6 +37,9 @@ { "path": "../remote-feature-flag-controller" }, + { + "path": "../seedless-onboarding-controller" + }, { "path": "../storage-service" }, diff --git a/yarn.lock b/yarn.lock index 29de7cca7a9..c04c5fcc476 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8434,7 +8434,7 @@ __metadata: languageName: node linkType: hard -"@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": +"@metamask/seedless-onboarding-controller@npm:^10.0.3, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": version: 0.0.0-use.local resolution: "@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller" dependencies: @@ -9165,6 +9165,7 @@ __metadata: "@metamask/passkey-controller": "npm:^2.1.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" + "@metamask/seedless-onboarding-controller": "npm:^10.0.3" "@metamask/storage-service": "npm:^1.0.2" "@metamask/transaction-controller": "npm:^69.0.0" "@metamask/utils": "npm:^11.11.0" From 3cbc1585ab154c6aede7fa697ce58a620267f6c1 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 15:59:35 +0800 Subject: [PATCH 03/10] fix: fixed build and lint --- .../passkey-controller.test.ts | 154 +++++++++++++++ .../seedless-onboarding-controller.test.ts | 182 ++++++++++++++++++ packages/wallet/src/types.ts | 4 +- 3 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts new file mode 100644 index 00000000000..6f1614a3c54 --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -0,0 +1,154 @@ +import { Messenger } from '@metamask/messenger'; +import { + PasskeyController, + getDefaultPasskeyControllerState, +} from '@metamask/passkey-controller'; +import type { PasskeyRecord } from '@metamask/passkey-controller'; + +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { passkeyController } from './passkey-controller'; + +const { PasskeyController: ActualPasskeyController } = jest.requireActual( + '@metamask/passkey-controller', +); + +jest.mock('@metamask/passkey-controller', () => ({ + ...jest.requireActual('@metamask/passkey-controller'), + PasskeyController: jest.fn(), +})); + +const REQUIRED_OPTIONS = { + expectedRPID: 'extension-id', + expectedOrigin: 'https://extension.origin', +}; + +/** + * Creates a root messenger for use in tests. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: 'Root' }); +} + +describe('passkeyController', () => { + beforeEach(() => { + jest.clearAllMocks(); + (PasskeyController as jest.Mock).mockImplementation((...args: unknown[]) => + new ActualPasskeyController(...args), + ); + }); + + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain(passkeyController); + }); + + it('initializes a PasskeyController with default state', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + const instance = passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(instance).toBeInstanceOf(ActualPasskeyController); + expect(instance.state).toStrictEqual(getDefaultPasskeyControllerState()); + }); + + it('forwards the provided state to the controller', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + const passkeyRecord: PasskeyRecord = { + credential: { + id: 'credential-id', + publicKey: 'public-key', + counter: 0, + transports: ['internal'], + aaguid: '00000000-0000-0000-0000-000000000000', + }, + encryptedVaultKey: { + ciphertext: 'YQ==', + iv: 'YWFhYWFhYWFhYQ==', + }, + keyDerivation: { method: 'userHandle' }, + }; + + const instance = passkeyController.init({ + state: { passkeyRecord }, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); + }); + + it('applies default rpName, userName, and userDisplayName when omitted', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(PasskeyController).toHaveBeenCalledWith( + expect.objectContaining({ + rpId: undefined, + rpName: 'MetaMask', + userName: 'MetaMask Wallet', + userDisplayName: 'MetaMask Wallet', + expectedRPID: REQUIRED_OPTIONS.expectedRPID, + expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, + }), + ); + }); + + it('forwards custom passkey configuration options', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + passkeyController.init({ + state: undefined, + messenger, + options: { + expectedRPID: ['extension-id', 'other-id'], + expectedOrigin: ['https://a.example', 'https://b.example'], + rpId: 'rp-id', + rpName: 'Custom RP', + userName: 'custom-user', + userDisplayName: 'Custom Display Name', + }, + }); + + expect(PasskeyController).toHaveBeenCalledWith( + expect.objectContaining({ + rpId: undefined, + rpName: 'Custom RP', + userName: 'custom-user', + userDisplayName: 'Custom Display Name', + expectedRPID: ['extension-id', 'other-id'], + expectedOrigin: ['https://a.example', 'https://b.example'], + }), + ); + }); + + it('exposes its state through the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = passkeyController.getMessenger(rootMessenger); + + passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(rootMessenger.call('PasskeyController:getState')).toStrictEqual( + getDefaultPasskeyControllerState(), + ); + }); +}); diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts new file mode 100644 index 00000000000..9fdfcace186 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts @@ -0,0 +1,182 @@ +import { Messenger } from '@metamask/messenger'; +import { + SecretType, + SeedlessOnboardingController, + getDefaultSeedlessOnboardingControllerState, +} from '@metamask/seedless-onboarding-controller'; + +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { encryptorFactory } from '../keyring-controller/encryptor'; +import * as encryptorModule from '../keyring-controller/encryptor'; +import { seedlessOnboardingController } from './seedless-onboarding-controller'; +import type { SeedlessOnboardingControllerInstanceOptions } from './types'; + +const { SeedlessOnboardingController: ActualSeedlessOnboardingController } = + jest.requireActual('@metamask/seedless-onboarding-controller'); + +jest.mock('@metamask/seedless-onboarding-controller', () => ({ + ...jest.requireActual('@metamask/seedless-onboarding-controller'), + SeedlessOnboardingController: jest.fn(), +})); + +/** + * Creates a root messenger for use in tests. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: 'Root' }); +} + +/** + * Creates the required seedless onboarding options for tests. + * + * @returns Seedless onboarding controller options. + */ +function getSeedlessOnboardingOptions(): Pick< + SeedlessOnboardingControllerInstanceOptions, + 'refreshJWTToken' | 'revokeRefreshToken' | 'renewRefreshToken' +> { + return { + refreshJWTToken: jest.fn(), + revokeRefreshToken: jest.fn(), + renewRefreshToken: jest.fn(), + }; +} + +describe('seedlessOnboardingController', () => { + beforeEach(() => { + jest.clearAllMocks(); + (SeedlessOnboardingController as jest.Mock).mockImplementation( + (...args: unknown[]) => new ActualSeedlessOnboardingController(...args), + ); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain( + seedlessOnboardingController, + ); + }); + + it('initializes a SeedlessOnboardingController with default state', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + const instance = seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect(instance).toBeInstanceOf(ActualSeedlessOnboardingController); + expect(instance.state).toStrictEqual( + getDefaultSeedlessOnboardingControllerState(), + ); + }); + + it('forwards the provided state to the controller', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + const instance = seedlessOnboardingController.init({ + state: { + migrationVersion: 1, + isSeedlessOnboardingUserAuthenticated: false, + socialBackupsMetadata: [{ hash: 'abc', type: SecretType.Mnemonic }], + }, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect(instance.state.migrationVersion).toBe(1); + expect(instance.state.socialBackupsMetadata).toStrictEqual([ + { hash: 'abc', type: SecretType.Mnemonic }, + ]); + }); + + it('forwards seedless onboarding options to the controller', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + const options = getSeedlessOnboardingOptions(); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options, + }); + + expect(SeedlessOnboardingController).toHaveBeenCalledWith( + expect.objectContaining({ + refreshJWTToken: options.refreshJWTToken, + revokeRefreshToken: options.revokeRefreshToken, + renewRefreshToken: options.renewRefreshToken, + }), + ); + }); + + it('applies the default encryptor when omitted', () => { + const defaultEncryptor = encryptorFactory(600_000); + const encryptorFactorySpy = jest + .spyOn(encryptorModule, 'encryptorFactory') + .mockReturnValue(defaultEncryptor); + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + const { encryptor } = (SeedlessOnboardingController as jest.Mock).mock + .calls[0][0] as { + encryptor: ReturnType; + }; + + expect(encryptorFactorySpy).toHaveBeenCalledWith(600_000); + expect(encryptor).toBe(defaultEncryptor); + }); + + it('uses the provided encryptor when supplied', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + const encryptor = encryptorFactory(100_000); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: { + ...getSeedlessOnboardingOptions(), + encryptor, + }, + }); + + expect(SeedlessOnboardingController).toHaveBeenCalledWith( + expect.objectContaining({ encryptor }), + ); + }); + + it('exposes its state through the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = seedlessOnboardingController.getMessenger(rootMessenger); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect( + rootMessenger.call('SeedlessOnboardingController:getState'), + ).toStrictEqual(getDefaultSeedlessOnboardingControllerState()); + }); +}); diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 1bddde41ee8..ec7485cea4d 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -31,9 +31,9 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; - passkeyController: PasskeyControllerInstanceOptions; + passkeyController?: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; - seedlessOnboardingController: SeedlessOnboardingControllerInstanceOptions; + seedlessOnboardingController?: SeedlessOnboardingControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; }; From abc2e4c542921ad676dbed796dc600b1a34632ef Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 16:14:47 +0800 Subject: [PATCH 04/10] chore: updated CHANGELOG --- README.md | 2 ++ eslint-suppressions.json | 5 --- packages/wallet/CHANGELOG.md | 4 +++ .../passkey-controller.test.ts | 4 +-- .../passkey-controller/passkey-controller.ts | 35 ++++++++++++------- .../seedless-onboarding-controller.ts | 28 ++++++++------- .../seedless-onboarding-controller/types.ts | 1 + packages/wallet/src/types.ts | 4 +-- 8 files changed, 49 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index fc19b98258a..e3bdf8dac9c 100644 --- a/README.md +++ b/README.md @@ -648,7 +648,9 @@ linkStyle default opacity:0.5 wallet --> keyring_controller; wallet --> messenger; wallet --> network_controller; + wallet --> passkey_controller; wallet --> remote_feature_flag_controller; + wallet --> seedless_onboarding_controller; wallet --> storage_service; wallet --> transaction_controller; wallet_cli --> base_controller; diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 79caefd17d3..46131ea3fee 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2240,11 +2240,6 @@ "count": 2 } }, - "packages/transaction-pay-controller/src/strategy/bridge/bridge-submit.ts": { - "no-restricted-syntax": { - "count": 1 - } - }, "packages/transaction-pay-controller/src/strategy/relay/hyperliquid-withdraw.ts": { "no-restricted-syntax": { "count": 1 diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 706c4ac482c..df287bf355b 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING** Wire `SeedlessOnboardingController` and `PasskeyController` into the default wallet initialization ([#9533](https://github.com/MetaMask/core/pull/9533)) + ## [7.0.1] ### Changed diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 6f1614a3c54..19d010d22d9 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -39,8 +39,8 @@ function getRootMessenger(): RootMessenger { describe('passkeyController', () => { beforeEach(() => { jest.clearAllMocks(); - (PasskeyController as jest.Mock).mockImplementation((...args: unknown[]) => - new ActualPasskeyController(...args), + (PasskeyController as jest.Mock).mockImplementation( + (...args: unknown[]) => new ActualPasskeyController(...args), ); }); diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index a1b90b50156..edc81458c1b 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -1,26 +1,35 @@ import { Messenger } from '@metamask/messenger'; +import { + PasskeyController, + PasskeyControllerMessenger, +} from '@metamask/passkey-controller'; + import { InitializationConfiguration } from '../../types'; -import { PasskeyController, PasskeyControllerMessenger } from '@metamask/passkey-controller'; const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; -export const passkeyController: InitializationConfiguration = { +export const passkeyController: InitializationConfiguration< + PasskeyController, + PasskeyControllerMessenger +> = { name: 'PasskeyController', - init: ({ state, messenger, options }) => new PasskeyController({ - messenger, - state, - rpId: undefined, - rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, - expectedRPID: options.expectedRPID, - expectedOrigin: options.expectedOrigin, - userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, - userDisplayName: options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, - }), + init: ({ state, messenger, options }) => + new PasskeyController({ + messenger, + state, + rpId: options.rpId ?? undefined, + rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, + expectedRPID: options.expectedRPID, + expectedOrigin: options.expectedOrigin, + userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, + userDisplayName: + options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, + }), getMessenger: (parent) => new Messenger({ namespace: 'PasskeyController', parent, }), -} +}; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts index 03ceec4e2e1..03bd03d3778 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -1,23 +1,27 @@ -import { SeedlessOnboardingController, SeedlessOnboardingControllerMessenger } from '@metamask/seedless-onboarding-controller'; -import { InitializationConfiguration } from '../../types'; -import { encryptorFactory } from '../keyring-controller/encryptor'; -import { Messenger } from '@metamask/messenger'; import { EncryptionKey } from '@metamask/browser-passworder'; import { Encryptor } from '@metamask/keyring-controller'; +import { Messenger } from '@metamask/messenger'; +import { + SeedlessOnboardingController, + SeedlessOnboardingControllerMessenger, +} from '@metamask/seedless-onboarding-controller'; + +import { InitializationConfiguration } from '../../types'; +import { encryptorFactory } from '../keyring-controller/encryptor'; export const seedlessOnboardingController: InitializationConfiguration< SeedlessOnboardingController, SeedlessOnboardingControllerMessenger > = { name: 'SeedlessOnboardingController', - init: ({ state, messenger, options }) => (new SeedlessOnboardingController({ - ...options, - state, - messenger, - encryptor: (options.encryptor ?? encryptorFactory(600_000)) as Encryptor< - EncryptionKey - >, - })), + init: ({ state, messenger, options }) => + new SeedlessOnboardingController({ + ...options, + state, + messenger, + encryptor: (options.encryptor ?? + encryptorFactory(600_000)) as Encryptor, + }), getMessenger: (parent) => new Messenger({ namespace: 'SeedlessOnboardingController', diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts index 2ed619c43a5..8bbf862698c 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts @@ -1,4 +1,5 @@ import type { SeedlessOnboardingControllerOptions } from '@metamask/seedless-onboarding-controller'; + import { GenericEncryptor } from '../keyring-controller/encryptor'; export type SeedlessOnboardingControllerInstanceOptions = Omit< diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index ec7485cea4d..910ea28c171 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -9,12 +9,12 @@ import type { ApprovalControllerInstanceOptions } from './initialization/instanc import type { ConnectivityControllerInstanceOptions } from './initialization/instances/connectivity-controller/types'; import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types'; import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; +import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; +import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; -import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; -import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; export type WalletOptions = { messenger?: RootMessenger; From b867f452d0415402ddbc47c10e557172f35c2401 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 16:20:25 +0800 Subject: [PATCH 05/10] chore: updaed CODE_OWNERS --- .github/CODEOWNERS | 2 ++ .../instances/passkey-controller/passkey-controller.test.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 49f39f1ce08..2c0825f7132 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -150,6 +150,8 @@ /packages/wallet/src/initialization/instances/remote-feature-flag-controller/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/storage-service/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/transaction-controller/ @MetaMask/confirmations +/packages/wallet/src/initialization/instances/passkey-controller @MetaMask/web3auth +/packages/wallet/src/initialization/instances/seedless-onboarding-controller @MetaMask/web3auth ## Package Release related /packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 19d010d22d9..f8293342a0d 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -127,7 +127,7 @@ describe('passkeyController', () => { expect(PasskeyController).toHaveBeenCalledWith( expect.objectContaining({ - rpId: undefined, + rpId: 'rp-id', rpName: 'Custom RP', userName: 'custom-user', userDisplayName: 'Custom Display Name', From 901744a99caaa0de4cb74b95a6a2422fc915ec15 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 10:10:31 +0800 Subject: [PATCH 06/10] chore: reuse constructor options for PasskeyControllerInstanceOptions --- .../passkey-controller.test.ts | 1 + .../passkey-controller/passkey-controller.ts | 2 +- .../instances/passkey-controller/types.ts | 41 +++---------------- 3 files changed, 7 insertions(+), 37 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index f8293342a0d..727275e0230 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -25,6 +25,7 @@ jest.mock('@metamask/passkey-controller', () => ({ const REQUIRED_OPTIONS = { expectedRPID: 'extension-id', expectedOrigin: 'https://extension.origin', + rpName: 'MetaMask', }; /** diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index edc81458c1b..e5918668554 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -19,7 +19,7 @@ export const passkeyController: InitializationConfiguration< new PasskeyController({ messenger, state, - rpId: options.rpId ?? undefined, + rpId: options.rpId, rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, expectedRPID: options.expectedRPID, expectedOrigin: options.expectedOrigin, diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index f74c75fedb0..e6b09e74257 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,37 +1,6 @@ -export type PasskeyControllerInstanceOptions = { - expectedRPID: string | string[]; +import { PasskeyController } from "@metamask/passkey-controller"; - /** - * Allowed value(s) for the WebAuthn client origin. - */ - expectedOrigin: string | string[]; - - /** - * Relying party ID(s) for verification (SHA-256 hash match in authenticator data). Pass a string or array of strings; an empty array skips RP ID - * allowlist checks in {@link verifyRegistrationResponse} / {@link verifyAuthenticationResponse}. - * - * @default undefined - */ - rpId?: string; - - /** - * Relying party name shown in the platform passkey UI. - * - * @default 'MetaMask' - */ - rpName?: string; - - /** - * Optional passkey user name; defaults to `rpName`. - * - * @default 'MetaMask Wallet' - */ - userName?: string; - - /** - * Optional display name; defaults to `rpName`. - * - * @default 'MetaMask Wallet' - */ - userDisplayName?: string; -}; +export type PasskeyControllerInstanceOptions = Omit< + ConstructorParameters[0], + 'messenger' | 'state' +>; From c94fc018b0822ac71422f7af31b1ded8895ad99e Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 10:32:20 +0800 Subject: [PATCH 07/10] fix: fixed lint --- .../src/initialization/instances/passkey-controller/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index e6b09e74257..b25bcf1c27a 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,4 +1,4 @@ -import { PasskeyController } from "@metamask/passkey-controller"; +import { PasskeyController } from '@metamask/passkey-controller'; export type PasskeyControllerInstanceOptions = Omit< ConstructorParameters[0], From ded1aa14c6b643949678f79f5890f8433325380e Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 11:29:25 +0800 Subject: [PATCH 08/10] feat: updated passkey-controller-init --- .../passkey-controller/passkey-controller.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index e5918668554..bf1836fa5c0 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -6,10 +6,6 @@ import { import { InitializationConfiguration } from '../../types'; -const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; -const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; -const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; - export const passkeyController: InitializationConfiguration< PasskeyController, PasskeyControllerMessenger @@ -17,15 +13,9 @@ export const passkeyController: InitializationConfiguration< name: 'PasskeyController', init: ({ state, messenger, options }) => new PasskeyController({ + ...options, messenger, state, - rpId: options.rpId, - rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, - expectedRPID: options.expectedRPID, - expectedOrigin: options.expectedOrigin, - userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, - userDisplayName: - options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, }), getMessenger: (parent) => new Messenger({ From 70079ce87c9bc5ffd441ff4dbf8782d29e1fe134 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 12:15:27 +0800 Subject: [PATCH 09/10] fix: fixed tests --- .../passkey-controller/passkey-controller.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 727275e0230..ac81fc353f6 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -89,7 +89,7 @@ describe('passkeyController', () => { expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); }); - it('applies default rpName, userName, and userDisplayName when omitted', () => { + it('forwards options as-is without injecting userName or userDisplayName defaults', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); passkeyController.init({ @@ -100,14 +100,18 @@ describe('passkeyController', () => { expect(PasskeyController).toHaveBeenCalledWith( expect.objectContaining({ - rpId: undefined, rpName: 'MetaMask', - userName: 'MetaMask Wallet', - userDisplayName: 'MetaMask Wallet', expectedRPID: REQUIRED_OPTIONS.expectedRPID, expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, }), ); + + const [passedOptions] = (PasskeyController as jest.Mock).mock.calls[0] as [ + Record, + ]; + expect(passedOptions).not.toHaveProperty('userName'); + expect(passedOptions).not.toHaveProperty('userDisplayName'); + expect(passedOptions.rpId).toBeUndefined(); }); it('forwards custom passkey configuration options', () => { From c60b97981104cb8659cd4b7984db53309c88c094 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 12:15:49 +0800 Subject: [PATCH 10/10] fix: fixed tests --- .../passkey-controller.test.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index ac81fc353f6..7606b942618 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -89,31 +89,6 @@ describe('passkeyController', () => { expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); }); - it('forwards options as-is without injecting userName or userDisplayName defaults', () => { - const messenger = passkeyController.getMessenger(getRootMessenger()); - - passkeyController.init({ - state: undefined, - messenger, - options: REQUIRED_OPTIONS, - }); - - expect(PasskeyController).toHaveBeenCalledWith( - expect.objectContaining({ - rpName: 'MetaMask', - expectedRPID: REQUIRED_OPTIONS.expectedRPID, - expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, - }), - ); - - const [passedOptions] = (PasskeyController as jest.Mock).mock.calls[0] as [ - Record, - ]; - expect(passedOptions).not.toHaveProperty('userName'); - expect(passedOptions).not.toHaveProperty('userDisplayName'); - expect(passedOptions.rpId).toBeUndefined(); - }); - it('forwards custom passkey configuration options', () => { const messenger = passkeyController.getMessenger(getRootMessenger());