@metamask/profile-sync-controller 33.0.0 → 34.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -1
- package/README.md +11 -4
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.ts +11 -0
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.ts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController-method-action-types.js.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.ts +11 -8
- package/dist/controllers/authentication/AuthenticationController.d.ts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.js +69 -18
- package/dist/controllers/authentication/AuthenticationController.js.map +1 -1
- package/dist/controllers/user-storage/contact-syncing/controller-integration.js +5 -5
- package/dist/controllers/user-storage/contact-syncing/controller-integration.js.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.ts +2 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.ts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.js +2 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.js.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/mfa/schemas.d.ts +8 -2
- package/dist/sdk/authentication-jwt-bearer/mfa/schemas.d.ts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/mfa/schemas.js +5 -3
- package/dist/sdk/authentication-jwt-bearer/mfa/schemas.js.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/mfa/services.d.ts +3 -1
- package/dist/sdk/authentication-jwt-bearer/mfa/services.d.ts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/mfa/services.js +3 -0
- package/dist/sdk/authentication-jwt-bearer/mfa/services.js.map +1 -1
- package/dist/sdk/authentication.d.ts +1 -1
- package/dist/sdk/authentication.d.ts.map +1 -1
- package/dist/sdk/authentication.js +1 -1
- package/dist/sdk/authentication.js.map +1 -1
- package/dist/sdk/mocks/auth.d.ts +10 -3
- package/dist/sdk/mocks/auth.d.ts.map +1 -1
- package/dist/sdk/mocks/auth.js +1 -3
- package/dist/sdk/mocks/auth.js.map +1 -1
- package/dist/shared/encryption/cache.d.ts +4 -4
- package/dist/shared/encryption/cache.d.ts.map +1 -1
- package/dist/shared/encryption/cache.js.map +1 -1
- package/dist/shared/encryption/constants.d.ts +0 -1
- package/dist/shared/encryption/constants.d.ts.map +1 -1
- package/dist/shared/encryption/constants.js +0 -2
- package/dist/shared/encryption/constants.js.map +1 -1
- package/dist/shared/encryption/encryption.d.ts.map +1 -1
- package/dist/shared/encryption/encryption.js +23 -25
- package/dist/shared/encryption/encryption.js.map +1 -1
- package/dist/shared/storage-schema.js.map +1 -1
- package/dist/shared/types/encryption.d.ts +1 -1
- package/dist/shared/types/encryption.d.ts.map +1 -1
- package/dist/shared/types/encryption.js.map +1 -1
- package/dist/shared/utils/message-signing.d.ts.map +1 -1
- package/dist/shared/utils/message-signing.js +2 -14
- package/dist/shared/utils/message-signing.js.map +1 -1
- package/package.json +15 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController.js","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { TraceCallback, TraceContext } from '@metamask/controller-utils';\nimport { selectHdKeyringEntropySourceIds } from '@metamask/keyring-controller';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithKeyringV2UnsafeAction,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SeedlessOnboardingControllerGetAccessTokenAction,\n SeedlessOnboardingControllerGetStateAction,\n} from '@metamask/seedless-onboarding-controller';\nimport type { Json } from '@metamask/utils';\n\nimport {\n BeginEnrollmentRequestStruct,\n CompleteEnrollmentRequestStruct,\n assertValidMfaRequest,\n BeginVerificationRequestStruct,\n CompleteVerificationRequestStruct,\n GetVerificationTokenRequestStruct,\n parseVerificationTokenClaims,\n} from '../../sdk/authentication-jwt-bearer/mfa/schemas.js';\nimport type {\n LoginIdentifierType,\n LoginResponse,\n ProfileAlias,\n ProfileIdentifier,\n SRPInterface,\n SrpLoginTag,\n UserProfile,\n UserProfileLineage,\n OidcTokenAudience,\n OidcTokenClaims,\n BeginEnrollmentRequest,\n CompleteEnrollmentRequest,\n EnrolledCredential,\n EnrollmentChallenge,\n BeginVerificationRequest,\n CompleteVerificationRequest,\n VerificationToken,\n GetVerificationTokenRequest,\n VerificationChallenge,\n} from '../../sdk/index.js';\nimport {\n assertMessageStartsWithMetamask,\n AuthType,\n Env,\n JwtBearerAuth,\n PairConflictError,\n getMfaErrorCode,\n VerificationTokenInvalidError,\n} from '../../sdk/index.js';\nimport { decodeJwtPayload } from '../../sdk/utils/jwt.js';\nimport { toErrorMessage } from '../../sdk/utils/to-error-message.js';\nimport type { MetaMetricsAuth } from '../../shared/types/services.js';\nimport { getPrimaryHdKeyringEntropySourceId } from '../../shared/utils/entropy-source.js';\nimport { getHdKeyringSeed } from '../../shared/utils/hd-keyring-seed.js';\nimport {\n getMessageSigningPublicKey,\n signMessageWithMessageSigningKey,\n} from '../../shared/utils/message-signing.js';\nimport { AuthenticationControllerMethodActions } from './AuthenticationController-method-action-types.js';\n\nconst controllerName = 'AuthenticationController';\n\n// State\nexport type AuthenticationControllerState = {\n isSignedIn: boolean;\n srpSessionData?: Record<string, LoginResponse>;\n /**\n * Credentials fetched for the current profile. The controller always seeds\n * this to an empty array; it remains optional so partial-state selectors stay\n * assignable to the controller state type.\n */\n enrolledCredentials?: EnrolledCredential[];\n /**\n * Client gate for profile pairing. Defaults to `true` (fresh install /\n * upgrade), set to `false` after a successful `performSignIn` pair, set\n * back to `true` via `requestProfilePairing()` when the SRP set changes,\n * and left `true` on pair failure so the next state shift retries.\n *\n * Optional in the type so partial-state selectors stay assignable to\n * `AuthenticationControllerState`. The controller seeds it via\n * `defaultState` at construction; consumers should read `undefined` as\n * `true` to mirror that runtime default.\n */\n needsProfilePairing?: boolean;\n /**\n * Client gate for social identifier pairing. Defaults to `true` (fresh\n * install / upgrade), set to `false` after a successful or 409\n * `POST /api/v2/profile/pair/identifier`, left `true` on other failures\n * so the next `performSignIn` retries, and cleared without an API call\n * when the user never logged in with a social provider.\n *\n * Optional in the type so partial-state selectors stay assignable to\n * `AuthenticationControllerState`. The controller seeds it via\n * `defaultState` at construction; consumers should read `undefined` as\n * `true` to mirror that runtime default.\n */\n needsSocialPairing?: boolean;\n};\nexport const defaultState: AuthenticationControllerState = {\n isSignedIn: false,\n enrolledCredentials: [],\n needsProfilePairing: true,\n needsSocialPairing: true,\n};\nconst metadata: StateMetadata<AuthenticationControllerState> = {\n isSignedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n needsProfilePairing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n needsSocialPairing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n srpSessionData: {\n // Remove access token and paired identifiers from state logs\n includeInStateLogs: (srpSessionData) => {\n // Unreachable branch, included just to fix a type error for the case where this property is\n // unset. The type gets collapsed to include `| undefined` even though `undefined` is never\n // set here, because we don't yet use `exactOptionalPropertyTypes`.\n // TODO: Remove branch after enabling `exactOptionalPropertyTypes`\n // ref: https://github.com/MetaMask/core/issues/6565\n if (srpSessionData === null || srpSessionData === undefined) {\n return null;\n }\n return Object.entries(srpSessionData).reduce<Record<string, Json>>(\n (sanitizedSrpSessionData, [key, value]) => {\n const { accessToken: _unused, ...tokenWithoutAccessToken } =\n value.token;\n const {\n pairedIdentifierIds: _unusedPairedIdentifierIds,\n ...profileWithoutPairedIdentifierIds\n } = value.profile;\n sanitizedSrpSessionData[key] = {\n ...value,\n token: tokenWithoutAccessToken,\n profile: profileWithoutPairedIdentifierIds,\n };\n return sanitizedSrpSessionData;\n },\n {},\n );\n },\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n enrolledCredentials: {\n // Allow-list so new credential types cannot leak identifiers by default.\n includeInStateLogs: (credentials) =>\n credentials?.map(({ type, status, enrolledAt }) => ({\n type,\n status,\n ...(enrolledAt === undefined ? {} : { enrolledAt }),\n })) ?? null,\n persist: false,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\n/**\n * Upper bound on a verification session's lifetime. The session also ends at\n * the verification token's own `exp` (15 minutes today), whichever comes\n * first, so this only matters if the server ever issues longer-lived tokens.\n * Callers needing a fresher proof pass `maxSessionAgeMs` to\n * `getVerificationToken`.\n */\nexport const VERIFICATION_SESSION_TTL_MS = 15 * 60_000;\n\n/**\n * Default maximum age of a verification session that may authorize enrolling a\n * credential. A session opened for an unrelated operation must not be able to\n * add a factor long after the fact, while a session proven moments ago (or\n * during the same setup flow, via `maxSessionAgeMs`) may.\n */\nexport const ENROLLMENT_MAX_SESSION_AGE_MS = 2 * 60_000;\n\n/**\n * Key of the only verification session held today. Sessions are keyed by token\n * audience so audience-scoped tokens can be added without reshaping storage.\n */\nconst DEFAULT_AUDIENCE = '';\n\ntype VerificationSession = {\n token: VerificationToken;\n expiresAt: number;\n timer: ReturnType<typeof setTimeout>;\n};\ntype ControllerConfig = {\n env: Env;\n /**\n * When `true`, `performSignIn` attempts to attach the Google/Apple/\n * Telegram social identifier to the primary SRP profile. Defaults to\n * `() => false`.\n */\n isSocialPairingEnabled: () => boolean;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'performSignIn',\n 'performSignOut',\n 'getBearerToken',\n 'getSessionProfile',\n 'refreshCanonicalProfileId',\n 'getUserProfileLineage',\n 'getCustomerServiceToken',\n 'getPartnerIdentityToken',\n 'isSignedIn',\n 'requestProfilePairing',\n 'clearState',\n 'refreshEnrolledCredentials',\n 'beginCredentialEnrollment',\n 'completeCredentialEnrollment',\n 'beginCredentialVerification',\n 'completeCredentialVerification',\n 'getVerificationToken',\n 'clearVerificationSession',\n] as const;\n\nexport type Actions =\n | AuthenticationControllerGetStateAction\n | AuthenticationControllerMethodActions;\n\nexport type AuthenticationControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AuthenticationControllerState\n>;\n\nexport type AuthenticationControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AuthenticationControllerState\n >;\n\nexport type ProfileSignInInfo = {\n profileId: string;\n profileAliases: ProfileAlias[];\n profileIdChanged: boolean;\n};\n\nexport type AuthenticationControllerProfileSignInEvent = {\n type: `${typeof controllerName}:profileSignIn`;\n payload: [ProfileSignInInfo];\n};\n\nexport type Events =\n | AuthenticationControllerStateChangeEvent\n | AuthenticationControllerProfileSignInEvent;\n\n// Allowed Actions\ntype AllowedActions =\n | KeyringControllerGetStateAction\n | KeyringControllerWithKeyringV2UnsafeAction\n | SeedlessOnboardingControllerGetStateAction\n | SeedlessOnboardingControllerGetAccessTokenAction;\n\ntype AllowedEvents = KeyringControllerLockEvent | KeyringControllerUnlockEvent;\n\n// Messenger\nexport type AuthenticationControllerMessenger = Messenger<\n typeof controllerName,\n Actions | AllowedActions,\n Events | AllowedEvents\n>;\n\n/**\n * Controller that enables authentication for restricted endpoints.\n * Used for Backup & Sync, Notifications, and other services.\n */\nexport class AuthenticationController extends BaseController<\n typeof controllerName,\n AuthenticationControllerState,\n AuthenticationControllerMessenger\n> {\n readonly #metametrics: MetaMetricsAuth;\n\n readonly #auth: SRPInterface;\n\n readonly #trace: TraceCallback;\n\n readonly #config: ControllerConfig = {\n env: Env.PRD,\n isSocialPairingEnabled: () => false,\n };\n\n #isUnlocked = false;\n\n /**\n * Bumped whenever the authenticated session ends, so an in-flight MFA\n * ceremony started under the previous session cannot apply its result.\n */\n #authSessionEpoch = 0;\n\n /**\n * Verification sessions keyed by token audience. At most one session per\n * audience; only `DEFAULT_AUDIENCE` is used until audience-scoped tokens\n * exist.\n */\n readonly #verificationSessions = new Map<string, VerificationSession>();\n\n /**\n * Sequence number of the most recently started credentials refresh. Only\n * that refresh may write the cache, so a slower, earlier request cannot\n * overwrite a newer list (e.g. an explicit refresh landing after an\n * enrollment's refresh).\n */\n #credentialsRefreshSeq = 0;\n\n /**\n * Bumped by `requestProfilePairing` and `clearState` so an in-flight\n * `performSignIn` can't clear `needsProfilePairing` afterwards.\n */\n #profilePairingRequestEpoch = 0;\n\n readonly #keyringController = {\n setupLockedStateSubscriptions: () => {\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n this.#isUnlocked = isUnlocked;\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n this.messenger.subscribe('KeyringController:lock', () => {\n this.#authSessionEpoch += 1;\n this.#isUnlocked = false;\n this.clearVerificationSession();\n });\n },\n };\n\n constructor({\n messenger,\n state,\n config,\n metametrics,\n trace,\n }: {\n messenger: AuthenticationControllerMessenger;\n state?: AuthenticationControllerState;\n config?: Partial<ControllerConfig>;\n /**\n * Not using the Messaging System as we\n * do not want to tie this strictly to extension\n */\n metametrics: MetaMetricsAuth;\n trace?: TraceCallback;\n }) {\n super({\n messenger,\n metadata,\n name: controllerName,\n state: { ...defaultState, ...state },\n });\n\n if (!metametrics) {\n throw new Error('`metametrics` field is required');\n }\n\n // `??` per key so an explicit `undefined` keeps the default rather than\n // clobbering it.\n this.#config = {\n env: config?.env ?? this.#config.env,\n isSocialPairingEnabled:\n config?.isSocialPairingEnabled ?? this.#config.isSocialPairingEnabled,\n };\n\n this.#metametrics = metametrics;\n this.#trace = trace ?? (((_request, fn) => fn?.()) as TraceCallback);\n\n this.#auth = new JwtBearerAuth(\n {\n env: this.#config.env,\n platform: metametrics.agent,\n type: AuthType.SRP,\n },\n {\n storage: {\n getLoginResponse: this.#getLoginResponseFromState.bind(this),\n setLoginResponse: this.#setLoginResponseToState.bind(this),\n },\n signing: {\n getIdentifier: this.#getPublicKey.bind(this),\n signMessage: this.#signMessage.bind(this),\n },\n getLoginTag: this.#getLoginTag.bind(this),\n getLoginIdentifierType: this.#getLoginIdentifierType.bind(this),\n metametrics: this.#metametrics,\n },\n );\n\n this.#keyringController.setupLockedStateSubscriptions();\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n async #getLoginResponseFromState(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n if (!this.state.srpSessionData?.[resolvedId]) {\n return null;\n }\n return this.state.srpSessionData[resolvedId];\n }\n\n async #setLoginResponseToState(\n loginResponse: LoginResponse,\n entropySourceId?: string,\n ) {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n const metaMetricsId = await this.#metametrics.getMetaMetricsId();\n this.update((state) => {\n state.isSignedIn = true;\n if (!state.srpSessionData) {\n state.srpSessionData = {};\n }\n // The API omits `paired_identifier_ids` when it fails to load them, so\n // keep the last known value rather than wiping it.\n const pairedIdentifierIds =\n loginResponse.profile.pairedIdentifierIds ??\n state.srpSessionData[resolvedId]?.profile.pairedIdentifierIds;\n state.srpSessionData[resolvedId] = {\n ...loginResponse,\n profile: {\n ...loginResponse.profile,\n metaMetricsId,\n ...(pairedIdentifierIds ? { pairedIdentifierIds } : {}),\n },\n };\n });\n }\n\n #assertIsUnlocked(methodName: string): void {\n if (!this.#isUnlocked) {\n throw new Error(`${methodName} - unable to proceed, wallet is locked`);\n }\n }\n\n #assertAuthSessionEpoch(epoch: number, methodName: string): void {\n if (!this.#isUnlocked || this.#authSessionEpoch !== epoch) {\n throw new Error(\n `${methodName} - unable to proceed, the authenticated session ended`,\n );\n }\n }\n\n /**\n * Reads the HD keyring entropy source IDs from KeyringController.\n *\n * @returns The HD keyring metadata IDs, primary first.\n */\n #getHdKeyringEntropySourceIds(): string[] {\n const keyringState = this.messenger.call('KeyringController:getState');\n return selectHdKeyringEntropySourceIds(keyringState);\n }\n\n /**\n * Resolves the primary SRP's entropy source ID from KeyringController rather\n * than the message-signing snap, so callers like `getBearerToken()` are not\n * blocked on snap boot.\n *\n * @returns The primary HD keyring metadata ID.\n * @throws If no HD keyring is available; callers must only resolve while\n * the wallet is unlocked.\n */\n #getPrimaryEntropySourceId(): string {\n const keyringState = this.messenger.call('KeyringController:getState');\n return getPrimaryHdKeyringEntropySourceId(keyringState);\n }\n\n /**\n * Resolves the SRP login tag for `raw_message`.\n *\n * - `primary` for the first HD entropy source\n * - `secondary` for any other entropy source\n *\n * @param entropySourceId - Entropy source for this login attempt.\n * @returns The login tag to append to the signed message.\n */\n async #getLoginTag(entropySourceId?: string): Promise<SrpLoginTag> {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const resolvedId = entropySourceId ?? primaryEntropySourceId;\n\n return resolvedId === primaryEntropySourceId ? 'primary' : 'secondary';\n }\n\n /**\n * Resolves metametrics `identifier_type` for `/srp/login`\n * (`SRP` | `GOOGLE` | `APPLE` | `TELEGRAM`).\n *\n * This is the auth method for the entropy source, independent of\n * {@link SrpLoginTag} (`primary` / `secondary`).\n *\n * SeedlessOnboarding currently exposes a single vault-level\n * `authConnection`, which always backs the primary entropy source. Non-\n * primary sources therefore return `SRP`. If social identities become\n * per-entropy later, resolve from that metadata instead of assuming\n * primary === social.\n *\n * Soft-fails to `SRP` when SeedlessOnboarding is not registered.\n *\n * @param entropySourceId - Entropy source for this login attempt.\n * @returns The login identifier type.\n */\n async #getLoginIdentifierType(\n entropySourceId?: string,\n ): Promise<LoginIdentifierType> {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const resolvedId = entropySourceId ?? primaryEntropySourceId;\n\n // Social vault authConnection is only associated with the primary source.\n if (resolvedId !== primaryEntropySourceId) {\n return 'SRP';\n }\n\n return this.#resolveSocialIdentifierType();\n }\n\n /**\n * Maps `SeedlessOnboardingController.state.authConnection` to a login\n * identifier type when a social vault is present.\n *\n * Returns `SRP` if there is no vault, the provider is unrecognized, or\n * SeedlessOnboardingController is unavailable on the messenger.\n *\n * @returns The social provider identifier type, or `SRP`.\n */\n #resolveSocialIdentifierType(): LoginIdentifierType {\n try {\n return this.#identifierTypeFromSeedlessState(\n this.messenger.call('SeedlessOnboardingController:getState'),\n );\n } catch {\n return 'SRP';\n }\n }\n\n /**\n * Maps SeedlessOnboarding vault + `authConnection` to a login identifier\n * type. Does not catch messenger errors — callers decide whether a\n * missing controller is `SRP` or a retryable failure.\n *\n * @param state - SeedlessOnboarding slice used for the mapping.\n * @param state.vault - Encrypted social vault, if present.\n * @param state.authConnection - Social provider id (`google` / `apple` /\n * `telegram`).\n * @returns The social provider identifier type, or `SRP`.\n */\n #identifierTypeFromSeedlessState(state: {\n vault?: string | null;\n authConnection?: string;\n }): LoginIdentifierType {\n if (state.vault === null || state.vault === undefined) {\n return 'SRP';\n }\n\n // Match provider strings from SeedlessOnboarding state rather than\n // importing AuthConnection — a value import would load that package\n // (and its heavy deps) whenever this controller is imported.\n switch (state.authConnection) {\n case 'google':\n return 'GOOGLE';\n case 'apple':\n return 'APPLE';\n case 'telegram':\n return 'TELEGRAM';\n default:\n return 'SRP';\n }\n }\n\n public async performSignIn(): Promise<string[]> {\n this.#assertIsUnlocked('performSignIn');\n\n const epochAtStart = this.#profilePairingRequestEpoch;\n const entropySourceIds = this.#getHdKeyringEntropySourceIds();\n const accessTokens: string[] = [];\n\n // We iterate sequentially in order to be sure that the first entry\n // is the primary SRP LoginResponse.\n for (const entropySourceId of entropySourceIds) {\n const accessToken = await this.#auth.getAccessToken(entropySourceId);\n accessTokens.push(accessToken);\n }\n\n if (entropySourceIds.length < 2) {\n // Single-SRP wallet: nothing to pair.\n this.#tryClearNeedsProfilePairing(epochAtStart);\n } else {\n // Pair failures must not break sign-in; the gate stays `true` for retry.\n try {\n await this.#doPair(accessTokens, epochAtStart);\n } catch {\n // noop\n }\n }\n\n try {\n await this.#trySocialPairing(accessTokens[0]);\n } catch {\n // noop\n }\n\n return accessTokens;\n }\n\n /**\n * Attaches a Google/Apple/Telegram social identifier to the primary SRP\n * profile via `POST /api/v2/profile/pair/identifier`.\n *\n * Runs only when `isSocialPairingEnabled()` is true and\n * `needsSocialPairing` is not `false` (`undefined` is treated as true).\n * No social provider (`authConnection` unset) → clear the flag (nothing\n * to pair). Social provider set but vault missing → skip (onboarding\n * still in flight). Google without email → skip (API requires it).\n * Telegram never sends `email` (the field holds a display name, not an\n * address). Missing social JWT → skip. 409 Conflict → clear the flag\n * (identifier already owned; retry is pointless). Other errors\n * propagate so the caller can swallow them and leave the flag set for\n * retry.\n *\n * @param primaryAccessToken - Primary SRP OIDC access token used as the\n * Bearer credential. The profile associated with this JWT becomes the\n * canonical owner of the social identifier.\n */\n async #trySocialPairing(primaryAccessToken?: string): Promise<void> {\n if (\n !this.#config.isSocialPairingEnabled() ||\n this.state.needsSocialPairing === false ||\n !primaryAccessToken\n ) {\n return;\n }\n\n let seedlessState: {\n vault?: string | null;\n authConnection?: string;\n socialLoginEmail?: string;\n };\n try {\n seedlessState = this.messenger.call(\n 'SeedlessOnboardingController:getState',\n );\n } catch {\n // Controller missing or getState failed — retry next performSignIn.\n return;\n }\n\n // `SRP` here does not describe the vault: it is the mapping's \"no social\n // identity\" result, returned when there is no seedless vault or the\n // provider is unrecognised. In both cases there is nothing to pair.\n const identifierType = this.#identifierTypeFromSeedlessState(seedlessState);\n if (identifierType === 'SRP') {\n const { vault, authConnection } = seedlessState;\n // Vault not written yet but a social provider is already known —\n // onboarding is still in flight. Do not clear; retry next sign-in.\n if ((vault === null || vault === undefined) && Boolean(authConnection)) {\n return;\n }\n this.#clearNeedsSocialPairing();\n return;\n }\n\n // Email policy follows the API contract: GOOGLE requires `email` (400\n // without it), APPLE accepts it optionally, TELEGRAM must not send it\n // (clients store a display name in `socialLoginEmail` for Telegram).\n // Clients request the `email` scope for Google, so a missing value is a\n // defensive case rather than an expected one; returning here leaves\n // `needsSocialPairing` set so the next sign-in retries instead of\n // sending a request that would fail.\n const { socialLoginEmail } = seedlessState;\n let email: string | undefined;\n if (identifierType === 'GOOGLE') {\n if (!socialLoginEmail) {\n return;\n }\n email = socialLoginEmail;\n } else if (identifierType === 'APPLE') {\n email = socialLoginEmail;\n }\n\n const socialJwt = await this.messenger.call(\n 'SeedlessOnboardingController:getAccessToken',\n );\n if (socialJwt === undefined || socialJwt === '') {\n return;\n }\n\n try {\n const pairedIdentifierIds = await this.#auth.pairSocialIdentifier(\n {\n identifierType,\n socialJwt,\n ...(email ? { email } : {}),\n },\n primaryAccessToken,\n );\n this.#setPrimaryPairedIdentifierIds(pairedIdentifierIds);\n this.#clearNeedsSocialPairing();\n } catch (error) {\n if (error instanceof PairConflictError) {\n this.#clearNeedsSocialPairing();\n return;\n }\n throw error;\n }\n }\n\n #clearNeedsSocialPairing(): void {\n if (this.state.needsSocialPairing !== false) {\n this.update((state) => {\n state.needsSocialPairing = false;\n });\n }\n }\n\n /**\n * Marks profile pairing as needed. Clients call this when the SRP set\n * changes (e.g. a new keyring was added) so the next auto-sign-in cycle\n * re-runs `performSignIn` and re-pairs.\n */\n public requestProfilePairing(): void {\n this.#profilePairingRequestEpoch += 1;\n if (!this.state.needsProfilePairing) {\n this.update((state) => {\n state.needsProfilePairing = true;\n });\n }\n }\n\n /**\n * Clears `needsProfilePairing` only if no `requestProfilePairing` call\n * landed since `epochAtStart` was captured. Prevents `performSignIn`\n * from silently overwriting a concurrent rearm.\n *\n * @param epochAtStart - Epoch value captured at the start of `performSignIn`.\n */\n #tryClearNeedsProfilePairing(epochAtStart: number): void {\n if (this.#profilePairingRequestEpoch !== epochAtStart) {\n return;\n }\n if (this.state.needsProfilePairing) {\n this.update((state) => {\n state.needsProfilePairing = false;\n });\n }\n }\n\n /**\n * Pairs all SRPs via `POST /profile/pair`, propagates the canonical\n * profile ID, clears `needsProfilePairing`, and emits\n * `AuthenticationController:profileSignIn` when the canonical changes or\n * new aliases are returned. Throws on failure.\n *\n * @param accessTokens - Per-SRP access tokens, primary first.\n * @param epochAtStart - Pairing-request epoch captured by the caller.\n * Used to skip the gate clear if `requestProfilePairing` ran while the\n * pair API call was in-flight.\n */\n async #doPair(accessTokens: string[], epochAtStart: number): Promise<void> {\n const previousCanonical = this.#getCanonicalProfileId();\n\n const profileAliases = await this.#pairSrpProfiles(accessTokens);\n const newCanonical = this.#getCanonicalProfileId();\n\n // If somehow we cannot compute the new canonical profile ID after pairing,\n // we just return now and do not update the `needsProfilePairing` flag.\n if (!newCanonical) {\n return;\n }\n\n this.#tryClearNeedsProfilePairing(epochAtStart);\n\n const profileIdChanged = previousCanonical !== newCanonical;\n const shouldEmitProfileSignInEvent =\n profileIdChanged || profileAliases.length > 0;\n\n if (shouldEmitProfileSignInEvent) {\n this.messenger.publish('AuthenticationController:profileSignIn', {\n profileId: newCanonical,\n profileAliases,\n profileIdChanged,\n });\n }\n }\n\n async #pairSrpProfiles(accessTokens: string[]): Promise<ProfileAlias[]> {\n if (accessTokens.length < 2) {\n return [];\n }\n const primaryAccessToken = accessTokens[0]; // Associated with primary SRP.\n const {\n profileAliases,\n profile: { canonicalProfileId, pairedIdentifierIds },\n } = await this.#auth.pairSrpProfiles(accessTokens, primaryAccessToken);\n this.#propagateCanonical(canonicalProfileId);\n this.#setPrimaryPairedIdentifierIds(pairedIdentifierIds);\n return profileAliases;\n }\n\n /**\n * Pair calls use the primary SRP's token, so their response only describes\n * the primary's profile: secondaries skipped by the server are not in it.\n *\n * @param pairedIdentifierIds - Identifiers returned by the pair call, if any.\n */\n #setPrimaryPairedIdentifierIds(\n pairedIdentifierIds: ProfileIdentifier[] | undefined,\n ): void {\n if (!pairedIdentifierIds) {\n return;\n }\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n this.update((state) => {\n const entry = state.srpSessionData?.[primaryEntropySourceId];\n if (entry?.profile) {\n entry.profile.pairedIdentifierIds = pairedIdentifierIds;\n }\n });\n }\n\n #propagateCanonical(canonicalProfileId: string): void {\n const { srpSessionData } = this.state;\n if (!srpSessionData) {\n return;\n }\n\n this.update((state) => {\n for (const entry of Object.values(state.srpSessionData ?? {})) {\n if (entry?.profile) {\n entry.profile.canonicalProfileId = canonicalProfileId;\n }\n }\n });\n }\n\n /**\n * Returns the canonical profile id from the primary SRP's cached session.\n * Returns `null` when no session exists yet for the primary SRP.\n *\n * Always reads from the primary SRP because the canonical is shared across\n * all paired SRPs after `#propagateCanonical`.\n *\n * @returns The canonical profile id, or `null` if unavailable.\n */\n #getCanonicalProfileId(): string | null {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n return (\n this.state.srpSessionData?.[primaryEntropySourceId]?.profile\n ?.canonicalProfileId ?? null\n );\n }\n\n /**\n * Runs one MFA network step inside a trace span tagged with the caller's\n * operation and the credential type, recording the outcome and MFA error\n * code.\n *\n * @param name - Span name.\n * @param operation - Caller-supplied `reason.operation`.\n * @param credentialType - Credential type the step concerns.\n * @param fn - The network step.\n * @returns The step's result.\n */\n async #runMfaRequest<Result>(\n name: string,\n operation: string,\n credentialType: string,\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return await this.#trace(\n {\n name,\n tags: { operation, credentialType },\n },\n async (context) => {\n try {\n const result = await fn();\n this.#setTraceAttribute(context, 'outcome', 'success');\n return result;\n } catch (error) {\n this.#setTraceAttribute(context, 'outcome', 'error');\n const mfaCode = getMfaErrorCode(error);\n if (mfaCode) {\n this.#setTraceAttribute(context, 'mfaErrorCode', mfaCode);\n if (mfaCode === 'authentication_required' && this.#isUnlocked) {\n this.#invalidateSrpSession(this.#getPrimaryEntropySourceId());\n // A verification session must not outlive a rejected base\n // session.\n this.clearVerificationSession();\n }\n }\n throw error;\n }\n },\n );\n }\n\n /**\n * `TraceContext` is opaque in `@metamask/controller-utils`; both clients\n * hand back a Sentry span, so attributes are set via duck typing and a\n * non-Sentry context is a silent no-op.\n *\n * @param context - Span handed to the trace callback, if any.\n * @param key - Attribute name.\n * @param value - Attribute value.\n */\n #setTraceAttribute(\n context: TraceContext | undefined,\n key: string,\n value: string,\n ): void {\n const traceSpan = context as\n | { setAttribute?: (attribute: string, data: string) => void }\n | undefined;\n traceSpan?.setAttribute?.(key, value);\n }\n\n /**\n * Refreshes credentials enrolled on the canonical profile.\n *\n * @returns The current supported credentials.\n */\n public async refreshEnrolledCredentials(): Promise<EnrolledCredential[]> {\n this.#assertIsUnlocked('refreshEnrolledCredentials');\n const sessionEpoch = this.#authSessionEpoch;\n this.#credentialsRefreshSeq += 1;\n const refreshSeq = this.#credentialsRefreshSeq;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const credentials = await this.#runMfaRequest(\n 'MFA Credentials Refresh',\n 'credentials.refresh',\n 'all',\n async () => await this.#auth.getMfaCredentials(primaryEntropySourceId),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'refreshEnrolledCredentials');\n\n // A newer refresh started while this one was in flight; its result is at\n // least as fresh, so defer to it rather than overwrite with older data.\n if (refreshSeq !== this.#credentialsRefreshSeq) {\n return this.#getEnrolledCredentials();\n }\n\n // Skip the write when nothing changed so subscribers are not woken up by\n // a fresh-but-identical array.\n if (\n JSON.stringify(credentials) !==\n JSON.stringify(this.#getEnrolledCredentials())\n ) {\n this.update((state) => {\n state.enrolledCredentials = credentials;\n });\n }\n return credentials;\n }\n\n /**\n * Begins enrollment of a passkey or email OTP credential.\n *\n * @param request - Credential, optional email address, and trace reason.\n * @returns A challenge for the client-owned ceremony.\n */\n public async beginCredentialEnrollment(\n request: BeginEnrollmentRequest,\n ): Promise<EnrollmentChallenge> {\n this.#assertIsUnlocked('beginCredentialEnrollment');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, BeginEnrollmentRequestStruct);\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n // The server requires AAL2 to add a credential once one that proves AAL2\n // exists: send the verification token only while a recent enough session\n // is live and let the server decide. An older session stays open for\n // other consumers.\n const accessToken = this.getVerificationToken({\n maxSessionAgeMs: request.maxSessionAgeMs ?? ENROLLMENT_MAX_SESSION_AGE_MS,\n })?.accessToken;\n const challenge = await this.#runMfaRequest(\n 'MFA Enroll Begin',\n request.reason.operation,\n request.type,\n async () =>\n await this.#auth.beginMfaEnrollment(request.type, {\n email: request.email,\n entropySourceId: primaryEntropySourceId,\n accessToken,\n }),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'beginCredentialEnrollment');\n return challenge;\n }\n\n /**\n * Completes credential enrollment and refreshes the credential cache.\n *\n * A cache-refresh failure does not undo successful enrollment. Email\n * enrollment invalidates the primary SRP session *after* refresh so the\n * credentials call can reuse the still-valid access token; the next token\n * fetch then includes the newly verified email claim. That invalidation\n * happens even if the session ends mid-request: the enrollment succeeded\n * on the server, so a token cached across a lock must not be reused\n * without the new claim.\n *\n * @param request - Flow identifier, platform or email proof, and trace reason.\n * @returns The refreshed credentials, or the existing cache if refresh fails.\n */\n public async completeCredentialEnrollment(\n request: CompleteEnrollmentRequest,\n ): Promise<EnrolledCredential[]> {\n this.#assertIsUnlocked('completeCredentialEnrollment');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, CompleteEnrollmentRequestStruct);\n const { type } = request.proof;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n await this.#runMfaRequest(\n 'MFA Enroll Complete',\n request.reason.operation,\n type,\n async () =>\n await this.#auth.completeMfaEnrollment(\n request.flowId,\n request.proof,\n primaryEntropySourceId,\n ),\n );\n\n try {\n this.#assertAuthSessionEpoch(\n sessionEpoch,\n 'completeCredentialEnrollment',\n );\n } catch (error) {\n if (type === 'email_otp') {\n this.#invalidateSrpSession(primaryEntropySourceId);\n }\n throw error;\n }\n\n // The verification session is deliberately kept: adding a factor does not\n // weaken an earlier proof, and `beginCredentialEnrollment` already limits\n // which sessions may add the next one.\n try {\n return await this.refreshEnrolledCredentials();\n } catch {\n return this.#getEnrolledCredentials();\n } finally {\n if (type === 'email_otp') {\n this.#invalidateSrpSession(primaryEntropySourceId);\n }\n }\n }\n\n /**\n * Begins verification with an enrolled credential.\n *\n * @param request - Credential type and trace reason.\n * @returns A challenge for the client-owned ceremony.\n */\n public async beginCredentialVerification(\n request: BeginVerificationRequest,\n ): Promise<VerificationChallenge> {\n this.#assertIsUnlocked('beginCredentialVerification');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, BeginVerificationRequestStruct);\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const challenge = await this.#runMfaRequest(\n 'MFA Verification Begin',\n request.reason.operation,\n request.type,\n async () =>\n await this.#auth.beginMfaVerification(\n request.type,\n primaryEntropySourceId,\n ),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'beginCredentialVerification');\n return challenge;\n }\n\n /**\n * Completes verification and opens a short-lived verification session.\n *\n * The assertion returned by the MFA service is exchanged at Hydra for an\n * access token. Its assurance level is not checked: the services receiving\n * the token enforce their own requirements. The token itself never enters\n * controller state.\n *\n * @param request - Flow identifier, platform or email proof, and trace reason.\n * @returns The verification token.\n */\n public async completeCredentialVerification(\n request: CompleteVerificationRequest,\n ): Promise<VerificationToken> {\n this.#assertIsUnlocked('completeCredentialVerification');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, CompleteVerificationRequestStruct);\n const { type } = request.proof;\n const { operation } = request.reason;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const assertion = await this.#runMfaRequest(\n 'MFA Verification Complete',\n operation,\n type,\n async () =>\n await this.#auth.completeMfaVerification(\n request.flowId,\n request.proof,\n primaryEntropySourceId,\n ),\n );\n this.#assertAuthSessionEpoch(\n sessionEpoch,\n 'completeCredentialVerification',\n );\n const accessToken = await this.#runMfaRequest(\n 'MFA Token Exchange',\n operation,\n type,\n async () => await this.#auth.exchangeMfaAssertion(assertion.token),\n );\n this.#assertAuthSessionEpoch(\n sessionEpoch,\n 'completeCredentialVerification',\n );\n\n let decodedClaims: unknown;\n try {\n decodedClaims = decodeJwtPayload(accessToken.accessToken);\n } catch (error) {\n throw new VerificationTokenInvalidError(toErrorMessage(error));\n }\n const claims = parseVerificationTokenClaims(decodedClaims);\n if (claims.exp * 1000 <= Date.now()) {\n throw new VerificationTokenInvalidError('Verification token is expired');\n }\n\n const token: VerificationToken = { ...accessToken, claims };\n this.#openVerificationSession(token);\n return token;\n }\n\n /**\n * Returns the active verification token when it meets the requested\n * freshness.\n *\n * @param request - Optional maximum session age in milliseconds, measured\n * from when the token was obtained. Zero always requires a new ceremony.\n * @returns A live verification token, or null when no reusable session\n * exists.\n */\n public getVerificationToken(\n request: GetVerificationTokenRequest = {},\n ): VerificationToken | null {\n this.#assertIsUnlocked('getVerificationToken');\n assertValidMfaRequest(request, GetVerificationTokenRequestStruct);\n const session = this.#verificationSessions.get(DEFAULT_AUDIENCE);\n if (!session) {\n return null;\n }\n const now = Date.now();\n if (now >= session.expiresAt) {\n // The hard-expiry timer has not fired yet (e.g. a suspended tab).\n this.#dropVerificationSession(DEFAULT_AUDIENCE);\n return null;\n }\n // `>=` so a zero max age always forces a fresh ceremony. Not cleared: the\n // max age is this caller's requirement; others may still use the session.\n const maxAge = request.maxSessionAgeMs;\n if (maxAge !== undefined && now - session.token.obtainedAt >= maxAge) {\n return null;\n }\n return session.token;\n }\n\n /**\n * Opens the verification session. Its lifetime is the session TTL clamped to\n * the token's own `exp`, so the session never outlives the token.\n *\n * @param token - The freshly exchanged verification token.\n */\n #openVerificationSession(token: VerificationToken): void {\n const audience = DEFAULT_AUDIENCE;\n this.#dropVerificationSession(audience);\n const expiresAt = Math.min(\n token.obtainedAt + VERIFICATION_SESSION_TTL_MS,\n token.claims.exp * 1000,\n );\n const timer = setTimeout(\n () => this.#dropVerificationSession(audience),\n Math.max(0, expiresAt - Date.now()),\n );\n // Never keep a Node process alive for the expiry timer (tests, tooling).\n (timer as { unref?: () => void }).unref?.();\n this.#verificationSessions.set(audience, { token, expiresAt, timer });\n }\n\n /**\n * Drops one audience's verification session and its expiration timer.\n *\n * @param audience - Audience whose session to drop.\n */\n #dropVerificationSession(audience: string): void {\n const session = this.#verificationSessions.get(audience);\n if (session) {\n clearTimeout(session.timer);\n this.#verificationSessions.delete(audience);\n }\n }\n\n /**\n * Clears every in-memory verification session and its expiration timer.\n */\n public clearVerificationSession(): void {\n for (const { timer } of this.#verificationSessions.values()) {\n clearTimeout(timer);\n }\n this.#verificationSessions.clear();\n }\n\n /**\n * Drops the cached credential list. Callers that wipe profile state must go\n * through this so subscribers never keep credentials belonging to a profile\n * that is no longer active.\n */\n #clearEnrolledCredentials(): void {\n if (this.#getEnrolledCredentials().length === 0) {\n return;\n }\n this.update((state) => {\n state.enrolledCredentials = [];\n });\n }\n\n #getEnrolledCredentials(): EnrolledCredential[] {\n return this.state.enrolledCredentials ?? [];\n }\n\n public performSignOut(): void {\n this.#authSessionEpoch += 1;\n this.clearVerificationSession();\n this.#clearEnrolledCredentials();\n this.update((state) => {\n state.isSignedIn = false;\n state.srpSessionData = undefined;\n });\n }\n\n /**\n * Resets the controller to `defaultState`. Clients call this on wallet reset\n * so the next wallet starts unsigned with both pairing gates re-armed.\n */\n public clearState(): void {\n this.#profilePairingRequestEpoch += 1;\n this.#authSessionEpoch += 1;\n this.clearVerificationSession();\n this.#clearEnrolledCredentials();\n this.update(() => ({ ...defaultState }));\n }\n\n /**\n * Returns a bearer token for the specified SRP, logging in if needed.\n *\n * When called without `entropySourceId`, returns the primary (first) SRP's\n * access token, which is effectively the canonical\n * profile's token that can be used by alias-aware consumers for cross-SRP\n * operations.\n *\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The OIDC access token.\n */\n public async getBearerToken(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('getBearerToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getAccessToken(resolvedId);\n }\n\n /**\n * Returns the cached session profile, logging in if no session exists.\n *\n * The returned `canonicalProfileId` reflects the value from the most recent\n * login or pairing. In the rare event where a canonical changed because of\n * a pairing that happened on another device, the cached value may be stale\n * until the next login. For guaranteed freshness, call\n * `refreshCanonicalProfileId()` before reading `canonicalProfileId`.\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns profile for the session.\n */\n public async getSessionProfile(\n entropySourceId?: string,\n ): Promise<UserProfile> {\n this.#assertIsUnlocked('getSessionProfile');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getUserProfile(resolvedId);\n }\n\n /**\n * Forces a fresh retrieval of the canonical profile ID from the server\n * and propagates it to all cached SRP sessions.\n *\n * This method invalidates the primary SRP's cached session and forces a\n * re-login. Use it before operations that require a guaranteed-fresh\n * canonical (e.g. storage key derivation for Accounts ADR 0005). For\n * best-effort reads, use\n * `getSessionProfile().canonicalProfileId` instead.\n *\n * Only the primary SRP is re-logged-in regardless of how many SRPs exist —\n * the server returns the current canonical for the entire pairing group\n * from any single SRP login.\n *\n * @returns The refreshed canonical profile ID.\n */\n public async refreshCanonicalProfileId(): Promise<string> {\n this.#assertIsUnlocked('refreshCanonicalProfileId');\n\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n this.#invalidateSrpSession(primaryEntropySourceId);\n await this.#auth.getAccessToken(primaryEntropySourceId);\n\n const canonical = this.#getCanonicalProfileId();\n if (!canonical) {\n throw new Error(\n 'refreshCanonicalProfileId - Unable to resolve canonical profile ID',\n );\n }\n\n this.#propagateCanonical(canonical);\n return canonical;\n }\n\n #invalidateSrpSession(entropySourceId: string): void {\n this.update((state) => {\n const entry = state.srpSessionData?.[entropySourceId];\n if (entry?.profile) {\n // Setting canonicalProfileId to '' forces a re-fetch on the next\n // #getAuthSession call. The falsy check (!auth.profile.canonicalProfileId)\n // treats '' the same as undefined/null — all signal an invalid session.\n entry.profile.canonicalProfileId = '';\n }\n });\n }\n\n public async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n this.#assertIsUnlocked('getUserProfileLineage');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getUserProfileLineage(resolvedId);\n }\n\n /**\n * Returns a Customer Service specific access token for the specified SRP,\n * logging in if needed.\n *\n * Exchanges the OIDC access token for a short-lived token scoped to the\n * customer-service audience. Customer Service tooling consumes this token to\n * identify and authenticate the user.\n *\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The customer-service access token.\n */\n public async getCustomerServiceToken(\n entropySourceId?: string,\n ): Promise<string> {\n this.#assertIsUnlocked('getCustomerServiceToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getCustomerServiceToken(resolvedId);\n }\n\n /**\n * Mints a partner identity token for the specified SRP, logging in if needed.\n *\n * Calls `POST /api/v2/oidc/token` with the Hydra login bearer and returns\n * the minted `access_token`. Email on live tokens is under JWT `ext`.\n * HTTP 422 throws `EmailRequiredError` when this profile has no\n * verified email.\n *\n * @param claims - Claim names to embed. Only `email` is supported.\n * @param audience - Partner audience (`kyc` or `iron`).\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The partner identity access token.\n */\n public async getPartnerIdentityToken(\n claims: OidcTokenClaims,\n audience: OidcTokenAudience,\n entropySourceId?: string,\n ): Promise<string> {\n this.#assertIsUnlocked('getPartnerIdentityToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getPartnerIdentityToken(\n claims,\n audience,\n resolvedId,\n );\n }\n\n public isSignedIn(): boolean {\n return this.state.isSignedIn;\n }\n\n /**\n * Reads the BIP-39 seed for an HD entropy source from KeyringController.\n *\n * @param entropySourceId - Entropy source ID. Defaults to the primary HD\n * keyring.\n * @returns The HD keyring seed.\n */\n async #getHdKeyringSeed(entropySourceId?: string): Promise<Uint8Array> {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return getHdKeyringSeed(this.messenger, resolvedId);\n }\n\n /**\n * Returns the message-signing public key via native SIP-6 derivation\n * (same key as `@metamask/message-signing-snap` with empty salt).\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns The public key hex.\n */\n async #getPublicKey(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('#getPublicKey');\n const seed = await this.#getHdKeyringSeed(entropySourceId);\n return getMessageSigningPublicKey(seed);\n }\n\n #_signMessageCache: Record<string, string> = {};\n\n /**\n * Builds a cache key scoped to a specific entropy source, so each SRP's\n * signature stays isolated (same pattern as `UserStorageController`).\n *\n * When `entropySourceId` is omitted (primary SRP), it is resolved to the\n * primary HD keyring's metadata ID rather than a stable literal. Because that\n * ID is randomly regenerated whenever the vault is recreated (e.g. on\n * restore), the cached entry is naturally invalidated across vaults — a\n * different SRP can never inherit the previous primary's cached signature.\n *\n * @param message - The tagged message used for signing.\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The scoped cache key.\n */\n #scopedCacheKey(\n message: `metamask:${string}`,\n entropySourceId?: string,\n ): string {\n return `${entropySourceId ?? this.#getPrimaryEntropySourceId()}:${message}`;\n }\n\n /**\n * Signs a `metamask:…` message with the native SIP-6 message-signing key.\n *\n * @param message - A specific tagged message to sign.\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns Compact secp256k1 signature hex.\n */\n async #signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n assertMessageStartsWithMetamask(message);\n this.#assertIsUnlocked('#signMessage');\n\n const cacheKey = this.#scopedCacheKey(message, entropySourceId);\n if (this.#_signMessageCache[cacheKey]) {\n return this.#_signMessageCache[cacheKey];\n }\n\n const seed = await this.#getHdKeyringSeed(entropySourceId);\n const result = await signMessageWithMessageSigningKey(message, seed);\n\n this.#_signMessageCache[cacheKey] = result;\n\n return result;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AuthenticationController.js","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { TraceCallback, TraceContext } from '@metamask/controller-utils';\nimport { selectHdKeyringEntropySourceIds } from '@metamask/keyring-controller';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithKeyringV2UnsafeAction,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SeedlessOnboardingControllerGetAccessTokenAction,\n SeedlessOnboardingControllerGetStateAction,\n} from '@metamask/seedless-onboarding-controller';\nimport type { Json } from '@metamask/utils';\n\nimport {\n BeginEnrollmentRequestStruct,\n CompleteEnrollmentRequestStruct,\n assertValidMfaRequest,\n BeginVerificationRequestStruct,\n CompleteVerificationRequestStruct,\n GetVerificationTokenRequestStruct,\n parseVerificationTokenClaims,\n} from '../../sdk/authentication-jwt-bearer/mfa/schemas.js';\nimport type {\n LoginIdentifierType,\n LoginResponse,\n ProfileAlias,\n ProfileIdentifier,\n SRPInterface,\n SrpLoginTag,\n UserProfile,\n UserProfileLineage,\n OidcTokenAudience,\n OidcTokenClaims,\n BeginEnrollmentRequest,\n CompleteEnrollmentRequest,\n EnrolledCredential,\n EnrollmentChallenge,\n BeginVerificationRequest,\n CompleteVerificationRequest,\n VerificationToken,\n GetVerificationTokenRequest,\n MfaCredentialType,\n MfaVerificationAssertion,\n VerificationChallenge,\n} from '../../sdk/index.js';\nimport {\n assertMessageStartsWithMetamask,\n AuthType,\n Env,\n JwtBearerAuth,\n PairConflictError,\n getMfaErrorCode,\n VerificationTokenInvalidError,\n} from '../../sdk/index.js';\nimport { decodeJwtPayload } from '../../sdk/utils/jwt.js';\nimport { toErrorMessage } from '../../sdk/utils/to-error-message.js';\nimport type { MetaMetricsAuth } from '../../shared/types/services.js';\nimport { getPrimaryHdKeyringEntropySourceId } from '../../shared/utils/entropy-source.js';\nimport { getHdKeyringSeed } from '../../shared/utils/hd-keyring-seed.js';\nimport {\n getMessageSigningPublicKey,\n signMessageWithMessageSigningKey,\n} from '../../shared/utils/message-signing.js';\nimport { AuthenticationControllerMethodActions } from './AuthenticationController-method-action-types.js';\n\nconst controllerName = 'AuthenticationController';\n\n// State\nexport type AuthenticationControllerState = {\n isSignedIn: boolean;\n srpSessionData?: Record<string, LoginResponse>;\n /**\n * Credentials fetched for the current profile. The controller always seeds\n * this to an empty array; it remains optional so partial-state selectors stay\n * assignable to the controller state type.\n */\n enrolledCredentials?: EnrolledCredential[];\n /**\n * Client gate for profile pairing. Defaults to `true` (fresh install /\n * upgrade), set to `false` after a successful `performSignIn` pair, set\n * back to `true` via `requestProfilePairing()` when the SRP set changes,\n * and left `true` on pair failure so the next state shift retries.\n *\n * Optional in the type so partial-state selectors stay assignable to\n * `AuthenticationControllerState`. The controller seeds it via\n * `defaultState` at construction; consumers should read `undefined` as\n * `true` to mirror that runtime default.\n */\n needsProfilePairing?: boolean;\n /**\n * Client gate for social identifier pairing. Defaults to `true` (fresh\n * install / upgrade), set to `false` after a successful or 409\n * `POST /api/v2/profile/pair/identifier`, left `true` on other failures\n * so the next `performSignIn` retries, and cleared without an API call\n * when the user never logged in with a social provider.\n *\n * Optional in the type so partial-state selectors stay assignable to\n * `AuthenticationControllerState`. The controller seeds it via\n * `defaultState` at construction; consumers should read `undefined` as\n * `true` to mirror that runtime default.\n */\n needsSocialPairing?: boolean;\n};\nexport const defaultState: AuthenticationControllerState = {\n isSignedIn: false,\n enrolledCredentials: [],\n needsProfilePairing: true,\n needsSocialPairing: true,\n};\nconst metadata: StateMetadata<AuthenticationControllerState> = {\n isSignedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n needsProfilePairing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n needsSocialPairing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n srpSessionData: {\n // Remove access token and paired identifiers from state logs\n includeInStateLogs: (srpSessionData) => {\n // Unreachable branch, included just to fix a type error for the case where this property is\n // unset. The type gets collapsed to include `| undefined` even though `undefined` is never\n // set here, because we don't yet use `exactOptionalPropertyTypes`.\n // TODO: Remove branch after enabling `exactOptionalPropertyTypes`\n // ref: https://github.com/MetaMask/core/issues/6565\n if (srpSessionData === null || srpSessionData === undefined) {\n return null;\n }\n return Object.entries(srpSessionData).reduce<Record<string, Json>>(\n (sanitizedSrpSessionData, [key, value]) => {\n const { accessToken: _unused, ...tokenWithoutAccessToken } =\n value.token;\n const {\n pairedIdentifierIds: _unusedPairedIdentifierIds,\n ...profileWithoutPairedIdentifierIds\n } = value.profile;\n sanitizedSrpSessionData[key] = {\n ...value,\n token: tokenWithoutAccessToken,\n profile: profileWithoutPairedIdentifierIds,\n };\n return sanitizedSrpSessionData;\n },\n {},\n );\n },\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n enrolledCredentials: {\n // Allow-list so new credential types cannot leak identifiers by default.\n includeInStateLogs: (credentials) =>\n credentials?.map(({ type, status, enrolledAt }) => ({\n type,\n status,\n ...(enrolledAt === undefined ? {} : { enrolledAt }),\n })) ?? null,\n persist: false,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\n/**\n * Default maximum age of a verification session that may authorize enrolling a\n * credential. A session opened for an unrelated operation must not be able to\n * add a factor long after the fact, while a session proven moments ago (or\n * during the same setup flow, via `maxSessionAgeMs`) may.\n */\nexport const ENROLLMENT_MAX_SESSION_AGE_MS = 2 * 60_000;\n\n/**\n * Key of the only verification session held today. Sessions are keyed by token\n * audience so audience-scoped tokens can be added without reshaping storage.\n */\nconst DEFAULT_AUDIENCE = '';\n\ntype VerificationSession = {\n token: VerificationToken;\n expiresAt: number;\n timer: ReturnType<typeof setTimeout>;\n};\ntype ControllerConfig = {\n env: Env;\n /**\n * When `true`, `performSignIn` attempts to attach the Google/Apple/\n * Telegram social identifier to the primary SRP profile. Defaults to\n * `() => false`.\n */\n isSocialPairingEnabled: () => boolean;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'performSignIn',\n 'performSignOut',\n 'getBearerToken',\n 'getSessionProfile',\n 'refreshCanonicalProfileId',\n 'getUserProfileLineage',\n 'getCustomerServiceToken',\n 'getPartnerIdentityToken',\n 'isSignedIn',\n 'requestProfilePairing',\n 'clearState',\n 'refreshEnrolledCredentials',\n 'beginCredentialEnrollment',\n 'completeCredentialEnrollment',\n 'beginCredentialVerification',\n 'completeCredentialVerification',\n 'getVerificationToken',\n 'clearVerificationSession',\n] as const;\n\nexport type Actions =\n | AuthenticationControllerGetStateAction\n | AuthenticationControllerMethodActions;\n\nexport type AuthenticationControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AuthenticationControllerState\n>;\n\nexport type AuthenticationControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AuthenticationControllerState\n >;\n\nexport type ProfileSignInInfo = {\n profileId: string;\n profileAliases: ProfileAlias[];\n profileIdChanged: boolean;\n};\n\nexport type AuthenticationControllerProfileSignInEvent = {\n type: `${typeof controllerName}:profileSignIn`;\n payload: [ProfileSignInInfo];\n};\n\nexport type Events =\n | AuthenticationControllerStateChangeEvent\n | AuthenticationControllerProfileSignInEvent;\n\n// Allowed Actions\ntype AllowedActions =\n | KeyringControllerGetStateAction\n | KeyringControllerWithKeyringV2UnsafeAction\n | SeedlessOnboardingControllerGetStateAction\n | SeedlessOnboardingControllerGetAccessTokenAction;\n\ntype AllowedEvents = KeyringControllerLockEvent | KeyringControllerUnlockEvent;\n\n// Messenger\nexport type AuthenticationControllerMessenger = Messenger<\n typeof controllerName,\n Actions | AllowedActions,\n Events | AllowedEvents\n>;\n\n/**\n * Controller that enables authentication for restricted endpoints.\n * Used for Backup & Sync, Notifications, and other services.\n */\nexport class AuthenticationController extends BaseController<\n typeof controllerName,\n AuthenticationControllerState,\n AuthenticationControllerMessenger\n> {\n readonly #metametrics: MetaMetricsAuth;\n\n readonly #auth: SRPInterface;\n\n readonly #trace: TraceCallback;\n\n readonly #config: ControllerConfig = {\n env: Env.PRD,\n isSocialPairingEnabled: () => false,\n };\n\n #isUnlocked = false;\n\n /**\n * Bumped whenever the authenticated session ends, so an in-flight MFA\n * ceremony started under the previous session cannot apply its result.\n */\n #authSessionEpoch = 0;\n\n /**\n * Verification sessions keyed by token audience. At most one session per\n * audience; only `DEFAULT_AUDIENCE` is used until audience-scoped tokens\n * exist.\n */\n readonly #verificationSessions = new Map<string, VerificationSession>();\n\n /**\n * Sequence number of the most recently started credentials refresh. Only\n * that refresh may write the cache, so a slower, earlier request cannot\n * overwrite a newer list (e.g. an explicit refresh landing after an\n * enrollment's refresh).\n */\n #credentialsRefreshSeq = 0;\n\n /**\n * Bumped by `requestProfilePairing` and `clearState` so an in-flight\n * `performSignIn` can't clear `needsProfilePairing` afterwards.\n */\n #profilePairingRequestEpoch = 0;\n\n /**\n * Sign-in currently running. Overlapping callers share it.\n */\n #ongoingSignIn?: Promise<string[]>;\n\n readonly #keyringController = {\n setupLockedStateSubscriptions: () => {\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n this.#isUnlocked = isUnlocked;\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n this.messenger.subscribe('KeyringController:lock', () => {\n this.#authSessionEpoch += 1;\n this.#isUnlocked = false;\n this.clearVerificationSession();\n });\n },\n };\n\n constructor({\n messenger,\n state,\n config,\n metametrics,\n trace,\n }: {\n messenger: AuthenticationControllerMessenger;\n state?: AuthenticationControllerState;\n config?: Partial<ControllerConfig>;\n /**\n * Not using the Messaging System as we\n * do not want to tie this strictly to extension\n */\n metametrics: MetaMetricsAuth;\n trace?: TraceCallback;\n }) {\n super({\n messenger,\n metadata,\n name: controllerName,\n state: { ...defaultState, ...state },\n });\n\n if (!metametrics) {\n throw new Error('`metametrics` field is required');\n }\n\n // `??` per key so an explicit `undefined` keeps the default rather than\n // clobbering it.\n this.#config = {\n env: config?.env ?? this.#config.env,\n isSocialPairingEnabled:\n config?.isSocialPairingEnabled ?? this.#config.isSocialPairingEnabled,\n };\n\n this.#metametrics = metametrics;\n this.#trace = trace ?? (((_request, fn) => fn?.()) as TraceCallback);\n\n this.#auth = new JwtBearerAuth(\n {\n env: this.#config.env,\n platform: metametrics.agent,\n type: AuthType.SRP,\n },\n {\n storage: {\n getLoginResponse: this.#getLoginResponseFromState.bind(this),\n setLoginResponse: this.#setLoginResponseToState.bind(this),\n },\n signing: {\n getIdentifier: this.#getPublicKey.bind(this),\n signMessage: this.#signMessage.bind(this),\n },\n getLoginTag: this.#getLoginTag.bind(this),\n getLoginIdentifierType: this.#getLoginIdentifierType.bind(this),\n metametrics: this.#metametrics,\n },\n );\n\n this.#keyringController.setupLockedStateSubscriptions();\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n async #getLoginResponseFromState(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n if (!this.state.srpSessionData?.[resolvedId]) {\n return null;\n }\n return this.state.srpSessionData[resolvedId];\n }\n\n async #setLoginResponseToState(\n loginResponse: LoginResponse,\n entropySourceId?: string,\n ) {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n const metaMetricsId = await this.#metametrics.getMetaMetricsId();\n this.update((state) => {\n state.isSignedIn = true;\n if (!state.srpSessionData) {\n state.srpSessionData = {};\n }\n // The API omits `paired_identifier_ids` when it fails to load them, so\n // keep the last known value rather than wiping it.\n const pairedIdentifierIds =\n loginResponse.profile.pairedIdentifierIds ??\n state.srpSessionData[resolvedId]?.profile.pairedIdentifierIds;\n state.srpSessionData[resolvedId] = {\n ...loginResponse,\n profile: {\n ...loginResponse.profile,\n metaMetricsId,\n ...(pairedIdentifierIds ? { pairedIdentifierIds } : {}),\n },\n };\n });\n }\n\n #assertIsUnlocked(methodName: string): void {\n if (!this.#isUnlocked) {\n throw new Error(`${methodName} - unable to proceed, wallet is locked`);\n }\n }\n\n #assertAuthSessionEpoch(epoch: number, methodName: string): void {\n if (!this.#isUnlocked || this.#authSessionEpoch !== epoch) {\n throw new Error(\n `${methodName} - unable to proceed, the authenticated session ended`,\n );\n }\n }\n\n /**\n * Reads the HD keyring entropy source IDs from KeyringController.\n *\n * @returns The HD keyring metadata IDs, primary first.\n */\n #getHdKeyringEntropySourceIds(): string[] {\n const keyringState = this.messenger.call('KeyringController:getState');\n return selectHdKeyringEntropySourceIds(keyringState);\n }\n\n /**\n * Resolves the primary SRP's entropy source ID from KeyringController rather\n * than the message-signing snap, so callers like `getBearerToken()` are not\n * blocked on snap boot.\n *\n * @returns The primary HD keyring metadata ID.\n * @throws If no HD keyring is available; callers must only resolve while\n * the wallet is unlocked.\n */\n #getPrimaryEntropySourceId(): string {\n const keyringState = this.messenger.call('KeyringController:getState');\n return getPrimaryHdKeyringEntropySourceId(keyringState);\n }\n\n /**\n * Resolves the SRP login tag for `raw_message`.\n *\n * - `primary` for the first HD entropy source\n * - `secondary` for any other entropy source\n *\n * @param entropySourceId - Entropy source for this login attempt.\n * @returns The login tag to append to the signed message.\n */\n async #getLoginTag(entropySourceId?: string): Promise<SrpLoginTag> {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const resolvedId = entropySourceId ?? primaryEntropySourceId;\n\n return resolvedId === primaryEntropySourceId ? 'primary' : 'secondary';\n }\n\n /**\n * Resolves metametrics `identifier_type` for `/srp/login`\n * (`SRP` | `GOOGLE` | `APPLE` | `TELEGRAM`).\n *\n * This is the auth method for the entropy source, independent of\n * {@link SrpLoginTag} (`primary` / `secondary`).\n *\n * SeedlessOnboarding currently exposes a single vault-level\n * `authConnection`, which always backs the primary entropy source. Non-\n * primary sources therefore return `SRP`. If social identities become\n * per-entropy later, resolve from that metadata instead of assuming\n * primary === social.\n *\n * Soft-fails to `SRP` when SeedlessOnboarding is not registered.\n *\n * @param entropySourceId - Entropy source for this login attempt.\n * @returns The login identifier type.\n */\n async #getLoginIdentifierType(\n entropySourceId?: string,\n ): Promise<LoginIdentifierType> {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const resolvedId = entropySourceId ?? primaryEntropySourceId;\n\n // Social vault authConnection is only associated with the primary source.\n if (resolvedId !== primaryEntropySourceId) {\n return 'SRP';\n }\n\n return this.#resolveSocialIdentifierType();\n }\n\n /**\n * Maps `SeedlessOnboardingController.state.authConnection` to a login\n * identifier type when a social vault is present.\n *\n * Returns `SRP` if there is no vault, the provider is unrecognized, or\n * SeedlessOnboardingController is unavailable on the messenger.\n *\n * @returns The social provider identifier type, or `SRP`.\n */\n #resolveSocialIdentifierType(): LoginIdentifierType {\n try {\n return this.#identifierTypeFromSeedlessState(\n this.messenger.call('SeedlessOnboardingController:getState'),\n );\n } catch {\n return 'SRP';\n }\n }\n\n /**\n * Maps SeedlessOnboarding vault + `authConnection` to a login identifier\n * type. Does not catch messenger errors — callers decide whether a\n * missing controller is `SRP` or a retryable failure.\n *\n * @param state - SeedlessOnboarding slice used for the mapping.\n * @param state.vault - Encrypted social vault, if present.\n * @param state.authConnection - Social provider id (`google` / `apple` /\n * `telegram`).\n * @returns The social provider identifier type, or `SRP`.\n */\n #identifierTypeFromSeedlessState(state: {\n vault?: string | null;\n authConnection?: string;\n }): LoginIdentifierType {\n if (state.vault === null || state.vault === undefined) {\n return 'SRP';\n }\n\n // Match provider strings from SeedlessOnboarding state rather than\n // importing AuthConnection — a value import would load that package\n // (and its heavy deps) whenever this controller is imported.\n switch (state.authConnection) {\n case 'google':\n return 'GOOGLE';\n case 'apple':\n return 'APPLE';\n case 'telegram':\n return 'TELEGRAM';\n default:\n return 'SRP';\n }\n }\n\n public async performSignIn(): Promise<string[]> {\n this.#assertIsUnlocked('performSignIn');\n if (this.#ongoingSignIn) {\n return await this.#ongoingSignIn;\n }\n\n const signIn = this.#runPerformSignIn().finally(() => {\n if (this.#ongoingSignIn === signIn) {\n this.#ongoingSignIn = undefined;\n }\n });\n\n this.#ongoingSignIn = signIn;\n return await signIn;\n }\n\n async #runPerformSignIn(): Promise<string[]> {\n const epochAtStart = this.#profilePairingRequestEpoch;\n const entropySourceIds = this.#getHdKeyringEntropySourceIds();\n const accessTokens: string[] = [];\n\n // We iterate sequentially in order to be sure that the first entry\n // is the primary SRP LoginResponse.\n for (const entropySourceId of entropySourceIds) {\n const accessToken = await this.#auth.getAccessToken(entropySourceId);\n accessTokens.push(accessToken);\n }\n\n if (entropySourceIds.length < 2) {\n // Single-SRP wallet: nothing to pair.\n this.#tryClearNeedsProfilePairing(epochAtStart);\n } else {\n // Pair failures must not break sign-in; the gate stays `true` for retry.\n try {\n await this.#doPair(accessTokens, epochAtStart);\n } catch {\n // noop\n }\n }\n\n try {\n await this.#trySocialPairing(accessTokens[0]);\n } catch {\n // noop\n }\n\n return accessTokens;\n }\n\n /**\n * Attaches a Google/Apple/Telegram social identifier to the primary SRP\n * profile via `POST /api/v2/profile/pair/identifier`.\n *\n * Runs only when `isSocialPairingEnabled()` is true and\n * `needsSocialPairing` is not `false` (`undefined` is treated as true).\n * No social provider (`authConnection` unset) → clear the flag (nothing\n * to pair). Social provider set but vault missing → skip (onboarding\n * still in flight). Google without email → skip (API requires it).\n * Telegram never sends `email` (the field holds a display name, not an\n * address). Missing social JWT → skip. 409 Conflict → clear the flag\n * (identifier already owned; retry is pointless). Other errors\n * propagate so the caller can swallow them and leave the flag set for\n * retry.\n *\n * @param primaryAccessToken - Primary SRP OIDC access token used as the\n * Bearer credential. The profile associated with this JWT becomes the\n * canonical owner of the social identifier.\n */\n async #trySocialPairing(primaryAccessToken?: string): Promise<void> {\n if (\n !this.#config.isSocialPairingEnabled() ||\n this.state.needsSocialPairing === false ||\n !primaryAccessToken\n ) {\n return;\n }\n\n let seedlessState: {\n vault?: string | null;\n authConnection?: string;\n socialLoginEmail?: string;\n };\n try {\n seedlessState = this.messenger.call(\n 'SeedlessOnboardingController:getState',\n );\n } catch {\n // Controller missing or getState failed — retry next performSignIn.\n return;\n }\n\n // `SRP` here does not describe the vault: it is the mapping's \"no social\n // identity\" result, returned when there is no seedless vault or the\n // provider is unrecognised. In both cases there is nothing to pair.\n const identifierType = this.#identifierTypeFromSeedlessState(seedlessState);\n if (identifierType === 'SRP') {\n const { vault, authConnection } = seedlessState;\n // Vault not written yet but a social provider is already known —\n // onboarding is still in flight. Do not clear; retry next sign-in.\n if ((vault === null || vault === undefined) && Boolean(authConnection)) {\n return;\n }\n this.#clearNeedsSocialPairing();\n return;\n }\n\n // Email policy follows the API contract: GOOGLE requires `email` (400\n // without it), APPLE accepts it optionally, TELEGRAM must not send it\n // (clients store a display name in `socialLoginEmail` for Telegram).\n // Clients request the `email` scope for Google, so a missing value is a\n // defensive case rather than an expected one; returning here leaves\n // `needsSocialPairing` set so the next sign-in retries instead of\n // sending a request that would fail.\n const { socialLoginEmail } = seedlessState;\n let email: string | undefined;\n if (identifierType === 'GOOGLE') {\n if (!socialLoginEmail) {\n return;\n }\n email = socialLoginEmail;\n } else if (identifierType === 'APPLE') {\n email = socialLoginEmail;\n }\n\n const socialJwt = await this.messenger.call(\n 'SeedlessOnboardingController:getAccessToken',\n );\n if (socialJwt === undefined || socialJwt === '') {\n return;\n }\n\n try {\n const pairedIdentifierIds = await this.#auth.pairSocialIdentifier(\n {\n identifierType,\n socialJwt,\n ...(email ? { email } : {}),\n },\n primaryAccessToken,\n );\n this.#setPrimaryPairedIdentifierIds(pairedIdentifierIds);\n this.#clearNeedsSocialPairing();\n } catch (error) {\n if (error instanceof PairConflictError) {\n this.#clearNeedsSocialPairing();\n return;\n }\n throw error;\n }\n }\n\n #clearNeedsSocialPairing(): void {\n if (this.state.needsSocialPairing !== false) {\n this.update((state) => {\n state.needsSocialPairing = false;\n });\n }\n }\n\n /**\n * Marks profile pairing as needed. Clients call this when the SRP set\n * changes (e.g. a new keyring was added) so the next auto-sign-in cycle\n * re-runs `performSignIn` and re-pairs.\n */\n public requestProfilePairing(): void {\n this.#profilePairingRequestEpoch += 1;\n if (!this.state.needsProfilePairing) {\n this.update((state) => {\n state.needsProfilePairing = true;\n });\n }\n }\n\n /**\n * Clears `needsProfilePairing` only if no `requestProfilePairing` call\n * landed since `epochAtStart` was captured. Prevents `performSignIn`\n * from silently overwriting a concurrent rearm.\n *\n * @param epochAtStart - Epoch value captured at the start of `performSignIn`.\n */\n #tryClearNeedsProfilePairing(epochAtStart: number): void {\n if (this.#profilePairingRequestEpoch !== epochAtStart) {\n return;\n }\n if (this.state.needsProfilePairing) {\n this.update((state) => {\n state.needsProfilePairing = false;\n });\n }\n }\n\n /**\n * Pairs all SRPs via `POST /profile/pair`, propagates the canonical\n * profile ID, clears `needsProfilePairing`, and emits\n * `AuthenticationController:profileSignIn` when the canonical changes or\n * new aliases are returned. Throws on failure.\n *\n * @param accessTokens - Per-SRP access tokens, primary first.\n * @param epochAtStart - Pairing-request epoch captured by the caller.\n * Used to skip the gate clear if `requestProfilePairing` ran while the\n * pair API call was in-flight.\n */\n async #doPair(accessTokens: string[], epochAtStart: number): Promise<void> {\n const previousCanonical = this.#getCanonicalProfileId();\n\n const profileAliases = await this.#pairSrpProfiles(accessTokens);\n const newCanonical = this.#getCanonicalProfileId();\n\n // If somehow we cannot compute the new canonical profile ID after pairing,\n // we just return now and do not update the `needsProfilePairing` flag.\n if (!newCanonical) {\n return;\n }\n\n this.#tryClearNeedsProfilePairing(epochAtStart);\n\n const profileIdChanged = previousCanonical !== newCanonical;\n const shouldEmitProfileSignInEvent =\n profileIdChanged || profileAliases.length > 0;\n\n if (shouldEmitProfileSignInEvent) {\n this.messenger.publish('AuthenticationController:profileSignIn', {\n profileId: newCanonical,\n profileAliases,\n profileIdChanged,\n });\n }\n }\n\n async #pairSrpProfiles(accessTokens: string[]): Promise<ProfileAlias[]> {\n if (accessTokens.length < 2) {\n return [];\n }\n const primaryAccessToken = accessTokens[0]; // Associated with primary SRP.\n const {\n profileAliases,\n profile: { canonicalProfileId, pairedIdentifierIds },\n } = await this.#auth.pairSrpProfiles(accessTokens, primaryAccessToken);\n this.#propagateCanonical(canonicalProfileId);\n this.#setPrimaryPairedIdentifierIds(pairedIdentifierIds);\n return profileAliases;\n }\n\n /**\n * Pair calls use the primary SRP's token, so their response only describes\n * the primary's profile: secondaries skipped by the server are not in it.\n *\n * @param pairedIdentifierIds - Identifiers returned by the pair call, if any.\n */\n #setPrimaryPairedIdentifierIds(\n pairedIdentifierIds: ProfileIdentifier[] | undefined,\n ): void {\n if (!pairedIdentifierIds) {\n return;\n }\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n this.update((state) => {\n const entry = state.srpSessionData?.[primaryEntropySourceId];\n if (entry?.profile) {\n entry.profile.pairedIdentifierIds = pairedIdentifierIds;\n }\n });\n }\n\n #propagateCanonical(canonicalProfileId: string): void {\n const { srpSessionData } = this.state;\n if (!srpSessionData) {\n return;\n }\n\n this.update((state) => {\n for (const entry of Object.values(state.srpSessionData ?? {})) {\n if (entry?.profile) {\n entry.profile.canonicalProfileId = canonicalProfileId;\n }\n }\n });\n }\n\n /**\n * Returns the canonical profile id from the primary SRP's cached session.\n * Returns `null` when no session exists yet for the primary SRP.\n *\n * Always reads from the primary SRP because the canonical is shared across\n * all paired SRPs after `#propagateCanonical`.\n *\n * @returns The canonical profile id, or `null` if unavailable.\n */\n #getCanonicalProfileId(): string | null {\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n return (\n this.state.srpSessionData?.[primaryEntropySourceId]?.profile\n ?.canonicalProfileId ?? null\n );\n }\n\n /**\n * Runs one MFA network step inside a trace span tagged with the caller's\n * operation and the credential type, recording the outcome and MFA error\n * code.\n *\n * @param name - Span name.\n * @param operation - Caller-supplied `reason.operation`.\n * @param credentialType - Credential type the step concerns.\n * @param fn - The network step.\n * @returns The step's result.\n */\n async #runMfaRequest<Result>(\n name: string,\n operation: string,\n credentialType: string,\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return await this.#trace(\n {\n name,\n tags: { operation, credentialType },\n },\n async (context) => {\n try {\n const result = await fn();\n this.#setTraceAttribute(context, 'outcome', 'success');\n return result;\n } catch (error) {\n this.#setTraceAttribute(context, 'outcome', 'error');\n const mfaCode = getMfaErrorCode(error);\n if (mfaCode) {\n this.#setTraceAttribute(context, 'mfaErrorCode', mfaCode);\n if (mfaCode === 'authentication_required' && this.#isUnlocked) {\n this.#invalidateSrpSession(this.#getPrimaryEntropySourceId());\n // A verification session must not outlive a rejected base\n // session.\n this.clearVerificationSession();\n }\n }\n throw error;\n }\n },\n );\n }\n\n /**\n * `TraceContext` is opaque in `@metamask/controller-utils`; both clients\n * hand back a Sentry span, so attributes are set via duck typing and a\n * non-Sentry context is a silent no-op.\n *\n * @param context - Span handed to the trace callback, if any.\n * @param key - Attribute name.\n * @param value - Attribute value.\n */\n #setTraceAttribute(\n context: TraceContext | undefined,\n key: string,\n value: string,\n ): void {\n const traceSpan = context as\n | { setAttribute?: (attribute: string, data: string) => void }\n | undefined;\n traceSpan?.setAttribute?.(key, value);\n }\n\n /**\n * Refreshes credentials enrolled on the canonical profile.\n *\n * @returns The current supported credentials.\n */\n public async refreshEnrolledCredentials(): Promise<EnrolledCredential[]> {\n this.#assertIsUnlocked('refreshEnrolledCredentials');\n const sessionEpoch = this.#authSessionEpoch;\n this.#credentialsRefreshSeq += 1;\n const refreshSeq = this.#credentialsRefreshSeq;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const credentials = await this.#runMfaRequest(\n 'MFA Credentials Refresh',\n 'credentials.refresh',\n 'all',\n async () => await this.#auth.getMfaCredentials(primaryEntropySourceId),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'refreshEnrolledCredentials');\n\n // A newer refresh started while this one was in flight; its result is at\n // least as fresh, so defer to it rather than overwrite with older data.\n if (refreshSeq !== this.#credentialsRefreshSeq) {\n return this.#getEnrolledCredentials();\n }\n\n // Skip the write when nothing changed so subscribers are not woken up by\n // a fresh-but-identical array.\n if (\n JSON.stringify(credentials) !==\n JSON.stringify(this.#getEnrolledCredentials())\n ) {\n this.update((state) => {\n state.enrolledCredentials = credentials;\n });\n }\n return credentials;\n }\n\n /**\n * Begins enrollment of a passkey or email OTP credential.\n *\n * @param request - Credential, optional email address, and trace reason.\n * @returns A challenge for the client-owned ceremony.\n */\n public async beginCredentialEnrollment(\n request: BeginEnrollmentRequest,\n ): Promise<EnrollmentChallenge> {\n this.#assertIsUnlocked('beginCredentialEnrollment');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, BeginEnrollmentRequestStruct);\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n // The server requires AAL2 to add a credential once one that proves AAL2\n // exists: send the verification token only while a recent enough session\n // is live and let the server decide. An older session stays open for\n // other consumers.\n const accessToken = this.getVerificationToken({\n maxSessionAgeMs: request.maxSessionAgeMs ?? ENROLLMENT_MAX_SESSION_AGE_MS,\n })?.accessToken;\n const challenge = await this.#runMfaRequest(\n 'MFA Enroll Begin',\n request.reason.operation,\n request.type,\n async () =>\n await this.#auth.beginMfaEnrollment(request.type, {\n email: request.email,\n entropySourceId: primaryEntropySourceId,\n accessToken,\n }),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'beginCredentialEnrollment');\n return challenge;\n }\n\n /**\n * Completes credential enrollment and refreshes the credential cache.\n *\n * The server returns an assertion for the new credential, which opens a\n * verification session like `completeCredentialVerification`, replacing\n * any earlier one. If that exchange fails, the earlier session is kept:\n * the credential is enrolled either way.\n *\n * A cache-refresh failure does not undo successful enrollment. Email\n * enrollment invalidates the primary SRP session *after* refresh so the\n * credentials call can reuse the still-valid access token; the next token\n * fetch then includes the newly verified email claim. That invalidation\n * happens even if the session ends mid-request: the enrollment succeeded\n * on the server, so a token cached across a lock must not be reused\n * without the new claim.\n *\n * @param request - Flow identifier, platform or email proof, and trace reason.\n * @returns The refreshed credentials, or the existing cache if refresh fails.\n */\n public async completeCredentialEnrollment(\n request: CompleteEnrollmentRequest,\n ): Promise<EnrolledCredential[]> {\n this.#assertIsUnlocked('completeCredentialEnrollment');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, CompleteEnrollmentRequestStruct);\n const { type } = request.proof;\n const { operation } = request.reason;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const assertion = await this.#runMfaRequest(\n 'MFA Enroll Complete',\n operation,\n type,\n async () =>\n await this.#auth.completeMfaEnrollment(\n request.flowId,\n request.proof,\n primaryEntropySourceId,\n ),\n );\n\n try {\n await this.#openVerificationSessionFromAssertion(assertion, {\n operation,\n type,\n sessionEpoch,\n methodName: 'completeCredentialEnrollment',\n });\n } catch {\n // Callers see no session and verify the credential instead.\n }\n\n // Also catches a session that ended during the enrollment request; the\n // exchange above then ran for nothing, and its session was never opened.\n try {\n this.#assertAuthSessionEpoch(\n sessionEpoch,\n 'completeCredentialEnrollment',\n );\n } catch (error) {\n if (type === 'email_otp') {\n this.#invalidateSrpSession(primaryEntropySourceId);\n }\n throw error;\n }\n\n try {\n return await this.refreshEnrolledCredentials();\n } catch {\n return this.#getEnrolledCredentials();\n } finally {\n if (type === 'email_otp') {\n this.#invalidateSrpSession(primaryEntropySourceId);\n }\n }\n }\n\n /**\n * Begins verification with an enrolled credential.\n *\n * @param request - Credential type and trace reason.\n * @returns A challenge for the client-owned ceremony.\n */\n public async beginCredentialVerification(\n request: BeginVerificationRequest,\n ): Promise<VerificationChallenge> {\n this.#assertIsUnlocked('beginCredentialVerification');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, BeginVerificationRequestStruct);\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const challenge = await this.#runMfaRequest(\n 'MFA Verification Begin',\n request.reason.operation,\n request.type,\n async () =>\n await this.#auth.beginMfaVerification(\n request.type,\n primaryEntropySourceId,\n ),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, 'beginCredentialVerification');\n return challenge;\n }\n\n /**\n * Completes verification and opens a short-lived verification session.\n *\n * The assertion returned by the MFA service is exchanged at Hydra for an\n * access token. Its assurance level is not checked: the services receiving\n * the token enforce their own requirements. The token itself never enters\n * controller state.\n *\n * @param request - Flow identifier, platform or email proof, and trace reason.\n * @returns The verification token.\n */\n public async completeCredentialVerification(\n request: CompleteVerificationRequest,\n ): Promise<VerificationToken> {\n this.#assertIsUnlocked('completeCredentialVerification');\n const sessionEpoch = this.#authSessionEpoch;\n assertValidMfaRequest(request, CompleteVerificationRequestStruct);\n const { type } = request.proof;\n const { operation } = request.reason;\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n const assertion = await this.#runMfaRequest(\n 'MFA Verification Complete',\n operation,\n type,\n async () =>\n await this.#auth.completeMfaVerification(\n request.flowId,\n request.proof,\n primaryEntropySourceId,\n ),\n );\n this.#assertAuthSessionEpoch(\n sessionEpoch,\n 'completeCredentialVerification',\n );\n return await this.#openVerificationSessionFromAssertion(assertion, {\n operation,\n type,\n sessionEpoch,\n methodName: 'completeCredentialVerification',\n });\n }\n\n /**\n * Exchanges an MFA assertion at Hydra and opens the verification session\n * with the resulting token.\n *\n * @param assertion - Assertion returned by a verify or enroll completion.\n * @param context - Trace tags and the session the call started in.\n * @param context.operation - Feature operation, for tracing.\n * @param context.type - Credential type the assertion proves, for tracing.\n * @param context.sessionEpoch - Authenticated session the call started in.\n * @param context.methodName - Public method name, for error messages.\n * @returns The verification token.\n */\n async #openVerificationSessionFromAssertion(\n assertion: MfaVerificationAssertion,\n {\n operation,\n type,\n sessionEpoch,\n methodName,\n }: {\n operation: string;\n type: MfaCredentialType;\n sessionEpoch: number;\n methodName: string;\n },\n ): Promise<VerificationToken> {\n const accessToken = await this.#runMfaRequest(\n 'MFA Token Exchange',\n operation,\n type,\n async () => await this.#auth.exchangeMfaAssertion(assertion.token),\n );\n this.#assertAuthSessionEpoch(sessionEpoch, methodName);\n\n let decodedClaims: unknown;\n try {\n decodedClaims = decodeJwtPayload(accessToken.accessToken);\n } catch (error) {\n throw new VerificationTokenInvalidError(toErrorMessage(error));\n }\n const claims = parseVerificationTokenClaims(decodedClaims);\n if (!Number.isFinite(accessToken.expiresIn) || accessToken.expiresIn <= 0) {\n throw new VerificationTokenInvalidError(\n 'Verification token has no remaining lifetime',\n );\n }\n\n const token: VerificationToken = { ...accessToken, claims };\n this.#openVerificationSession(token);\n return token;\n }\n\n /**\n * Returns the active verification token when it meets the requested\n * freshness.\n *\n * Low-level: features should go through the client MFA kit\n * (`verifyOrEnroll`), which reuses a matching session without showing any\n * screen and checks which method proved it. Read the token directly only\n * from code that cannot show UI, and treat `null` as \"let the UI layer\n * ask\".\n *\n * @param request - Optional maximum session age in milliseconds, measured\n * from when the token was obtained. Zero always requires a new ceremony.\n * @returns A live verification token, or null when no reusable session\n * exists.\n */\n public getVerificationToken(\n request: GetVerificationTokenRequest = {},\n ): VerificationToken | null {\n this.#assertIsUnlocked('getVerificationToken');\n assertValidMfaRequest(request, GetVerificationTokenRequestStruct);\n const session = this.#verificationSessions.get(DEFAULT_AUDIENCE);\n if (!session) {\n return null;\n }\n const now = Date.now();\n if (now >= session.expiresAt) {\n // The hard-expiry timer has not fired yet (e.g. a suspended tab).\n this.#dropVerificationSession(DEFAULT_AUDIENCE);\n return null;\n }\n // `>=` so a zero max age always forces a fresh ceremony. Not cleared: the\n // max age is this caller's requirement; others may still use the session.\n const maxAge = request.maxSessionAgeMs;\n if (maxAge !== undefined && now - session.token.obtainedAt >= maxAge) {\n return null;\n }\n return session.token;\n }\n\n /**\n * Opens the verification session for the lifetime the server gave the token.\n * It is measured from `expires_in` on the local clock rather than read from\n * `exp`, so a skewed device clock cannot shorten or extend it.\n *\n * @param token - The freshly exchanged verification token.\n */\n #openVerificationSession(token: VerificationToken): void {\n const audience = DEFAULT_AUDIENCE;\n this.#dropVerificationSession(audience);\n const expiresAt = token.obtainedAt + token.expiresIn * 1000;\n const timer = setTimeout(\n () => this.#dropVerificationSession(audience),\n Math.max(0, expiresAt - Date.now()),\n );\n // Never keep a Node process alive for the expiry timer (tests, tooling).\n (timer as { unref?: () => void }).unref?.();\n this.#verificationSessions.set(audience, { token, expiresAt, timer });\n }\n\n /**\n * Drops one audience's verification session and its expiration timer.\n *\n * @param audience - Audience whose session to drop.\n */\n #dropVerificationSession(audience: string): void {\n const session = this.#verificationSessions.get(audience);\n if (session) {\n clearTimeout(session.timer);\n this.#verificationSessions.delete(audience);\n }\n }\n\n /**\n * Clears every in-memory verification session and its expiration timer.\n */\n public clearVerificationSession(): void {\n for (const { timer } of this.#verificationSessions.values()) {\n clearTimeout(timer);\n }\n this.#verificationSessions.clear();\n }\n\n /**\n * Drops the cached credential list. Callers that wipe profile state must go\n * through this so subscribers never keep credentials belonging to a profile\n * that is no longer active.\n */\n #clearEnrolledCredentials(): void {\n if (this.#getEnrolledCredentials().length === 0) {\n return;\n }\n this.update((state) => {\n state.enrolledCredentials = [];\n });\n }\n\n #getEnrolledCredentials(): EnrolledCredential[] {\n return this.state.enrolledCredentials ?? [];\n }\n\n public performSignOut(): void {\n this.#authSessionEpoch += 1;\n this.clearVerificationSession();\n this.#clearEnrolledCredentials();\n this.update((state) => {\n state.isSignedIn = false;\n state.srpSessionData = undefined;\n });\n }\n\n /**\n * Resets the controller to `defaultState`. Clients call this on wallet reset\n * so the next wallet starts unsigned with both pairing gates re-armed.\n */\n public clearState(): void {\n this.#profilePairingRequestEpoch += 1;\n this.#authSessionEpoch += 1;\n this.clearVerificationSession();\n this.#clearEnrolledCredentials();\n this.update(() => ({ ...defaultState }));\n }\n\n /**\n * Returns a bearer token for the specified SRP, logging in if needed.\n *\n * When called without `entropySourceId`, returns the primary (first) SRP's\n * access token, which is effectively the canonical\n * profile's token that can be used by alias-aware consumers for cross-SRP\n * operations.\n *\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The OIDC access token.\n */\n public async getBearerToken(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('getBearerToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getAccessToken(resolvedId);\n }\n\n /**\n * Returns the cached session profile, logging in if no session exists.\n *\n * The returned `canonicalProfileId` reflects the value from the most recent\n * login or pairing. In the rare event where a canonical changed because of\n * a pairing that happened on another device, the cached value may be stale\n * until the next login. For guaranteed freshness, call\n * `refreshCanonicalProfileId()` before reading `canonicalProfileId`.\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns profile for the session.\n */\n public async getSessionProfile(\n entropySourceId?: string,\n ): Promise<UserProfile> {\n this.#assertIsUnlocked('getSessionProfile');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getUserProfile(resolvedId);\n }\n\n /**\n * Forces a fresh retrieval of the canonical profile ID from the server\n * and propagates it to all cached SRP sessions.\n *\n * This method invalidates the primary SRP's cached session and forces a\n * re-login. Use it before operations that require a guaranteed-fresh\n * canonical (e.g. storage key derivation for Accounts ADR 0005). For\n * best-effort reads, use\n * `getSessionProfile().canonicalProfileId` instead.\n *\n * Only the primary SRP is re-logged-in regardless of how many SRPs exist —\n * the server returns the current canonical for the entire pairing group\n * from any single SRP login.\n *\n * @returns The refreshed canonical profile ID.\n */\n public async refreshCanonicalProfileId(): Promise<string> {\n this.#assertIsUnlocked('refreshCanonicalProfileId');\n\n const primaryEntropySourceId = this.#getPrimaryEntropySourceId();\n this.#invalidateSrpSession(primaryEntropySourceId);\n await this.#auth.getAccessToken(primaryEntropySourceId);\n\n const canonical = this.#getCanonicalProfileId();\n if (!canonical) {\n throw new Error(\n 'refreshCanonicalProfileId - Unable to resolve canonical profile ID',\n );\n }\n\n this.#propagateCanonical(canonical);\n return canonical;\n }\n\n #invalidateSrpSession(entropySourceId: string): void {\n this.update((state) => {\n const entry = state.srpSessionData?.[entropySourceId];\n if (entry?.profile) {\n // Setting canonicalProfileId to '' forces a re-fetch on the next\n // #getAuthSession call. The falsy check (!auth.profile.canonicalProfileId)\n // treats '' the same as undefined/null — all signal an invalid session.\n entry.profile.canonicalProfileId = '';\n }\n });\n }\n\n public async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n this.#assertIsUnlocked('getUserProfileLineage');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getUserProfileLineage(resolvedId);\n }\n\n /**\n * Returns a Customer Service specific access token for the specified SRP,\n * logging in if needed.\n *\n * Exchanges the OIDC access token for a short-lived token scoped to the\n * customer-service audience. Customer Service tooling consumes this token to\n * identify and authenticate the user.\n *\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The customer-service access token.\n */\n public async getCustomerServiceToken(\n entropySourceId?: string,\n ): Promise<string> {\n this.#assertIsUnlocked('getCustomerServiceToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getCustomerServiceToken(resolvedId);\n }\n\n /**\n * Mints a partner identity token for the specified SRP, logging in if needed.\n *\n * Calls `POST /api/v2/oidc/token` with the Hydra login bearer and returns\n * the minted `access_token`. Email on live tokens is under JWT `ext`.\n * HTTP 422 throws `EmailRequiredError` when this profile has no\n * verified email.\n *\n * @param claims - Claim names to embed. Only `email` is supported.\n * @param audience - Partner audience (`kyc` or `iron`).\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The partner identity access token.\n */\n public async getPartnerIdentityToken(\n claims: OidcTokenClaims,\n audience: OidcTokenAudience,\n entropySourceId?: string,\n ): Promise<string> {\n this.#assertIsUnlocked('getPartnerIdentityToken');\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return await this.#auth.getPartnerIdentityToken(\n claims,\n audience,\n resolvedId,\n );\n }\n\n public isSignedIn(): boolean {\n return this.state.isSignedIn;\n }\n\n /**\n * Reads the BIP-39 seed for an HD entropy source from KeyringController.\n *\n * @param entropySourceId - Entropy source ID. Defaults to the primary HD\n * keyring.\n * @returns The HD keyring seed.\n */\n async #getHdKeyringSeed(entropySourceId?: string): Promise<Uint8Array> {\n const resolvedId = entropySourceId ?? this.#getPrimaryEntropySourceId();\n return getHdKeyringSeed(this.messenger, resolvedId);\n }\n\n /**\n * Returns the message-signing public key via native SIP-6 derivation\n * (same key as `@metamask/message-signing-snap` with empty salt).\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns The public key hex.\n */\n async #getPublicKey(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('#getPublicKey');\n const seed = await this.#getHdKeyringSeed(entropySourceId);\n return getMessageSigningPublicKey(seed);\n }\n\n #_signMessageCache: Record<string, string> = {};\n\n /**\n * Builds a cache key scoped to a specific entropy source, so each SRP's\n * signature stays isolated (same pattern as `UserStorageController`).\n *\n * When `entropySourceId` is omitted (primary SRP), it is resolved to the\n * primary HD keyring's metadata ID rather than a stable literal. Because that\n * ID is randomly regenerated whenever the vault is recreated (e.g. on\n * restore), the cached entry is naturally invalidated across vaults — a\n * different SRP can never inherit the previous primary's cached signature.\n *\n * @param message - The tagged message used for signing.\n * @param entropySourceId - The entropy source ID. Omit for the primary SRP.\n * @returns The scoped cache key.\n */\n #scopedCacheKey(\n message: `metamask:${string}`,\n entropySourceId?: string,\n ): string {\n return `${entropySourceId ?? this.#getPrimaryEntropySourceId()}:${message}`;\n }\n\n /**\n * Signs a `metamask:…` message with the native SIP-6 message-signing key.\n *\n * @param message - A specific tagged message to sign.\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns Compact secp256k1 signature hex.\n */\n async #signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n assertMessageStartsWithMetamask(message);\n this.#assertIsUnlocked('#signMessage');\n\n const cacheKey = this.#scopedCacheKey(message, entropySourceId);\n if (this.#_signMessageCache[cacheKey]) {\n return this.#_signMessageCache[cacheKey];\n }\n\n const seed = await this.#getHdKeyringSeed(entropySourceId);\n const result = await signMessageWithMessageSigningKey(message, seed);\n\n this.#_signMessageCache[cacheKey] = result;\n\n return result;\n }\n}\n"]}
|
|
@@ -84,11 +84,7 @@ export async function syncContactsWithUserStorage(config, options) {
|
|
|
84
84
|
contactsToDeleteLocally.push(remoteContact);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
else if (
|
|
88
|
-
// SCENARIO 2: New contact from remote - import to local
|
|
89
|
-
contactsToAddOrUpdateLocally.push(remoteContact);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
87
|
+
else if (localContact) {
|
|
92
88
|
// SCENARIO 4 & 6: Contact exists on both sides - check for conflicts
|
|
93
89
|
const hasContentDifference = localContact.name !== remoteContact.name ||
|
|
94
90
|
localContact.memo !== remoteContact.memo;
|
|
@@ -107,6 +103,10 @@ export async function syncContactsWithUserStorage(config, options) {
|
|
|
107
103
|
}
|
|
108
104
|
// Else: content is identical, no action needed
|
|
109
105
|
}
|
|
106
|
+
else {
|
|
107
|
+
// SCENARIO 2: New contact from remote - import to local
|
|
108
|
+
contactsToAddOrUpdateLocally.push(remoteContact);
|
|
109
|
+
}
|
|
110
110
|
}
|
|
111
111
|
// SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts
|
|
112
112
|
for (const localContact of localVisibleContacts) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller-integration.js","sourceRoot":"","sources":["../../../../src/controllers/user-storage/contact-syncing/controller-integration.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,EACL,qCAAqC,EACrC,qCAAqC,GACtC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAW1D;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,OAAyB;IACjD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAyC,EACzC,OAA8B;IAE9B,MAAM,EAAE,YAAY,EAAE,gCAAgC,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1E,MAAM,EACJ,+BAA+B,EAC/B,gBAAgB,EAChB,gBAAgB,GACjB,GAAG,MAAM,CAAC;IAEX,0CAA0C;IAC1C,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,OAAO;IACT,CAAC;IAED,sGAAsG;IACtG,8FAA8F;IAC9F,+EAA+E;IAE/E,iFAAiF;IACjF,MAAM,oBAAoB,GACxB,YAAY,EAAE;SACX,IAAI,CAAC,4BAA4B,CAAC;SAClC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC;SAC3D,MAAM,CACL,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CACxE,IAAI,EAAE,CAAC;IAEZ,4CAA4C;IAC5C,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAExD,wFAAwF;IACxF,MAAM,mBAAmB,GACvB,cAAc,EAAE,MAAM,CACpB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CACxE,IAAI,EAAE,CAAC;IAEV,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC7B,IAAI,CAAC;YACH,iDAAiD;YACjD,MAAM,gCAAgC,EAAE,CAAC,6BAA6B,CACpE,IAAI,CACL,CAAC;YAEF,oCAAoC;YACpC,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA4B,CAAC;YAC7D,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAgC,CAAC;YAElE,oBAAoB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBACvC,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBACtC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;YAEH,mBAAmB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBACtC,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBACtC,iBAAiB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;YAEH,iDAAiD;YACjD,MAAM,4BAA4B,GAA2B,EAAE,CAAC;YAChE,MAAM,uBAAuB,GAA2B,EAAE,CAAC;YAC3D,MAAM,wBAAwB,GAAuB,EAAE,CAAC;YAExD,sFAAsF;YACtF,KAAK,MAAM,aAAa,IAAI,mBAAmB,EAAE,CAAC;gBAChD,MAAM,GAAG,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;gBAC5C,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAE/C,gEAAgE;gBAChE,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC;oBAC5B,oFAAoF;oBACpF,IAAI,YAAY,EAAE,CAAC;wBACjB,uBAAuB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;oBAC9C,CAAC;gBACH,CAAC;qBAAM,IAAI,CAAC,YAAY,EAAE,CAAC;oBACzB,wDAAwD;oBACxD,4BAA4B,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACnD,CAAC;qBAAM,CAAC;oBACN,qEAAqE;oBACrE,MAAM,oBAAoB,GACxB,YAAY,CAAC,IAAI,KAAK,aAAa,CAAC,IAAI;wBACxC,YAAY,CAAC,IAAI,KAAK,aAAa,CAAC,IAAI,CAAC;oBAE3C,IAAI,oBAAoB,EAAE,CAAC;wBACzB,sDAAsD;wBACtD,MAAM,cAAc,GAAG,YAAY,CAAC,aAAa,IAAI,CAAC,CAAC;wBACvD,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,IAAI,CAAC,CAAC;wBAEzD,IAAI,cAAc,IAAI,eAAe,EAAE,CAAC;4BACtC,mDAAmD;4BACnD,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;wBAC9C,CAAC;6BAAM,CAAC;4BACN,uCAAuC;4BACvC,4BAA4B,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;wBACnD,CAAC;oBACH,CAAC;oBAED,+CAA+C;gBACjD,CAAC;YACH,CAAC;YAED,sGAAsG;YACtG,KAAK,MAAM,YAAY,IAAI,oBAAoB,EAAE,CAAC;gBAChD,MAAM,GAAG,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;gBAC3C,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAEjD,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,kDAAkD;oBAClD,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC;YAED,wBAAwB;YACxB,wFAAwF;YACxF,+EAA+E;YAC/E,iDAAiD;YACjD,KAAK,MAAM,OAAO,IAAI,uBAAuB,EAAE,CAAC;gBAC9C,YAAY,EAAE,CAAC,IAAI,CACjB,8BAA8B,EAC9B,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,CAChB,CAAC;gBAEF,IAAI,gBAAgB,EAAE,CAAC;oBACrB,gBAAgB,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC;YAED,gCAAgC;YAChC,wFAAwF;YACxF,+EAA+E;YAC/E,iDAAiD;YACjD,KAAK,MAAM,OAAO,IAAI,4BAA4B,EAAE,CAAC;gBACnD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;oBACvB,YAAY,EAAE,CAAC,IAAI,CACjB,2BAA2B,EAC3B,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,IAAI,IAAI,EAAE,EAClB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,IAAI,IAAI,EAAE,EAClB,OAAO,CAAC,WAAW,CACpB,CAAC;oBAEF,IAAI,gBAAgB,EAAE,CAAC;wBACrB,gBAAgB,EAAE,CAAC;oBACrB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,kCAAkC;YAClC,IAAI,wBAAwB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxC,MAAM,qBAAqB,GAAyC,EAAE,CAAC;gBACvE,KAAK,MAAM,YAAY,IAAI,wBAAwB,EAAE,CAAC;oBACpD,MAAM,GAAG,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;oBAC3C,qBAAqB,CAAC,GAAG,CAAC,GAAG;wBAC3B,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,qDAAqD;wBACpF,GAAG,YAAY,EAAE,8BAA8B;wBAC/C,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,kBAAkB;qBAC9C,CAAC;gBACJ,CAAC;gBACD,0CAA0C;gBAC1C,MAAM,yBAAyB,CAC7B,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,EACpC,OAAO,CACR,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,+BAA+B,EAAE,CAAC;gBACpC,+BAA+B,CAAC,8BAA8B,EAAE;oBAC9D,KAAK;iBACN,CAAC,CAAC;gBAEH,iDAAiD;gBACjD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,gCAAgC,EAAE,CAAC,6BAA6B,CACpE,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,KAAK,EAAE,CAAC;QACV,mDAAmD;QACnD,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;QAClD,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;QAEvD,MAAM,KAAK,CACT;YACE,IAAI,EAAE,SAAS,CAAC,eAAe;YAC/B,IAAI,EAAE;gBACJ,iBAAiB,EAAE,oBAAoB,CAAC,MAAM;gBAC9C,kBAAkB,EAAE,0BAA0B,CAAC,MAAM;gBACrD,WAAW,EACT,0BAA0B,CAAC,MAAM,KAAK,CAAC;oBACvC,oBAAoB,CAAC,MAAM,GAAG,CAAC;gBACjC,eAAe,EACb,oBAAoB,CAAC,MAAM,KAAK,CAAC;oBACjC,0BAA0B,CAAC,MAAM,GAAG,CAAC;gBACvC,aAAa,EACX,oBAAoB,CAAC,MAAM,GAAG,CAAC;oBAC/B,0BAA0B,CAAC,MAAM,GAAG,CAAC;gBACvC,aAAa,EACX,oBAAoB,CAAC,MAAM,GAAG,CAAC;oBAC/B,0BAA0B,CAAC,MAAM,GAAG,CAAC;gBACvC,gBAAgB,EACd,oBAAoB,CAAC,MAAM,GAAG,0BAA0B,CAAC,MAAM;aAClE;SACF,EACD,WAAW,CACZ,CAAC;QAEF,OAAO;IACT,CAAC;IAED,MAAM,WAAW,EAAE,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,iBAAiB,CAC9B,OAA8B;IAE9B,MAAM,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,uBAAuB,GAC3B,MAAM,gCAAgC,EAAE,CAAC,kCAAkC,CACzE,0BAA0B,CAAC,WAAW,CACvC,CAAC;QAEJ,IAAI,CAAC,uBAAuB,IAAI,uBAAuB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,qFAAqF;QACrF,MAAM,oBAAoB,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE;YACvE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAA4B,CAAC;YACjE,OAAO,qCAAqC,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,yBAAyB,CACtC,QAA4B,EAC5B,OAA8B;IAE9B,MAAM,EAAE,gCAAgC,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE5D,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;QAC9B,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QAED,6FAA6F;QAC7F,MAAM,cAAc,GAAuB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAClE,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,YAAY,GAAG,qCAAqC,CAAC,OAAO,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,MAAM,gCAAgC,EAAE,CAAC,sBAAsB,CAC7D,0BAA0B,CAAC,WAAW,EACtC,cAAc,CACf,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,KAAK;QACV,CAAC,CAAC,MAAM,KAAK,CACT;YACE,IAAI,EAAE,SAAS,CAAC,oBAAoB;YACpC,IAAI,EAAE;gBACJ,YAAY,EAAE,QAAQ,CAAC,MAAM;gBAC7B,iCAAiC;gBACjC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACvC,UAAU,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBACxD,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM;aAC7D;SACF,EACD,YAAY,CACb;QACH,CAAC,CAAC,MAAM,YAAY,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAyB,EACzB,OAA8B;IAE9B,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE1B,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;QAC/B,IACE,CAAC,wBAAwB,CAAC,OAAO,CAAC;YAClC,CAAC,OAAO,CAAC,OAAO;YAChB,CAAC,OAAO,CAAC,OAAO;YAChB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EACrB,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;QAErD,yCAAyC;QACzC,MAAM,YAAY,GAAG;YACnB,GAAG,OAAO;YACV,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;QAE1B,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,YAAY,GAAG,qCAAqC,CAAC,YAAY,CAAC,CAAC;QAEzE,4CAA4C;QAC5C,MAAM,gCAAgC,EAAE,CAAC,iBAAiB,CACxD,GAAG,0BAA0B,CAAC,WAAW,IAAI,GAAG,EAAE,EAClD,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAC7B,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,MAAM,KAAK,CAChB;YACE,IAAI,EAAE,SAAS,CAAC,uBAAuB;YACvC,IAAI,EAAE;gBACJ,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,yBAAyB;gBACzB,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;gBAC5C,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;gBACtC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,iBAAiB;aAC5D;SACF,EACD,aAAa,CACd,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,aAAa,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAyB,EACzB,OAA8B;IAE9B,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;QAC/B,IACE,CAAC,wBAAwB,CAAC,OAAO,CAAC;YAClC,CAAC,OAAO,CAAC,OAAO;YAChB,CAAC,OAAO,CAAC,OAAO;YAChB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EACrB,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;QACrD,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAEtC,IAAI,CAAC;YACH,wCAAwC;YACxC,MAAM,mBAAmB,GACvB,MAAM,gCAAgC,EAAE,CAAC,iBAAiB,CACxD,GAAG,0BAA0B,CAAC,WAAW,IAAI,GAAG,EAAE,CACnD,CAAC;YAEJ,IAAI,mBAAmB,EAAE,CAAC;gBACxB,uCAAuC;gBACvC,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CACrC,mBAAmB,CACO,CAAC;gBAC7B,MAAM,eAAe,GACnB,qCAAqC,CAAC,oBAAoB,CAAC,CAAC;gBAE9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACvB,MAAM,cAAc,GAAG;oBACrB,GAAG,eAAe;oBAClB,SAAS,EAAE,GAAG;oBACd,aAAa,EAAE,GAAG;iBACK,CAAC;gBAE1B,MAAM,mBAAmB,GACvB,qCAAqC,CAAC,cAAc,CAAC,CAAC;gBAExD,2CAA2C;gBAC3C,MAAM,gCAAgC,EAAE,CAAC,iBAAiB,CACxD,GAAG,0BAA0B,CAAC,WAAW,IAAI,GAAG,EAAE,EAClD,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,CACpC,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,OAAO,CAAC,IAAI,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,KAAK;QACV,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,uBAAuB,EAAE,EAAE,aAAa,CAAC;QACzE,CAAC,CAAC,MAAM,aAAa,EAAE,CAAC;AAC5B,CAAC","sourcesContent":["import type { AddressBookEntry } from '@metamask/address-book-controller';\n\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema.js';\nimport { TraceName } from '../constants.js';\nimport { canPerformContactSyncing } from './sync-utils.js';\nimport type { ContactSyncingOptions } from './types.js';\nimport type { UserStorageContactEntry } from './types.js';\nimport type { SyncAddressBookEntry } from './utils.js';\nimport {\n mapAddressBookEntryToUserStorageEntry,\n mapUserStorageEntryToAddressBookEntry,\n} from './utils.js';\nimport { isContactBridgedFromAccounts } from './utils.js';\n\nexport type SyncContactsWithUserStorageConfig = {\n onContactSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n onContactUpdated?: () => void;\n onContactDeleted?: () => void;\n};\n\n/**\n * Creates a unique key for a contact based on chainId and address\n *\n * @param contact - The contact to create a key for\n * @returns A unique string key\n */\nfunction createContactKey(contact: AddressBookEntry): string {\n if (!contact.address) {\n throw new Error('Contact address is required to create storage key');\n }\n return `${contact.chainId}_${contact.address.toLowerCase()}`;\n}\n\n/**\n * Syncs contacts between local storage and user storage (remote).\n *\n * Handles the following syncing scenarios:\n * 1. First Sync: When local contacts exist but there are no remote contacts, uploads all local contacts.\n * 2. New Device Sync: Downloads remote contacts that don't exist locally (empty local address book).\n * 3. Simple Merge: Ensures both sides (local & remote) have all contacts.\n * 4. Contact Naming Conflicts: When same contact has different names, uses most recent by timestamp.\n * 5. Local Updates: When a contact was updated locally, syncs changes to remote if local is newer.\n * 6. Remote Updates: When a contact was updated remotely, applies changes locally if remote is newer.\n * 7. Local Deletions: Handled by real-time event handlers (deleteContactInRemoteStorage) to prevent false positives.\n * 8. Remote Deletions: When a contact was deleted remotely, applies deletion locally.\n * 9. Concurrent Updates: Resolves conflicts using timestamps to determine the winner.\n * 10. Restore After Delete: If a contact is modified after being deleted, restores it.\n * 11. ChainId Differences: Treats same address on different chains as separate contacts.\n *\n * @param config - Parameters used for syncing callbacks\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when contact synchronization is complete\n */\nexport async function syncContactsWithUserStorage(\n config: SyncContactsWithUserStorageConfig,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { getMessenger, getUserStorageControllerInstance, trace } = options;\n const {\n onContactSyncErroneousSituation,\n onContactUpdated,\n onContactDeleted,\n } = config;\n\n // Cannot perform sync, conditions not met\n if (!canPerformContactSyncing(options)) {\n return;\n }\n\n // NOTE: Pre-sync operations (canPerformContactSyncing, AddressBookController:list, getRemoteContacts)\n // are intentionally outside try-catch to let errors bubble up to Sentry for better debugging.\n // Only \"erroneous situation\" errors during sync logic itself should be caught.\n\n // Get all local contacts from AddressBookController (exclude chain \"*\" contacts)\n const localVisibleContacts =\n getMessenger()\n .call('AddressBookController:list')\n .filter((contact) => !isContactBridgedFromAccounts(contact))\n .filter(\n (contact) => contact.address && contact.chainId && contact.name?.trim(),\n ) || [];\n\n // Get remote contacts from user storage API\n const remoteContacts = await getRemoteContacts(options);\n\n // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)\n const validRemoteContacts =\n remoteContacts?.filter(\n (contact) => contact.address && contact.chainId && contact.name?.trim(),\n ) || [];\n\n const performSync = async () => {\n try {\n // Activate sync semaphore to prevent event loops\n await getUserStorageControllerInstance().setIsContactSyncingInProgress(\n true,\n );\n\n // Prepare maps for efficient lookup\n const localContactsMap = new Map<string, AddressBookEntry>();\n const remoteContactsMap = new Map<string, SyncAddressBookEntry>();\n\n localVisibleContacts.forEach((contact) => {\n const key = createContactKey(contact);\n localContactsMap.set(key, contact);\n });\n\n validRemoteContacts.forEach((contact) => {\n const key = createContactKey(contact);\n remoteContactsMap.set(key, contact);\n });\n\n // Lists to track contacts that need to be synced\n const contactsToAddOrUpdateLocally: SyncAddressBookEntry[] = [];\n const contactsToDeleteLocally: SyncAddressBookEntry[] = [];\n const contactsToUpdateRemotely: AddressBookEntry[] = [];\n\n // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates\n for (const remoteContact of validRemoteContacts) {\n const key = createContactKey(remoteContact);\n const localContact = localContactsMap.get(key);\n\n // Handle remote contact based on its status and local existence\n if (remoteContact.deletedAt) {\n // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally\n if (localContact) {\n contactsToDeleteLocally.push(remoteContact);\n }\n } else if (!localContact) {\n // SCENARIO 2: New contact from remote - import to local\n contactsToAddOrUpdateLocally.push(remoteContact);\n } else {\n // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts\n const hasContentDifference =\n localContact.name !== remoteContact.name ||\n localContact.memo !== remoteContact.memo;\n\n if (hasContentDifference) {\n // Check timestamps to determine which version to keep\n const localTimestamp = localContact.lastUpdatedAt || 0;\n const remoteTimestamp = remoteContact.lastUpdatedAt || 0;\n\n if (localTimestamp >= remoteTimestamp) {\n // Local is newer (or same age) - use local version\n contactsToUpdateRemotely.push(localContact);\n } else {\n // Remote is newer - use remote version\n contactsToAddOrUpdateLocally.push(remoteContact);\n }\n }\n\n // Else: content is identical, no action needed\n }\n }\n\n // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts\n for (const localContact of localVisibleContacts) {\n const key = createContactKey(localContact);\n const remoteContact = remoteContactsMap.get(key);\n\n if (!remoteContact) {\n // New local contact or first sync - add to remote\n contactsToUpdateRemotely.push(localContact);\n }\n }\n\n // Apply local deletions\n // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry\n // for debugging. Previous versions silently suppressed these errors which made\n // troubleshooting contact sync issues difficult.\n for (const contact of contactsToDeleteLocally) {\n getMessenger().call(\n 'AddressBookController:delete',\n contact.chainId,\n contact.address,\n );\n\n if (onContactDeleted) {\n onContactDeleted();\n }\n }\n\n // Apply local additions/updates\n // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry\n // for debugging. Previous versions silently suppressed these errors which made\n // troubleshooting contact sync issues difficult.\n for (const contact of contactsToAddOrUpdateLocally) {\n if (!contact.deletedAt) {\n getMessenger().call(\n 'AddressBookController:set',\n contact.address,\n contact.name || '',\n contact.chainId,\n contact.memo || '',\n contact.addressType,\n );\n\n if (onContactUpdated) {\n onContactUpdated();\n }\n }\n }\n\n // Apply changes to remote storage\n if (contactsToUpdateRemotely.length > 0) {\n const updatedRemoteContacts: Record<string, SyncAddressBookEntry> = {};\n for (const localContact of contactsToUpdateRemotely) {\n const key = createContactKey(localContact);\n updatedRemoteContacts[key] = {\n ...remoteContactsMap.get(key), // Start with an existing remote contact if it exists\n ...localContact, // override with local changes\n lastUpdatedAt: Date.now(), // mark as updated\n };\n }\n // Save updated contacts to remote storage\n await saveContactsToUserStorage(\n Object.values(updatedRemoteContacts),\n options,\n );\n }\n } catch (error) {\n if (onContactSyncErroneousSituation) {\n onContactSyncErroneousSituation('Error synchronizing contacts', {\n error,\n });\n\n // Re-throw the error to be handled by the caller\n throw error;\n }\n } finally {\n await getUserStorageControllerInstance().setIsContactSyncingInProgress(\n false,\n );\n }\n };\n\n if (trace) {\n // Gather pre-sync metrics for performance analysis\n const initialLocalContacts = localVisibleContacts;\n const initialValidRemoteContacts = validRemoteContacts;\n\n await trace(\n {\n name: TraceName.ContactSyncFull,\n data: {\n localContactCount: initialLocalContacts.length,\n remoteContactCount: initialValidRemoteContacts.length,\n isFirstSync:\n initialValidRemoteContacts.length === 0 &&\n initialLocalContacts.length > 0,\n isNewDeviceSync:\n initialLocalContacts.length === 0 &&\n initialValidRemoteContacts.length > 0,\n isRegularSync:\n initialLocalContacts.length > 0 &&\n initialValidRemoteContacts.length > 0,\n hasDataToSync:\n initialLocalContacts.length > 0 ||\n initialValidRemoteContacts.length > 0,\n expectedWorkload:\n initialLocalContacts.length + initialValidRemoteContacts.length,\n },\n },\n performSync,\n );\n\n return;\n }\n\n await performSync();\n}\n\n/**\n * Retrieves remote contacts from user storage API\n *\n * @param options - Parameters used for retrieving remote contacts\n * @returns Array of contacts from remote storage, or null if none found\n */\nasync function getRemoteContacts(\n options: ContactSyncingOptions,\n): Promise<SyncAddressBookEntry[] | null> {\n const { getUserStorageControllerInstance } = options;\n\n try {\n const remoteContactsJsonArray =\n await getUserStorageControllerInstance().performGetStorageAllFeatureEntries(\n USER_STORAGE_FEATURE_NAMES.addressBook,\n );\n\n if (!remoteContactsJsonArray || remoteContactsJsonArray.length === 0) {\n return null;\n }\n\n // Parse each JSON entry and convert from UserStorageContactEntry to AddressBookEntry\n const remoteStorageEntries = remoteContactsJsonArray.map((contactJson) => {\n const entry = JSON.parse(contactJson) as UserStorageContactEntry;\n return mapUserStorageEntryToAddressBookEntry(entry);\n });\n\n return remoteStorageEntries;\n } catch {\n return null;\n }\n}\n\n/**\n * Saves local contacts to user storage\n *\n * @param contacts - The contacts to save to user storage\n * @param options - Parameters used for saving contacts\n * @returns Promise that resolves when contacts are saved\n */\nasync function saveContactsToUserStorage(\n contacts: AddressBookEntry[],\n options: ContactSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance, trace } = options;\n\n const saveContacts = async () => {\n if (!contacts || contacts.length === 0) {\n return;\n }\n\n // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs\n const storageEntries: [string, string][] = contacts.map((contact) => {\n const key = createContactKey(contact);\n const storageEntry = mapAddressBookEntryToUserStorageEntry(contact);\n return [key, JSON.stringify(storageEntry)];\n });\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.addressBook,\n storageEntries,\n );\n };\n\n return trace\n ? await trace(\n {\n name: TraceName.ContactSyncSaveBatch,\n data: {\n contactCount: contacts.length,\n // Performance scaling indicators\n hasBatchOperations: contacts.length > 1,\n chainCount: new Set(contacts.map((c) => c.chainId)).size,\n hasMemosCount: contacts.filter((c) => c.memo?.length).length,\n },\n },\n saveContacts,\n )\n : await saveContacts();\n}\n\n/**\n * Updates a single contact in remote storage without performing a full sync\n * This is used when a contact is updated locally to efficiently push changes to remote\n *\n * @param contact - The contact that was updated locally\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when the contact is updated\n */\nexport async function updateContactInRemoteStorage(\n contact: AddressBookEntry,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { trace } = options;\n\n const updateContact = async () => {\n if (\n !canPerformContactSyncing(options) ||\n !contact.address ||\n !contact.chainId ||\n !contact.name?.trim()\n ) {\n return;\n }\n\n const { getUserStorageControllerInstance } = options;\n\n // Create an updated entry with timestamp\n const updatedEntry = {\n ...contact,\n lastUpdatedAt: contact.lastUpdatedAt || Date.now(),\n } as SyncAddressBookEntry;\n\n const key = createContactKey(contact);\n const storageEntry = mapAddressBookEntryToUserStorageEntry(updatedEntry);\n\n // Save individual contact to remote storage\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n JSON.stringify(storageEntry),\n );\n };\n\n if (trace) {\n return await trace(\n {\n name: TraceName.ContactSyncUpdateRemote,\n data: {\n chainId: contact.chainId,\n // Performance indicators\n hasTimestamp: Boolean(contact.lastUpdatedAt),\n hasMemo: Boolean(contact.memo?.length),\n isUpdate: Boolean(contact.lastUpdatedAt), // vs new contact\n },\n },\n updateContact,\n );\n }\n\n return await updateContact();\n}\n\n/**\n * Marks a single contact as deleted in remote storage without performing a full sync\n * This is used when a contact is deleted locally to efficiently push the deletion to remote\n *\n * @param contact - The contact that was deleted locally (contains at least address and chainId)\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when the contact is marked as deleted\n */\nexport async function deleteContactInRemoteStorage(\n contact: AddressBookEntry,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { trace } = options;\n const deleteContact = async () => {\n if (\n !canPerformContactSyncing(options) ||\n !contact.address ||\n !contact.chainId ||\n !contact.name?.trim()\n ) {\n return;\n }\n\n const { getUserStorageControllerInstance } = options;\n const key = createContactKey(contact);\n\n try {\n // Try to get the existing contact first\n const existingContactJson =\n await getUserStorageControllerInstance().performGetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n );\n\n if (existingContactJson) {\n // Mark the existing contact as deleted\n const existingStorageEntry = JSON.parse(\n existingContactJson,\n ) as UserStorageContactEntry;\n const existingContact =\n mapUserStorageEntryToAddressBookEntry(existingStorageEntry);\n\n const now = Date.now();\n const deletedContact = {\n ...existingContact,\n deletedAt: now,\n lastUpdatedAt: now,\n } as SyncAddressBookEntry;\n\n const deletedStorageEntry =\n mapAddressBookEntryToUserStorageEntry(deletedContact);\n\n // Save the deleted contact back to storage\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n JSON.stringify(deletedStorageEntry),\n );\n }\n } catch {\n // If contact doesn't exist in remote storage, no need to mark as deleted\n console.warn('Contact not found in remote storage for deletion:', key);\n }\n };\n\n return trace\n ? await trace({ name: TraceName.ContactSyncDeleteRemote }, deleteContact)\n : await deleteContact();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"controller-integration.js","sourceRoot":"","sources":["../../../../src/controllers/user-storage/contact-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AddressBookEntry } from '@metamask/address-book-controller';\n\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema.js';\nimport { TraceName } from '../constants.js';\nimport { canPerformContactSyncing } from './sync-utils.js';\nimport type { ContactSyncingOptions } from './types.js';\nimport type { UserStorageContactEntry } from './types.js';\nimport type { SyncAddressBookEntry } from './utils.js';\nimport {\n mapAddressBookEntryToUserStorageEntry,\n mapUserStorageEntryToAddressBookEntry,\n} from './utils.js';\nimport { isContactBridgedFromAccounts } from './utils.js';\n\nexport type SyncContactsWithUserStorageConfig = {\n onContactSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n onContactUpdated?: () => void;\n onContactDeleted?: () => void;\n};\n\n/**\n * Creates a unique key for a contact based on chainId and address\n *\n * @param contact - The contact to create a key for\n * @returns A unique string key\n */\nfunction createContactKey(contact: AddressBookEntry): string {\n if (!contact.address) {\n throw new Error('Contact address is required to create storage key');\n }\n return `${contact.chainId}_${contact.address.toLowerCase()}`;\n}\n\n/**\n * Syncs contacts between local storage and user storage (remote).\n *\n * Handles the following syncing scenarios:\n * 1. First Sync: When local contacts exist but there are no remote contacts, uploads all local contacts.\n * 2. New Device Sync: Downloads remote contacts that don't exist locally (empty local address book).\n * 3. Simple Merge: Ensures both sides (local & remote) have all contacts.\n * 4. Contact Naming Conflicts: When same contact has different names, uses most recent by timestamp.\n * 5. Local Updates: When a contact was updated locally, syncs changes to remote if local is newer.\n * 6. Remote Updates: When a contact was updated remotely, applies changes locally if remote is newer.\n * 7. Local Deletions: Handled by real-time event handlers (deleteContactInRemoteStorage) to prevent false positives.\n * 8. Remote Deletions: When a contact was deleted remotely, applies deletion locally.\n * 9. Concurrent Updates: Resolves conflicts using timestamps to determine the winner.\n * 10. Restore After Delete: If a contact is modified after being deleted, restores it.\n * 11. ChainId Differences: Treats same address on different chains as separate contacts.\n *\n * @param config - Parameters used for syncing callbacks\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when contact synchronization is complete\n */\nexport async function syncContactsWithUserStorage(\n config: SyncContactsWithUserStorageConfig,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { getMessenger, getUserStorageControllerInstance, trace } = options;\n const {\n onContactSyncErroneousSituation,\n onContactUpdated,\n onContactDeleted,\n } = config;\n\n // Cannot perform sync, conditions not met\n if (!canPerformContactSyncing(options)) {\n return;\n }\n\n // NOTE: Pre-sync operations (canPerformContactSyncing, AddressBookController:list, getRemoteContacts)\n // are intentionally outside try-catch to let errors bubble up to Sentry for better debugging.\n // Only \"erroneous situation\" errors during sync logic itself should be caught.\n\n // Get all local contacts from AddressBookController (exclude chain \"*\" contacts)\n const localVisibleContacts =\n getMessenger()\n .call('AddressBookController:list')\n .filter((contact) => !isContactBridgedFromAccounts(contact))\n .filter(\n (contact) => contact.address && contact.chainId && contact.name?.trim(),\n ) || [];\n\n // Get remote contacts from user storage API\n const remoteContacts = await getRemoteContacts(options);\n\n // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)\n const validRemoteContacts =\n remoteContacts?.filter(\n (contact) => contact.address && contact.chainId && contact.name?.trim(),\n ) || [];\n\n const performSync = async () => {\n try {\n // Activate sync semaphore to prevent event loops\n await getUserStorageControllerInstance().setIsContactSyncingInProgress(\n true,\n );\n\n // Prepare maps for efficient lookup\n const localContactsMap = new Map<string, AddressBookEntry>();\n const remoteContactsMap = new Map<string, SyncAddressBookEntry>();\n\n localVisibleContacts.forEach((contact) => {\n const key = createContactKey(contact);\n localContactsMap.set(key, contact);\n });\n\n validRemoteContacts.forEach((contact) => {\n const key = createContactKey(contact);\n remoteContactsMap.set(key, contact);\n });\n\n // Lists to track contacts that need to be synced\n const contactsToAddOrUpdateLocally: SyncAddressBookEntry[] = [];\n const contactsToDeleteLocally: SyncAddressBookEntry[] = [];\n const contactsToUpdateRemotely: AddressBookEntry[] = [];\n\n // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates\n for (const remoteContact of validRemoteContacts) {\n const key = createContactKey(remoteContact);\n const localContact = localContactsMap.get(key);\n\n // Handle remote contact based on its status and local existence\n if (remoteContact.deletedAt) {\n // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally\n if (localContact) {\n contactsToDeleteLocally.push(remoteContact);\n }\n } else if (localContact) {\n // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts\n const hasContentDifference =\n localContact.name !== remoteContact.name ||\n localContact.memo !== remoteContact.memo;\n\n if (hasContentDifference) {\n // Check timestamps to determine which version to keep\n const localTimestamp = localContact.lastUpdatedAt || 0;\n const remoteTimestamp = remoteContact.lastUpdatedAt || 0;\n\n if (localTimestamp >= remoteTimestamp) {\n // Local is newer (or same age) - use local version\n contactsToUpdateRemotely.push(localContact);\n } else {\n // Remote is newer - use remote version\n contactsToAddOrUpdateLocally.push(remoteContact);\n }\n }\n\n // Else: content is identical, no action needed\n } else {\n // SCENARIO 2: New contact from remote - import to local\n contactsToAddOrUpdateLocally.push(remoteContact);\n }\n }\n\n // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts\n for (const localContact of localVisibleContacts) {\n const key = createContactKey(localContact);\n const remoteContact = remoteContactsMap.get(key);\n\n if (!remoteContact) {\n // New local contact or first sync - add to remote\n contactsToUpdateRemotely.push(localContact);\n }\n }\n\n // Apply local deletions\n // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry\n // for debugging. Previous versions silently suppressed these errors which made\n // troubleshooting contact sync issues difficult.\n for (const contact of contactsToDeleteLocally) {\n getMessenger().call(\n 'AddressBookController:delete',\n contact.chainId,\n contact.address,\n );\n\n if (onContactDeleted) {\n onContactDeleted();\n }\n }\n\n // Apply local additions/updates\n // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry\n // for debugging. Previous versions silently suppressed these errors which made\n // troubleshooting contact sync issues difficult.\n for (const contact of contactsToAddOrUpdateLocally) {\n if (!contact.deletedAt) {\n getMessenger().call(\n 'AddressBookController:set',\n contact.address,\n contact.name || '',\n contact.chainId,\n contact.memo || '',\n contact.addressType,\n );\n\n if (onContactUpdated) {\n onContactUpdated();\n }\n }\n }\n\n // Apply changes to remote storage\n if (contactsToUpdateRemotely.length > 0) {\n const updatedRemoteContacts: Record<string, SyncAddressBookEntry> = {};\n for (const localContact of contactsToUpdateRemotely) {\n const key = createContactKey(localContact);\n updatedRemoteContacts[key] = {\n ...remoteContactsMap.get(key), // Start with an existing remote contact if it exists\n ...localContact, // override with local changes\n lastUpdatedAt: Date.now(), // mark as updated\n };\n }\n // Save updated contacts to remote storage\n await saveContactsToUserStorage(\n Object.values(updatedRemoteContacts),\n options,\n );\n }\n } catch (error) {\n if (onContactSyncErroneousSituation) {\n onContactSyncErroneousSituation('Error synchronizing contacts', {\n error,\n });\n\n // Re-throw the error to be handled by the caller\n throw error;\n }\n } finally {\n await getUserStorageControllerInstance().setIsContactSyncingInProgress(\n false,\n );\n }\n };\n\n if (trace) {\n // Gather pre-sync metrics for performance analysis\n const initialLocalContacts = localVisibleContacts;\n const initialValidRemoteContacts = validRemoteContacts;\n\n await trace(\n {\n name: TraceName.ContactSyncFull,\n data: {\n localContactCount: initialLocalContacts.length,\n remoteContactCount: initialValidRemoteContacts.length,\n isFirstSync:\n initialValidRemoteContacts.length === 0 &&\n initialLocalContacts.length > 0,\n isNewDeviceSync:\n initialLocalContacts.length === 0 &&\n initialValidRemoteContacts.length > 0,\n isRegularSync:\n initialLocalContacts.length > 0 &&\n initialValidRemoteContacts.length > 0,\n hasDataToSync:\n initialLocalContacts.length > 0 ||\n initialValidRemoteContacts.length > 0,\n expectedWorkload:\n initialLocalContacts.length + initialValidRemoteContacts.length,\n },\n },\n performSync,\n );\n\n return;\n }\n\n await performSync();\n}\n\n/**\n * Retrieves remote contacts from user storage API\n *\n * @param options - Parameters used for retrieving remote contacts\n * @returns Array of contacts from remote storage, or null if none found\n */\nasync function getRemoteContacts(\n options: ContactSyncingOptions,\n): Promise<SyncAddressBookEntry[] | null> {\n const { getUserStorageControllerInstance } = options;\n\n try {\n const remoteContactsJsonArray =\n await getUserStorageControllerInstance().performGetStorageAllFeatureEntries(\n USER_STORAGE_FEATURE_NAMES.addressBook,\n );\n\n if (!remoteContactsJsonArray || remoteContactsJsonArray.length === 0) {\n return null;\n }\n\n // Parse each JSON entry and convert from UserStorageContactEntry to AddressBookEntry\n const remoteStorageEntries = remoteContactsJsonArray.map((contactJson) => {\n const entry = JSON.parse(contactJson) as UserStorageContactEntry;\n return mapUserStorageEntryToAddressBookEntry(entry);\n });\n\n return remoteStorageEntries;\n } catch {\n return null;\n }\n}\n\n/**\n * Saves local contacts to user storage\n *\n * @param contacts - The contacts to save to user storage\n * @param options - Parameters used for saving contacts\n * @returns Promise that resolves when contacts are saved\n */\nasync function saveContactsToUserStorage(\n contacts: AddressBookEntry[],\n options: ContactSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance, trace } = options;\n\n const saveContacts = async () => {\n if (!contacts || contacts.length === 0) {\n return;\n }\n\n // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs\n const storageEntries: [string, string][] = contacts.map((contact) => {\n const key = createContactKey(contact);\n const storageEntry = mapAddressBookEntryToUserStorageEntry(contact);\n return [key, JSON.stringify(storageEntry)];\n });\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.addressBook,\n storageEntries,\n );\n };\n\n return trace\n ? await trace(\n {\n name: TraceName.ContactSyncSaveBatch,\n data: {\n contactCount: contacts.length,\n // Performance scaling indicators\n hasBatchOperations: contacts.length > 1,\n chainCount: new Set(contacts.map((c) => c.chainId)).size,\n hasMemosCount: contacts.filter((c) => c.memo?.length).length,\n },\n },\n saveContacts,\n )\n : await saveContacts();\n}\n\n/**\n * Updates a single contact in remote storage without performing a full sync\n * This is used when a contact is updated locally to efficiently push changes to remote\n *\n * @param contact - The contact that was updated locally\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when the contact is updated\n */\nexport async function updateContactInRemoteStorage(\n contact: AddressBookEntry,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { trace } = options;\n\n const updateContact = async () => {\n if (\n !canPerformContactSyncing(options) ||\n !contact.address ||\n !contact.chainId ||\n !contact.name?.trim()\n ) {\n return;\n }\n\n const { getUserStorageControllerInstance } = options;\n\n // Create an updated entry with timestamp\n const updatedEntry = {\n ...contact,\n lastUpdatedAt: contact.lastUpdatedAt || Date.now(),\n } as SyncAddressBookEntry;\n\n const key = createContactKey(contact);\n const storageEntry = mapAddressBookEntryToUserStorageEntry(updatedEntry);\n\n // Save individual contact to remote storage\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n JSON.stringify(storageEntry),\n );\n };\n\n if (trace) {\n return await trace(\n {\n name: TraceName.ContactSyncUpdateRemote,\n data: {\n chainId: contact.chainId,\n // Performance indicators\n hasTimestamp: Boolean(contact.lastUpdatedAt),\n hasMemo: Boolean(contact.memo?.length),\n isUpdate: Boolean(contact.lastUpdatedAt), // vs new contact\n },\n },\n updateContact,\n );\n }\n\n return await updateContact();\n}\n\n/**\n * Marks a single contact as deleted in remote storage without performing a full sync\n * This is used when a contact is deleted locally to efficiently push the deletion to remote\n *\n * @param contact - The contact that was deleted locally (contains at least address and chainId)\n * @param options - Parameters used for syncing operations\n * @returns Promise that resolves when the contact is marked as deleted\n */\nexport async function deleteContactInRemoteStorage(\n contact: AddressBookEntry,\n options: ContactSyncingOptions,\n): Promise<void> {\n const { trace } = options;\n const deleteContact = async () => {\n if (\n !canPerformContactSyncing(options) ||\n !contact.address ||\n !contact.chainId ||\n !contact.name?.trim()\n ) {\n return;\n }\n\n const { getUserStorageControllerInstance } = options;\n const key = createContactKey(contact);\n\n try {\n // Try to get the existing contact first\n const existingContactJson =\n await getUserStorageControllerInstance().performGetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n );\n\n if (existingContactJson) {\n // Mark the existing contact as deleted\n const existingStorageEntry = JSON.parse(\n existingContactJson,\n ) as UserStorageContactEntry;\n const existingContact =\n mapUserStorageEntryToAddressBookEntry(existingStorageEntry);\n\n const now = Date.now();\n const deletedContact = {\n ...existingContact,\n deletedAt: now,\n lastUpdatedAt: now,\n } as SyncAddressBookEntry;\n\n const deletedStorageEntry =\n mapAddressBookEntryToUserStorageEntry(deletedContact);\n\n // Save the deleted contact back to storage\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`,\n JSON.stringify(deletedStorageEntry),\n );\n }\n } catch {\n // If contact doesn't exist in remote storage, no need to mark as deleted\n console.warn('Contact not found in remote storage for deletion:', key);\n }\n };\n\n return trace\n ? await trace({ name: TraceName.ContactSyncDeleteRemote }, deleteContact)\n : await deleteContact();\n}\n"]}
|
|
@@ -62,8 +62,9 @@ export declare class SRPJwtBearerAuth implements IBaseAuth {
|
|
|
62
62
|
* @param flowId - Identifier returned by the begin call.
|
|
63
63
|
* @param proof - Platform attestation or email code.
|
|
64
64
|
* @param entropySourceId - Entropy source whose profile owns the credential.
|
|
65
|
+
* @returns Assertion proving the enrolled credential.
|
|
65
66
|
*/
|
|
66
|
-
completeMfaEnrollment(flowId: string, proof: EnrollmentProof, entropySourceId?: string): Promise<
|
|
67
|
+
completeMfaEnrollment(flowId: string, proof: EnrollmentProof, entropySourceId?: string): Promise<MfaVerificationAssertion>;
|
|
67
68
|
/**
|
|
68
69
|
* Begins verification with an enrolled credential.
|
|
69
70
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-srp.d.ts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAiBtE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAWxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,mBAAmB,EAEnB,iBAAiB,EACjB,eAAe,EACf,0BAA0B,EAC1B,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACjE;;;OAGG;IACH,sBAAsB,CAAC,EAAE,CACvB,eAAe,CAAC,EAAE,MAAM,KACrB,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAIF,eAAO,MAAM,kBAAkB,QAAS,CAAC;AA+BzC,qBAAa,gBAAiB,YAAW,SAAS;;IAsChD,YACE,MAAM,EAAE,UAAU,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAA;KAAE,EAC3C,OAAO,EAAE,yBAAyB,GAAG;QACnC,cAAc,CAAC,EAAE,eAAe,CAAC;QACjC,WAAW,CAAC,EAAE,eAAe,CAAC;KAC/B,EAkBF;IAED,iBAAiB,CAAC,QAAQ,EAAE,eAAe,QAG1C;IAGK,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ9D;IAEK,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAQnE;IAEK,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE7D;IAEK,qBAAqB,CACzB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAG7B;IAEK,uBAAuB,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvE;IAEK,uBAAuB,CAC3B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAQjB;IAED;;;;;;;;;;;;OAYG;IACG,kBAAkB,CACtB,IAAI,EAAE,iBAAiB,EACvB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GACA,OAAO,CAAC,mBAAmB,CAAC,CA4B9B;IAED
|
|
1
|
+
{"version":3,"file":"flow-srp.d.ts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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"}
|
|
@@ -139,10 +139,11 @@ export class SRPJwtBearerAuth {
|
|
|
139
139
|
* @param flowId - Identifier returned by the begin call.
|
|
140
140
|
* @param proof - Platform attestation or email code.
|
|
141
141
|
* @param entropySourceId - Entropy source whose profile owns the credential.
|
|
142
|
+
* @returns Assertion proving the enrolled credential.
|
|
142
143
|
*/
|
|
143
144
|
async completeMfaEnrollment(flowId, proof, entropySourceId) {
|
|
144
145
|
const accessToken = await this.getAccessToken(entropySourceId);
|
|
145
|
-
await mfaEnrollComplete(this.#config.env, accessToken, {
|
|
146
|
+
return await mfaEnrollComplete(this.#config.env, accessToken, {
|
|
146
147
|
credential_type: proof.type,
|
|
147
148
|
flow_id: flowId,
|
|
148
149
|
...(proof.type === 'passkey'
|