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
1 change: 1 addition & 0 deletions packages/profile-sync-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `beginStepUp`, `completeStepUp`, `getElevatedProfileToken` and `clearStepUpSession` to `AuthenticationController`, holding the elevated token in memory only behind a hard-expiring `stepUpSessionExpiresAt` state and a `stepUpSessionTtlMs` config ([#10267](https://github.com/MetaMask/core/pull/10267))
- Add `refreshEnrolledCredentials`, `beginCredentialEnrollment` and `completeCredentialEnrollment` to `AuthenticationController`, backed by a memory-only `enrolledCredentials` state and an optional `trace` callback ([#10266](https://github.com/MetaMask/core/pull/10266))
- Add passkey and email OTP enrollment, verification, credential-list, and elevated-token exchange SDK methods ([#10265](https://github.com/MetaMask/core/pull/10265))
- Add validated MFA domain types and structured `MfaError` classes with a serialization-safe `mfaCode` ([#10264](https://github.com/MetaMask/core/pull/10264))
Expand Down
17 changes: 17 additions & 0 deletions packages/profile-sync-controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ import { ... } from '@metamask/profile-sync-controller/auth/mocks'
import { ... } from '@metamask/profile-sync-controller/user-storage/mocks'
```

## Multi-factor authentication

`AuthenticationController` exposes UI-independent primitives for passkey and
email OTP enrollment and step-up verification:

- `refreshEnrolledCredentials()` refreshes the in-memory credential list.
- `beginCredentialEnrollment()` and `completeCredentialEnrollment()` surround
a client-owned passkey ceremony or email-code screen.
- `beginStepUp()` and `completeStepUp()` verify an enrolled credential and
return an elevated profile token.
- `getElevatedProfileToken()` reuses a live elevated session when it satisfies
the caller's freshness requirement; `clearStepUpSession()` clears it.

Clients must retain the challenge `flowId`, perform the platform ceremony, and
send the resulting proof to the matching completion method. OTP codes,
passkey results, and elevated tokens are never persisted in controller state.

## Contributing

This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,52 @@ export type AuthenticationControllerCompleteCredentialEnrollmentAction = {
handler: AuthenticationController['completeCredentialEnrollment'];
};

/**
* Begins step-up verification with an enrolled credential.
*
* @param request - Credential type and trace reason.
* @returns A challenge for the client-owned ceremony.
*/
export type AuthenticationControllerBeginStepUpAction = {
type: `AuthenticationController:beginStepUp`;
handler: AuthenticationController['beginStepUp'];
};

/**
* Completes step-up verification and opens a short-lived elevated session.
*
* The AAL2 assertion returned by the MFA service is exchanged at Hydra for
* an elevated access token, whose claims are checked before the session
* opens. The token itself never enters controller state.
*
* @param request - Flow identifier, platform or email proof, and trace reason.
* @returns The elevated profile access token.
*/
export type AuthenticationControllerCompleteStepUpAction = {
type: `AuthenticationController:completeStepUp`;
handler: AuthenticationController['completeStepUp'];
};

/**
* Returns the active elevated token when it meets the requested freshness.
*
* @param request - Optional maximum session age in milliseconds, measured
* from when the token was obtained. Zero always requires a new ceremony.
* @returns A live elevated token, or null when no reusable session exists.
*/
export type AuthenticationControllerGetElevatedProfileTokenAction = {
type: `AuthenticationController:getElevatedProfileToken`;
handler: AuthenticationController['getElevatedProfileToken'];
};

/**
* Clears the in-memory elevated session and its expiration timer.
*/
export type AuthenticationControllerClearStepUpSessionAction = {
type: `AuthenticationController:clearStepUpSession`;
handler: AuthenticationController['clearStepUpSession'];
};

export type AuthenticationControllerPerformSignOutAction = {
type: `AuthenticationController:performSignOut`;
handler: AuthenticationController['performSignOut'];
Expand Down Expand Up @@ -182,6 +228,10 @@ export type AuthenticationControllerMethodActions =
| AuthenticationControllerRefreshEnrolledCredentialsAction
| AuthenticationControllerBeginCredentialEnrollmentAction
| AuthenticationControllerCompleteCredentialEnrollmentAction
| AuthenticationControllerBeginStepUpAction
| AuthenticationControllerCompleteStepUpAction
| AuthenticationControllerGetElevatedProfileTokenAction
| AuthenticationControllerClearStepUpSessionAction
| AuthenticationControllerPerformSignOutAction
| AuthenticationControllerClearStateAction
| AuthenticationControllerGetBearerTokenAction
Expand Down
Loading
Loading