feat(wallet): Add SeedlessOnboardingController and PasskeyController#9533
feat(wallet): Add SeedlessOnboardingController and PasskeyController#9533lwin-kyaw wants to merge 11 commits into
Conversation
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
| ...options, | ||
| state, | ||
| messenger, | ||
| encryptor: (options.encryptor ?? |
There was a problem hiding this comment.
Why do we need a typecast here? Is there a way to avoid this?
I think we also do this for KeyringController and I am not sure why. @mikesposito do you know? I think I've asked you this before.
There was a problem hiding this comment.
I'm assuming that the generic types the controller is instantiated with do not match the ones of the encryptor created by the factory. We likely just need to carry additional generic types for encryption key and key derivation options (like KeyringController does IIRC)
There was a problem hiding this comment.
Hmm, okay. Seems like a larger change. I guess we don't have to solve this right now.
| * Encryptor used to protect the seedless onboarding vault. Defaults to a PBKDF2 encryptor | ||
| * configured with 600,000 iterations. | ||
| */ | ||
| encryptor?: GenericEncryptor; |
There was a problem hiding this comment.
Why do we need to override this option just for initialization? (edit: oh, perhaps this is related to #9533 (comment))
There was a problem hiding this comment.
Hi, we wanna follow the KeyringControllerInstanceOptions.
We intentionally make encryptor field optional (default with GenericEncryptor) so that extension side controller init is align with KeyringController initialization.
For the mobile side, we have to provide the mobile compatible version.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 ded1aa1. Configure here.

Explanation
Adds
PasskeyControllerandSeedlessOnboardingControlleras default initialized controllers in thewalletpackage, following theInitializationConfigurationpattern established byKeyringController,AccountsController, and other wallet instances. Each controller gets its own per-directory init module (passkey-controller/,seedless-onboarding-controller/) with colocated types and tests.@metamask/walletis the shared controller-integration layer for extension, mobile, and other clients, so values that differ between clients are injected viainstanceOptionsrather than hardcoded.PasskeyController — WebAuthn relying-party configuration is platform-specific, so
passkeyController.expectedRPID,passkeyController.expectedOrigin, andpasskeyController.rpNamemust be supplied by each client (see the options table below). The wallet init forwardsinstanceOptionsto the controller as-is and does not inject any MetaMask-specific defaults;rpNameis required, anduserName/userDisplayNamefall back torpNameinsidePasskeyControllerwhen omitted. Persisted passkey state is forwarded from wallet state when present.SeedlessOnboardingController — JWT lifecycle callbacks (
refreshJWTToken,revokeRefreshToken,renewRefreshToken) are platform-specific and must be injected by each client (typically delegating toOAuthServiceon extension orAuthTokenHandleron mobile). A default PBKDF2 encryptor (600,000 iterations, shared withKeyringController) is applied when no customencryptoris passed. All otherSeedlessOnboardingControllerconstructor options (e.g.network,passwordOutdatedCacheTTL,keyDerivationInterface) are forwarded frominstanceOptionsas-is.Neither controller requires messenger delegation beyond the standard
getState/stateChangedwiring — both are self-contained at the messenger level.References
Per-environment options
passkeyController.expectedRPIDextension.runtime.getURL('')(stripped trailing slash)'extension-id')passkeyController.expectedOriginexpectedRPID'https://extension.origin')passkeyController.rpName'MetaMask')'MetaMask'); requiredpasskeyController.userName/userDisplayNamerpNameis desiredrpNameinside the controller when omittedseedlessOnboardingController.refreshJWTTokenOAuthService:getNewRefreshTokenvia init messengerAuthTokenHandlerjest.fn()in unit testsseedlessOnboardingController.revokeRefreshTokenOAuthService:revokeRefreshTokenAuthTokenHandlerjest.fn()in unit testsseedlessOnboardingController.renewRefreshTokenOAuthService:renewRefreshTokenAuthTokenHandlerjest.fn()in unit testsseedlessOnboardingController.encryptorencryptorFactory(600_000)(extension-local factory)Encryptoradapter (cipher↔datanormalization)encryptorFactory(600_000)seedlessOnboardingController.networkWeb3AuthNetwork.Devnet(dev/test) orMainnet(prod)web3AuthNetworkconstantPasskeyControllerhas no messenger-level dependencies on other controllers.SeedlessOnboardingControllercurrently receives JWT callbacks out-of-band (not via messenger); a future refactor may route these throughOAuthServiceactions directly.Client construction sites
These are the current
mainconstruction sites the compatibility audit was based on (the adoption PRs below will replace them):PasskeyController
SeedlessOnboardingController
Client adoption PRs
Checklist
Note
High Risk
Breaking wallet bootstrap adds auth/onboarding controllers touching passkeys, JWT lifecycle, and vault encryption; clients must supply options and avoid duplicate controller wiring.
Overview
Breaking:
@metamask/walletnow initializesPasskeyControllerandSeedlessOnboardingControllerby default, alongside existing wallet instances.Each controller gets an
InitializationConfigurationmodule (init, messenger wiring,instanceOptionstypes). Clients inject platform-specific settings via optionalpasskeyControllerandseedlessOnboardingControlleronWalletOptions.instanceOptions—e.g. WebAuthnexpectedRPID/expectedOriginfor passkeys, JWT refresh/revoke/renew callbacks for seedless onboarding. Seedless onboarding defaults to the same PBKDF2 encryptor as keyring (encryptorFactory(600_000)) when no custom encryptor is passed.The package adds dependencies on
@metamask/passkey-controllerand@metamask/seedless-onboarding-controller, updates build/tsconfig references, CODEOWNERS for the new init paths, and the README dependency graph. Unit tests cover registration indefaultConfigurations, option/state forwarding, and messengergetStateexposure.Reviewed by Cursor Bugbot for commit c60b979. Bugbot is set up for automated code reviews on this repo. Configure here.