@metamask-previews/profile-sync-controller 28.0.2-preview-8daaa5a40 → 28.0.2-preview-ae9b6b322
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 +14 -2
- package/dist/controllers/authentication/AuthenticationController-method-action-types.cjs.map +1 -1
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.cts +52 -6
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.cts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.mts +52 -6
- package/dist/controllers/authentication/AuthenticationController-method-action-types.d.mts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController-method-action-types.mjs.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.cjs +143 -6
- package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.cts +66 -7
- package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.mts +66 -7
- package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.mjs +143 -6
- package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
- package/dist/controllers/authentication/index.cjs.map +1 -1
- package/dist/controllers/authentication/index.d.cts +1 -1
- package/dist/controllers/authentication/index.d.cts.map +1 -1
- package/dist/controllers/authentication/index.d.mts +1 -1
- package/dist/controllers/authentication/index.d.mts.map +1 -1
- package/dist/controllers/authentication/index.mjs.map +1 -1
- package/dist/controllers/authentication/mocks/mockResponses.cjs +10 -1
- package/dist/controllers/authentication/mocks/mockResponses.cjs.map +1 -1
- package/dist/controllers/authentication/mocks/mockResponses.d.cts +17 -32
- package/dist/controllers/authentication/mocks/mockResponses.d.cts.map +1 -1
- package/dist/controllers/authentication/mocks/mockResponses.d.mts +17 -32
- package/dist/controllers/authentication/mocks/mockResponses.d.mts.map +1 -1
- package/dist/controllers/authentication/mocks/mockResponses.mjs +9 -1
- package/dist/controllers/authentication/mocks/mockResponses.mjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs +28 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts +2 -0
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts +2 -0
- package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs +29 -2
- package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/services.cjs +58 -1
- package/dist/sdk/authentication-jwt-bearer/services.cjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/services.d.cts +17 -1
- package/dist/sdk/authentication-jwt-bearer/services.d.cts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/services.d.mts +17 -1
- package/dist/sdk/authentication-jwt-bearer/services.d.mts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/services.mjs +55 -0
- package/dist/sdk/authentication-jwt-bearer/services.mjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/types.cjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/types.d.cts +20 -1
- package/dist/sdk/authentication-jwt-bearer/types.d.cts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/types.d.mts +20 -1
- package/dist/sdk/authentication-jwt-bearer/types.d.mts.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/types.mjs.map +1 -1
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.cjs +27 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.cjs.map +1 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.cts +13 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.cts.map +1 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.mts +13 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.mts.map +1 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.mjs +23 -0
- package/dist/sdk/authentication-jwt-bearer/utils/identifier.mjs.map +1 -0
- package/dist/sdk/authentication.cjs +4 -0
- package/dist/sdk/authentication.cjs.map +1 -1
- package/dist/sdk/authentication.d.cts +2 -0
- package/dist/sdk/authentication.d.cts.map +1 -1
- package/dist/sdk/authentication.d.mts +2 -0
- package/dist/sdk/authentication.d.mts.map +1 -1
- package/dist/sdk/authentication.mjs +4 -0
- package/dist/sdk/authentication.mjs.map +1 -1
- package/dist/sdk/mocks/auth.cjs +11 -1
- package/dist/sdk/mocks/auth.cjs.map +1 -1
- package/dist/sdk/mocks/auth.d.cts +10 -0
- package/dist/sdk/mocks/auth.d.cts.map +1 -1
- package/dist/sdk/mocks/auth.d.mts +10 -0
- package/dist/sdk/mocks/auth.d.mts.map +1 -1
- package/dist/sdk/mocks/auth.mjs +11 -1
- package/dist/sdk/mocks/auth.mjs.map +1 -1
- package/dist/sdk/user-storage.cjs +26 -3
- package/dist/sdk/user-storage.cjs.map +1 -1
- package/dist/sdk/user-storage.d.cts +7 -0
- package/dist/sdk/user-storage.d.cts.map +1 -1
- package/dist/sdk/user-storage.d.mts +7 -0
- package/dist/sdk/user-storage.d.mts.map +1 -1
- package/dist/sdk/user-storage.mjs +26 -3
- package/dist/sdk/user-storage.mjs.map +1 -1
- package/dist/sdk/utils/validate-pair-response.cjs +29 -0
- package/dist/sdk/utils/validate-pair-response.cjs.map +1 -0
- package/dist/sdk/utils/validate-pair-response.d.cts +26 -0
- package/dist/sdk/utils/validate-pair-response.d.cts.map +1 -0
- package/dist/sdk/utils/validate-pair-response.d.mts +26 -0
- package/dist/sdk/utils/validate-pair-response.d.mts.map +1 -0
- package/dist/sdk/utils/validate-pair-response.mjs +25 -0
- package/dist/sdk/utils/validate-pair-response.mjs.map +1 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -9,12 +9,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- Add SRP profile pairing support (Accounts ADR 0006) ([#8504](https://github.com/MetaMask/core/pull/8504), [#8642](https://github.com/MetaMask/core/pull/8642))
|
|
13
|
+
- Add `performProfilePairing` public method (and messenger action) to `AuthenticationController` — pairs all SRPs via `POST /profile/pair` when 2+ SRPs exist; intended to be called by the client-side `useAutoProfilePairing` hook rather than inline with sign-in
|
|
14
|
+
- Add `hasPairedAtLeastOnce: boolean` to `AuthenticationControllerState` — monotonic flag (false → true, never reset) set by `performProfilePairing` on first successful pairing; persisted across sessions so the client hook can determine first-launch pairing needs
|
|
15
|
+
- Add `canonicalProfileId` to `UserProfile` — the unified profile ID across paired SRPs
|
|
16
|
+
- Add `ProfileAlias` type for transient alias data returned by the pairing API
|
|
17
|
+
- Add `pairSrpProfiles` method to `SRPJwtBearerAuth` and `JwtBearerAuth`
|
|
18
|
+
- Add `ProfileSignInEvent` (`AuthenticationController:profileSignIn`) emitted by `performProfilePairing` after successful pairing
|
|
19
|
+
- Send `X-MetaMask-Profile-Pairing: enabled` header on all `/srp/login` requests
|
|
20
|
+
- Resolve original per-SRP `profileId` from `profile_aliases` using `computeIdentifierId`
|
|
21
|
+
- Propagate canonical profile ID to all `srpSessionData` entries after pairing
|
|
22
|
+
- Add `refreshCanonicalProfileId` method — forces a fresh canonical retrieval from the server (1 primary SRP login) and propagates it to all cached SRP sessions. For best-effort reads, use `getSessionProfile().canonicalProfileId` instead.
|
|
23
|
+
- Force re-login when cached session is missing `canonicalProfileId`
|
|
12
24
|
- Add optional `getAppVersion` callback to `MetaMetricsAuth`, forwarded as `metametrics.app_version` in the `POST /api/v2/srp/login` payload. ([#8626](https://github.com/MetaMask/core/pull/8626))
|
|
13
25
|
|
|
14
26
|
### Changed
|
|
15
27
|
|
|
16
|
-
- Bump `@metamask/keyring-controller` from `^25.1.1` to `^25.
|
|
17
|
-
- Bump `@metamask/messenger` from `^1.0.0` to `^1.
|
|
28
|
+
- Bump `@metamask/keyring-controller` from `^25.1.1` to `^25.3.0` ([#8363](https://github.com/MetaMask/core/pull/8363), [#8634](https://github.com/MetaMask/core/pull/8634))
|
|
29
|
+
- Bump `@metamask/messenger` from `^1.0.0` to `^1.2.0` ([#8364](https://github.com/MetaMask/core/pull/8364), [#8373](https://github.com/MetaMask/core/pull/8373), [#8632](https://github.com/MetaMask/core/pull/8632))
|
|
18
30
|
- Bump `@metamask/base-controller` from `^9.0.1` to `^9.1.0` ([#8457](https://github.com/MetaMask/core/pull/8457))
|
|
19
31
|
|
|
20
32
|
## [28.0.2]
|
package/dist/controllers/authentication/AuthenticationController-method-action-types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController-method-action-types.cjs","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticationController } from './AuthenticationController';\n\nexport type AuthenticationControllerPerformSignInAction = {\n type: `AuthenticationController:performSignIn`;\n handler: AuthenticationController['performSignIn'];\n};\n\nexport type AuthenticationControllerPerformSignOutAction = {\n type: `AuthenticationController:performSignOut`;\n handler: AuthenticationController['performSignOut'];\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"AuthenticationController-method-action-types.cjs","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticationController } from './AuthenticationController';\n\nexport type AuthenticationControllerPerformSignInAction = {\n type: `AuthenticationController:performSignIn`;\n handler: AuthenticationController['performSignIn'];\n};\n\n/**\n * Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the\n * canonical profile ID into every cached SRP session, and emits\n * `AuthenticationController:profileSignIn` when the canonical changes or\n * new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.\n *\n * No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when\n * the wallet is locked.\n *\n * Pairing failures are swallowed so the caller (typically the client-side\n * `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.\n */\nexport type AuthenticationControllerPerformProfilePairingAction = {\n type: `AuthenticationController:performProfilePairing`;\n handler: AuthenticationController['performProfilePairing'];\n};\n\nexport type AuthenticationControllerPerformSignOutAction = {\n type: `AuthenticationController:performSignOut`;\n handler: AuthenticationController['performSignOut'];\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 */\nexport type AuthenticationControllerGetBearerTokenAction = {\n type: `AuthenticationController:getBearerToken`;\n handler: AuthenticationController['getBearerToken'];\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 */\nexport type AuthenticationControllerGetSessionProfileAction = {\n type: `AuthenticationController:getSessionProfile`;\n handler: AuthenticationController['getSessionProfile'];\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 */\nexport type AuthenticationControllerRefreshCanonicalProfileIdAction = {\n type: `AuthenticationController:refreshCanonicalProfileId`;\n handler: AuthenticationController['refreshCanonicalProfileId'];\n};\n\nexport type AuthenticationControllerGetUserProfileLineageAction = {\n type: `AuthenticationController:getUserProfileLineage`;\n handler: AuthenticationController['getUserProfileLineage'];\n};\n\nexport type AuthenticationControllerIsSignedInAction = {\n type: `AuthenticationController:isSignedIn`;\n handler: AuthenticationController['isSignedIn'];\n};\n\n/**\n * Union of all AuthenticationController action types.\n */\nexport type AuthenticationControllerMethodActions =\n | AuthenticationControllerPerformSignInAction\n | AuthenticationControllerPerformProfilePairingAction\n | AuthenticationControllerPerformSignOutAction\n | AuthenticationControllerGetBearerTokenAction\n | AuthenticationControllerGetSessionProfileAction\n | AuthenticationControllerRefreshCanonicalProfileIdAction\n | AuthenticationControllerGetUserProfileLineageAction\n | AuthenticationControllerIsSignedInAction;\n"]}
|
|
@@ -7,23 +7,49 @@ export type AuthenticationControllerPerformSignInAction = {
|
|
|
7
7
|
type: `AuthenticationController:performSignIn`;
|
|
8
8
|
handler: AuthenticationController['performSignIn'];
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the
|
|
12
|
+
* canonical profile ID into every cached SRP session, and emits
|
|
13
|
+
* `AuthenticationController:profileSignIn` when the canonical changes or
|
|
14
|
+
* new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.
|
|
15
|
+
*
|
|
16
|
+
* No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when
|
|
17
|
+
* the wallet is locked.
|
|
18
|
+
*
|
|
19
|
+
* Pairing failures are swallowed so the caller (typically the client-side
|
|
20
|
+
* `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.
|
|
21
|
+
*/
|
|
22
|
+
export type AuthenticationControllerPerformProfilePairingAction = {
|
|
23
|
+
type: `AuthenticationController:performProfilePairing`;
|
|
24
|
+
handler: AuthenticationController['performProfilePairing'];
|
|
25
|
+
};
|
|
10
26
|
export type AuthenticationControllerPerformSignOutAction = {
|
|
11
27
|
type: `AuthenticationController:performSignOut`;
|
|
12
28
|
handler: AuthenticationController['performSignOut'];
|
|
13
29
|
};
|
|
14
30
|
/**
|
|
15
|
-
*
|
|
16
|
-
* Logs a user in if a user is not logged in.
|
|
31
|
+
* Returns a bearer token for the specified SRP, logging in if needed.
|
|
17
32
|
*
|
|
18
|
-
*
|
|
33
|
+
* When called without `entropySourceId`, returns the primary (first) SRP's
|
|
34
|
+
* access token, which is effectively the canonical
|
|
35
|
+
* profile's token that can be used by alias-aware consumers for cross-SRP
|
|
36
|
+
* operations.
|
|
37
|
+
*
|
|
38
|
+
* @param entropySourceId - The entropy source ID. Omit for the primary SRP.
|
|
39
|
+
* @returns The OIDC access token.
|
|
19
40
|
*/
|
|
20
41
|
export type AuthenticationControllerGetBearerTokenAction = {
|
|
21
42
|
type: `AuthenticationController:getBearerToken`;
|
|
22
43
|
handler: AuthenticationController['getBearerToken'];
|
|
23
44
|
};
|
|
24
45
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
46
|
+
* Returns the cached session profile, logging in if no session exists.
|
|
47
|
+
*
|
|
48
|
+
* The returned `canonicalProfileId` reflects the value from the most recent
|
|
49
|
+
* login or pairing. In the rare event where a canonical changed because of
|
|
50
|
+
* a pairing that happened on another device, the cached value may be stale
|
|
51
|
+
* until the next login. For guaranteed freshness, call
|
|
52
|
+
* `refreshCanonicalProfileId()` before reading `canonicalProfileId`.
|
|
27
53
|
*
|
|
28
54
|
* @param entropySourceId - The entropy source ID used to derive the key,
|
|
29
55
|
* when multiple sources are available (Multi-SRP).
|
|
@@ -33,6 +59,26 @@ export type AuthenticationControllerGetSessionProfileAction = {
|
|
|
33
59
|
type: `AuthenticationController:getSessionProfile`;
|
|
34
60
|
handler: AuthenticationController['getSessionProfile'];
|
|
35
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Forces a fresh retrieval of the canonical profile ID from the server
|
|
64
|
+
* and propagates it to all cached SRP sessions.
|
|
65
|
+
*
|
|
66
|
+
* This method invalidates the primary SRP's cached session and forces a
|
|
67
|
+
* re-login. Use it before operations that require a guaranteed-fresh
|
|
68
|
+
* canonical (e.g. storage key derivation for Accounts ADR 0005). For
|
|
69
|
+
* best-effort reads, use
|
|
70
|
+
* `getSessionProfile().canonicalProfileId` instead.
|
|
71
|
+
*
|
|
72
|
+
* Only the primary SRP is re-logged-in regardless of how many SRPs exist —
|
|
73
|
+
* the server returns the current canonical for the entire pairing group
|
|
74
|
+
* from any single SRP login.
|
|
75
|
+
*
|
|
76
|
+
* @returns The refreshed canonical profile ID.
|
|
77
|
+
*/
|
|
78
|
+
export type AuthenticationControllerRefreshCanonicalProfileIdAction = {
|
|
79
|
+
type: `AuthenticationController:refreshCanonicalProfileId`;
|
|
80
|
+
handler: AuthenticationController['refreshCanonicalProfileId'];
|
|
81
|
+
};
|
|
36
82
|
export type AuthenticationControllerGetUserProfileLineageAction = {
|
|
37
83
|
type: `AuthenticationController:getUserProfileLineage`;
|
|
38
84
|
handler: AuthenticationController['getUserProfileLineage'];
|
|
@@ -44,5 +90,5 @@ export type AuthenticationControllerIsSignedInAction = {
|
|
|
44
90
|
/**
|
|
45
91
|
* Union of all AuthenticationController action types.
|
|
46
92
|
*/
|
|
47
|
-
export type AuthenticationControllerMethodActions = AuthenticationControllerPerformSignInAction | AuthenticationControllerPerformSignOutAction | AuthenticationControllerGetBearerTokenAction | AuthenticationControllerGetSessionProfileAction | AuthenticationControllerGetUserProfileLineageAction | AuthenticationControllerIsSignedInAction;
|
|
93
|
+
export type AuthenticationControllerMethodActions = AuthenticationControllerPerformSignInAction | AuthenticationControllerPerformProfilePairingAction | AuthenticationControllerPerformSignOutAction | AuthenticationControllerGetBearerTokenAction | AuthenticationControllerGetSessionProfileAction | AuthenticationControllerRefreshCanonicalProfileIdAction | AuthenticationControllerGetUserProfileLineageAction | AuthenticationControllerIsSignedInAction;
|
|
48
94
|
//# sourceMappingURL=AuthenticationController-method-action-types.d.cts.map
|
package/dist/controllers/authentication/AuthenticationController-method-action-types.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController-method-action-types.d.cts","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,uCAAmC;AAE3E,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,wBAAwB,CAAC,eAAe,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"AuthenticationController-method-action-types.d.cts","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,uCAAmC;AAE3E,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,wBAAwB,CAAC,eAAe,CAAC,CAAC;CACpD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,wBAAwB,CAAC,uBAAuB,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,+CAA+C,GAAG;IAC5D,IAAI,EAAE,4CAA4C,CAAC;IACnD,OAAO,EAAE,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;CACxD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,uDAAuD,GAAG;IACpE,IAAI,EAAE,oDAAoD,CAAC;IAC3D,OAAO,EAAE,wBAAwB,CAAC,2BAA2B,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,wBAAwB,CAAC,uBAAuB,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,wBAAwB,CAAC,YAAY,CAAC,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qCAAqC,GAC7C,2CAA2C,GAC3C,mDAAmD,GACnD,4CAA4C,GAC5C,4CAA4C,GAC5C,+CAA+C,GAC/C,uDAAuD,GACvD,mDAAmD,GACnD,wCAAwC,CAAC"}
|
|
@@ -7,23 +7,49 @@ export type AuthenticationControllerPerformSignInAction = {
|
|
|
7
7
|
type: `AuthenticationController:performSignIn`;
|
|
8
8
|
handler: AuthenticationController['performSignIn'];
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the
|
|
12
|
+
* canonical profile ID into every cached SRP session, and emits
|
|
13
|
+
* `AuthenticationController:profileSignIn` when the canonical changes or
|
|
14
|
+
* new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.
|
|
15
|
+
*
|
|
16
|
+
* No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when
|
|
17
|
+
* the wallet is locked.
|
|
18
|
+
*
|
|
19
|
+
* Pairing failures are swallowed so the caller (typically the client-side
|
|
20
|
+
* `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.
|
|
21
|
+
*/
|
|
22
|
+
export type AuthenticationControllerPerformProfilePairingAction = {
|
|
23
|
+
type: `AuthenticationController:performProfilePairing`;
|
|
24
|
+
handler: AuthenticationController['performProfilePairing'];
|
|
25
|
+
};
|
|
10
26
|
export type AuthenticationControllerPerformSignOutAction = {
|
|
11
27
|
type: `AuthenticationController:performSignOut`;
|
|
12
28
|
handler: AuthenticationController['performSignOut'];
|
|
13
29
|
};
|
|
14
30
|
/**
|
|
15
|
-
*
|
|
16
|
-
* Logs a user in if a user is not logged in.
|
|
31
|
+
* Returns a bearer token for the specified SRP, logging in if needed.
|
|
17
32
|
*
|
|
18
|
-
*
|
|
33
|
+
* When called without `entropySourceId`, returns the primary (first) SRP's
|
|
34
|
+
* access token, which is effectively the canonical
|
|
35
|
+
* profile's token that can be used by alias-aware consumers for cross-SRP
|
|
36
|
+
* operations.
|
|
37
|
+
*
|
|
38
|
+
* @param entropySourceId - The entropy source ID. Omit for the primary SRP.
|
|
39
|
+
* @returns The OIDC access token.
|
|
19
40
|
*/
|
|
20
41
|
export type AuthenticationControllerGetBearerTokenAction = {
|
|
21
42
|
type: `AuthenticationController:getBearerToken`;
|
|
22
43
|
handler: AuthenticationController['getBearerToken'];
|
|
23
44
|
};
|
|
24
45
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
46
|
+
* Returns the cached session profile, logging in if no session exists.
|
|
47
|
+
*
|
|
48
|
+
* The returned `canonicalProfileId` reflects the value from the most recent
|
|
49
|
+
* login or pairing. In the rare event where a canonical changed because of
|
|
50
|
+
* a pairing that happened on another device, the cached value may be stale
|
|
51
|
+
* until the next login. For guaranteed freshness, call
|
|
52
|
+
* `refreshCanonicalProfileId()` before reading `canonicalProfileId`.
|
|
27
53
|
*
|
|
28
54
|
* @param entropySourceId - The entropy source ID used to derive the key,
|
|
29
55
|
* when multiple sources are available (Multi-SRP).
|
|
@@ -33,6 +59,26 @@ export type AuthenticationControllerGetSessionProfileAction = {
|
|
|
33
59
|
type: `AuthenticationController:getSessionProfile`;
|
|
34
60
|
handler: AuthenticationController['getSessionProfile'];
|
|
35
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Forces a fresh retrieval of the canonical profile ID from the server
|
|
64
|
+
* and propagates it to all cached SRP sessions.
|
|
65
|
+
*
|
|
66
|
+
* This method invalidates the primary SRP's cached session and forces a
|
|
67
|
+
* re-login. Use it before operations that require a guaranteed-fresh
|
|
68
|
+
* canonical (e.g. storage key derivation for Accounts ADR 0005). For
|
|
69
|
+
* best-effort reads, use
|
|
70
|
+
* `getSessionProfile().canonicalProfileId` instead.
|
|
71
|
+
*
|
|
72
|
+
* Only the primary SRP is re-logged-in regardless of how many SRPs exist —
|
|
73
|
+
* the server returns the current canonical for the entire pairing group
|
|
74
|
+
* from any single SRP login.
|
|
75
|
+
*
|
|
76
|
+
* @returns The refreshed canonical profile ID.
|
|
77
|
+
*/
|
|
78
|
+
export type AuthenticationControllerRefreshCanonicalProfileIdAction = {
|
|
79
|
+
type: `AuthenticationController:refreshCanonicalProfileId`;
|
|
80
|
+
handler: AuthenticationController['refreshCanonicalProfileId'];
|
|
81
|
+
};
|
|
36
82
|
export type AuthenticationControllerGetUserProfileLineageAction = {
|
|
37
83
|
type: `AuthenticationController:getUserProfileLineage`;
|
|
38
84
|
handler: AuthenticationController['getUserProfileLineage'];
|
|
@@ -44,5 +90,5 @@ export type AuthenticationControllerIsSignedInAction = {
|
|
|
44
90
|
/**
|
|
45
91
|
* Union of all AuthenticationController action types.
|
|
46
92
|
*/
|
|
47
|
-
export type AuthenticationControllerMethodActions = AuthenticationControllerPerformSignInAction | AuthenticationControllerPerformSignOutAction | AuthenticationControllerGetBearerTokenAction | AuthenticationControllerGetSessionProfileAction | AuthenticationControllerGetUserProfileLineageAction | AuthenticationControllerIsSignedInAction;
|
|
93
|
+
export type AuthenticationControllerMethodActions = AuthenticationControllerPerformSignInAction | AuthenticationControllerPerformProfilePairingAction | AuthenticationControllerPerformSignOutAction | AuthenticationControllerGetBearerTokenAction | AuthenticationControllerGetSessionProfileAction | AuthenticationControllerRefreshCanonicalProfileIdAction | AuthenticationControllerGetUserProfileLineageAction | AuthenticationControllerIsSignedInAction;
|
|
48
94
|
//# sourceMappingURL=AuthenticationController-method-action-types.d.mts.map
|
package/dist/controllers/authentication/AuthenticationController-method-action-types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController-method-action-types.d.mts","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,uCAAmC;AAE3E,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,wBAAwB,CAAC,eAAe,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"AuthenticationController-method-action-types.d.mts","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,uCAAmC;AAE3E,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,wBAAwB,CAAC,eAAe,CAAC,CAAC;CACpD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,wBAAwB,CAAC,uBAAuB,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;CACrD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,+CAA+C,GAAG;IAC5D,IAAI,EAAE,4CAA4C,CAAC;IACnD,OAAO,EAAE,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;CACxD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,uDAAuD,GAAG;IACpE,IAAI,EAAE,oDAAoD,CAAC;IAC3D,OAAO,EAAE,wBAAwB,CAAC,2BAA2B,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,wBAAwB,CAAC,uBAAuB,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,wBAAwB,CAAC,YAAY,CAAC,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qCAAqC,GAC7C,2CAA2C,GAC3C,mDAAmD,GACnD,4CAA4C,GAC5C,4CAA4C,GAC5C,+CAA+C,GAC/C,uDAAuD,GACvD,mDAAmD,GACnD,wCAAwC,CAAC"}
|
package/dist/controllers/authentication/AuthenticationController-method-action-types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController-method-action-types.mjs","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticationController } from './AuthenticationController';\n\nexport type AuthenticationControllerPerformSignInAction = {\n type: `AuthenticationController:performSignIn`;\n handler: AuthenticationController['performSignIn'];\n};\n\nexport type AuthenticationControllerPerformSignOutAction = {\n type: `AuthenticationController:performSignOut`;\n handler: AuthenticationController['performSignOut'];\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"AuthenticationController-method-action-types.mjs","sourceRoot":"","sources":["../../../src/controllers/authentication/AuthenticationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticationController } from './AuthenticationController';\n\nexport type AuthenticationControllerPerformSignInAction = {\n type: `AuthenticationController:performSignIn`;\n handler: AuthenticationController['performSignIn'];\n};\n\n/**\n * Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the\n * canonical profile ID into every cached SRP session, and emits\n * `AuthenticationController:profileSignIn` when the canonical changes or\n * new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.\n *\n * No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when\n * the wallet is locked.\n *\n * Pairing failures are swallowed so the caller (typically the client-side\n * `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.\n */\nexport type AuthenticationControllerPerformProfilePairingAction = {\n type: `AuthenticationController:performProfilePairing`;\n handler: AuthenticationController['performProfilePairing'];\n};\n\nexport type AuthenticationControllerPerformSignOutAction = {\n type: `AuthenticationController:performSignOut`;\n handler: AuthenticationController['performSignOut'];\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 */\nexport type AuthenticationControllerGetBearerTokenAction = {\n type: `AuthenticationController:getBearerToken`;\n handler: AuthenticationController['getBearerToken'];\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 */\nexport type AuthenticationControllerGetSessionProfileAction = {\n type: `AuthenticationController:getSessionProfile`;\n handler: AuthenticationController['getSessionProfile'];\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 */\nexport type AuthenticationControllerRefreshCanonicalProfileIdAction = {\n type: `AuthenticationController:refreshCanonicalProfileId`;\n handler: AuthenticationController['refreshCanonicalProfileId'];\n};\n\nexport type AuthenticationControllerGetUserProfileLineageAction = {\n type: `AuthenticationController:getUserProfileLineage`;\n handler: AuthenticationController['getUserProfileLineage'];\n};\n\nexport type AuthenticationControllerIsSignedInAction = {\n type: `AuthenticationController:isSignedIn`;\n handler: AuthenticationController['isSignedIn'];\n};\n\n/**\n * Union of all AuthenticationController action types.\n */\nexport type AuthenticationControllerMethodActions =\n | AuthenticationControllerPerformSignInAction\n | AuthenticationControllerPerformProfilePairingAction\n | AuthenticationControllerPerformSignOutAction\n | AuthenticationControllerGetBearerTokenAction\n | AuthenticationControllerGetSessionProfileAction\n | AuthenticationControllerRefreshCanonicalProfileIdAction\n | AuthenticationControllerGetUserProfileLineageAction\n | AuthenticationControllerIsSignedInAction;\n"]}
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _AuthenticationController_instances, _AuthenticationController_metametrics, _AuthenticationController_auth, _AuthenticationController_config, _AuthenticationController_isUnlocked, _AuthenticationController_cachedPrimaryEntropySourceId, _AuthenticationController_keyringController, _AuthenticationController_getLoginResponseFromState, _AuthenticationController_setLoginResponseToState, _AuthenticationController_assertIsUnlocked, _AuthenticationController_getPrimaryEntropySourceId, _AuthenticationController_snapGetPublicKey, _AuthenticationController_snapGetAllPublicKeys, _AuthenticationController__snapSignMessageCache, _AuthenticationController_snapSignMessage;
|
|
13
|
+
var _AuthenticationController_instances, _AuthenticationController_metametrics, _AuthenticationController_auth, _AuthenticationController_config, _AuthenticationController_isUnlocked, _AuthenticationController_cachedPrimaryEntropySourceId, _AuthenticationController_keyringController, _AuthenticationController_getLoginResponseFromState, _AuthenticationController_setLoginResponseToState, _AuthenticationController_assertIsUnlocked, _AuthenticationController_getPrimaryEntropySourceId, _AuthenticationController_pairSrpProfiles, _AuthenticationController_propagateCanonical, _AuthenticationController_getCanonicalProfileId, _AuthenticationController_invalidateSrpSession, _AuthenticationController_snapGetPublicKey, _AuthenticationController_snapGetAllPublicKeys, _AuthenticationController__snapSignMessageCache, _AuthenticationController_snapSignMessage;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AuthenticationController = exports.defaultState = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -19,6 +19,7 @@ const auth_snap_requests_1 = require("./auth-snap-requests.cjs");
|
|
|
19
19
|
const controllerName = 'AuthenticationController';
|
|
20
20
|
exports.defaultState = {
|
|
21
21
|
isSignedIn: false,
|
|
22
|
+
hasPairedAtLeastOnce: false,
|
|
22
23
|
};
|
|
23
24
|
const metadata = {
|
|
24
25
|
isSignedIn: {
|
|
@@ -27,6 +28,12 @@ const metadata = {
|
|
|
27
28
|
includeInDebugSnapshot: true,
|
|
28
29
|
usedInUi: true,
|
|
29
30
|
},
|
|
31
|
+
hasPairedAtLeastOnce: {
|
|
32
|
+
includeInStateLogs: true,
|
|
33
|
+
persist: true,
|
|
34
|
+
includeInDebugSnapshot: true,
|
|
35
|
+
usedInUi: true,
|
|
36
|
+
},
|
|
30
37
|
srpSessionData: {
|
|
31
38
|
// Remove access token from state logs
|
|
32
39
|
includeInStateLogs: (srpSessionData) => {
|
|
@@ -55,8 +62,10 @@ const metadata = {
|
|
|
55
62
|
const MESSENGER_EXPOSED_METHODS = [
|
|
56
63
|
'performSignIn',
|
|
57
64
|
'performSignOut',
|
|
65
|
+
'performProfilePairing',
|
|
58
66
|
'getBearerToken',
|
|
59
67
|
'getSessionProfile',
|
|
68
|
+
'refreshCanonicalProfileId',
|
|
60
69
|
'getUserProfileLineage',
|
|
61
70
|
'isSignedIn',
|
|
62
71
|
];
|
|
@@ -131,6 +140,52 @@ class AuthenticationController extends base_controller_1.BaseController {
|
|
|
131
140
|
}
|
|
132
141
|
return accessTokens;
|
|
133
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the
|
|
145
|
+
* canonical profile ID into every cached SRP session, and emits
|
|
146
|
+
* `AuthenticationController:profileSignIn` when the canonical changes or
|
|
147
|
+
* new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.
|
|
148
|
+
*
|
|
149
|
+
* No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when
|
|
150
|
+
* the wallet is locked.
|
|
151
|
+
*
|
|
152
|
+
* Pairing failures are swallowed so the caller (typically the client-side
|
|
153
|
+
* `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.
|
|
154
|
+
*/
|
|
155
|
+
async performProfilePairing() {
|
|
156
|
+
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_assertIsUnlocked).call(this, 'performProfilePairing');
|
|
157
|
+
const allPublicKeys = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_snapGetAllPublicKeys).call(this);
|
|
158
|
+
if (allPublicKeys.length < 2) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const previousCanonical = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getCanonicalProfileId).call(this);
|
|
162
|
+
const accessTokens = [];
|
|
163
|
+
for (const [entropySourceId] of allPublicKeys) {
|
|
164
|
+
accessTokens.push(await __classPrivateFieldGet(this, _AuthenticationController_auth, "f").getAccessToken(entropySourceId));
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const profileAliases = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_pairSrpProfiles).call(this, accessTokens);
|
|
168
|
+
const newCanonical = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getCanonicalProfileId).call(this);
|
|
169
|
+
if (!newCanonical) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.update((state) => {
|
|
173
|
+
state.hasPairedAtLeastOnce = true;
|
|
174
|
+
});
|
|
175
|
+
const profileIdChanged = previousCanonical !== newCanonical;
|
|
176
|
+
const shouldEmitProfileSignInEvent = profileIdChanged || profileAliases.length > 0;
|
|
177
|
+
if (shouldEmitProfileSignInEvent) {
|
|
178
|
+
this.messenger.publish('AuthenticationController:profileSignIn', {
|
|
179
|
+
profileId: newCanonical,
|
|
180
|
+
profileAliases,
|
|
181
|
+
profileIdChanged,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Non-fatal — caller re-invokes on the next trigger.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
134
189
|
performSignOut() {
|
|
135
190
|
__classPrivateFieldSet(this, _AuthenticationController_cachedPrimaryEntropySourceId, undefined, "f");
|
|
136
191
|
this.update((state) => {
|
|
@@ -139,10 +194,15 @@ class AuthenticationController extends base_controller_1.BaseController {
|
|
|
139
194
|
});
|
|
140
195
|
}
|
|
141
196
|
/**
|
|
142
|
-
*
|
|
143
|
-
* Logs a user in if a user is not logged in.
|
|
197
|
+
* Returns a bearer token for the specified SRP, logging in if needed.
|
|
144
198
|
*
|
|
145
|
-
*
|
|
199
|
+
* When called without `entropySourceId`, returns the primary (first) SRP's
|
|
200
|
+
* access token, which is effectively the canonical
|
|
201
|
+
* profile's token that can be used by alias-aware consumers for cross-SRP
|
|
202
|
+
* operations.
|
|
203
|
+
*
|
|
204
|
+
* @param entropySourceId - The entropy source ID. Omit for the primary SRP.
|
|
205
|
+
* @returns The OIDC access token.
|
|
146
206
|
*/
|
|
147
207
|
async getBearerToken(entropySourceId) {
|
|
148
208
|
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_assertIsUnlocked).call(this, 'getBearerToken');
|
|
@@ -150,8 +210,13 @@ class AuthenticationController extends base_controller_1.BaseController {
|
|
|
150
210
|
return await __classPrivateFieldGet(this, _AuthenticationController_auth, "f").getAccessToken(resolvedId);
|
|
151
211
|
}
|
|
152
212
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
213
|
+
* Returns the cached session profile, logging in if no session exists.
|
|
214
|
+
*
|
|
215
|
+
* The returned `canonicalProfileId` reflects the value from the most recent
|
|
216
|
+
* login or pairing. In the rare event where a canonical changed because of
|
|
217
|
+
* a pairing that happened on another device, the cached value may be stale
|
|
218
|
+
* until the next login. For guaranteed freshness, call
|
|
219
|
+
* `refreshCanonicalProfileId()` before reading `canonicalProfileId`.
|
|
155
220
|
*
|
|
156
221
|
* @param entropySourceId - The entropy source ID used to derive the key,
|
|
157
222
|
* when multiple sources are available (Multi-SRP).
|
|
@@ -162,6 +227,34 @@ class AuthenticationController extends base_controller_1.BaseController {
|
|
|
162
227
|
const resolvedId = entropySourceId ?? (await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getPrimaryEntropySourceId).call(this));
|
|
163
228
|
return await __classPrivateFieldGet(this, _AuthenticationController_auth, "f").getUserProfile(resolvedId);
|
|
164
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Forces a fresh retrieval of the canonical profile ID from the server
|
|
232
|
+
* and propagates it to all cached SRP sessions.
|
|
233
|
+
*
|
|
234
|
+
* This method invalidates the primary SRP's cached session and forces a
|
|
235
|
+
* re-login. Use it before operations that require a guaranteed-fresh
|
|
236
|
+
* canonical (e.g. storage key derivation for Accounts ADR 0005). For
|
|
237
|
+
* best-effort reads, use
|
|
238
|
+
* `getSessionProfile().canonicalProfileId` instead.
|
|
239
|
+
*
|
|
240
|
+
* Only the primary SRP is re-logged-in regardless of how many SRPs exist —
|
|
241
|
+
* the server returns the current canonical for the entire pairing group
|
|
242
|
+
* from any single SRP login.
|
|
243
|
+
*
|
|
244
|
+
* @returns The refreshed canonical profile ID.
|
|
245
|
+
*/
|
|
246
|
+
async refreshCanonicalProfileId() {
|
|
247
|
+
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_assertIsUnlocked).call(this, 'refreshCanonicalProfileId');
|
|
248
|
+
const primaryEntropySourceId = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getPrimaryEntropySourceId).call(this);
|
|
249
|
+
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_invalidateSrpSession).call(this, primaryEntropySourceId);
|
|
250
|
+
await __classPrivateFieldGet(this, _AuthenticationController_auth, "f").getAccessToken(primaryEntropySourceId);
|
|
251
|
+
const canonical = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getCanonicalProfileId).call(this);
|
|
252
|
+
if (!canonical) {
|
|
253
|
+
throw new Error('refreshCanonicalProfileId - Unable to resolve canonical profile ID');
|
|
254
|
+
}
|
|
255
|
+
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_propagateCanonical).call(this, canonical);
|
|
256
|
+
return canonical;
|
|
257
|
+
}
|
|
165
258
|
async getUserProfileLineage(entropySourceId) {
|
|
166
259
|
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_assertIsUnlocked).call(this, 'getUserProfileLineage');
|
|
167
260
|
const resolvedId = entropySourceId ?? (await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getPrimaryEntropySourceId).call(this));
|
|
@@ -212,6 +305,50 @@ _AuthenticationController_metametrics = new WeakMap(), _AuthenticationController
|
|
|
212
305
|
}
|
|
213
306
|
__classPrivateFieldSet(this, _AuthenticationController_cachedPrimaryEntropySourceId, primaryId, "f");
|
|
214
307
|
return __classPrivateFieldGet(this, _AuthenticationController_cachedPrimaryEntropySourceId, "f");
|
|
308
|
+
}, _AuthenticationController_pairSrpProfiles = async function _AuthenticationController_pairSrpProfiles(accessTokens) {
|
|
309
|
+
if (accessTokens.length < 2) {
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
const primaryAccessToken = accessTokens[0]; // Associated with primary SRP.
|
|
313
|
+
const { profileAliases, profile: { canonicalProfileId }, } = await __classPrivateFieldGet(this, _AuthenticationController_auth, "f").pairSrpProfiles(accessTokens, primaryAccessToken);
|
|
314
|
+
__classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_propagateCanonical).call(this, canonicalProfileId);
|
|
315
|
+
return profileAliases;
|
|
316
|
+
}, _AuthenticationController_propagateCanonical = function _AuthenticationController_propagateCanonical(canonicalProfileId) {
|
|
317
|
+
const { srpSessionData } = this.state;
|
|
318
|
+
if (!srpSessionData) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
this.update((state) => {
|
|
322
|
+
for (const entry of Object.values(state.srpSessionData ?? {})) {
|
|
323
|
+
if (entry?.profile) {
|
|
324
|
+
entry.profile.canonicalProfileId = canonicalProfileId;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}, _AuthenticationController_getCanonicalProfileId =
|
|
329
|
+
/**
|
|
330
|
+
* Returns the canonical profile id from the primary SRP's cached session.
|
|
331
|
+
* Returns `null` when no session exists yet for the primary SRP.
|
|
332
|
+
*
|
|
333
|
+
* Always reads from the primary SRP because the canonical is shared across
|
|
334
|
+
* all paired SRPs after `#propagateCanonical`.
|
|
335
|
+
*
|
|
336
|
+
* @returns The canonical profile id, or `null` if unavailable.
|
|
337
|
+
*/
|
|
338
|
+
async function _AuthenticationController_getCanonicalProfileId() {
|
|
339
|
+
const primaryEntropySourceId = await __classPrivateFieldGet(this, _AuthenticationController_instances, "m", _AuthenticationController_getPrimaryEntropySourceId).call(this);
|
|
340
|
+
return (this.state.srpSessionData?.[primaryEntropySourceId]?.profile
|
|
341
|
+
?.canonicalProfileId ?? null);
|
|
342
|
+
}, _AuthenticationController_invalidateSrpSession = function _AuthenticationController_invalidateSrpSession(entropySourceId) {
|
|
343
|
+
this.update((state) => {
|
|
344
|
+
const entry = state.srpSessionData?.[entropySourceId];
|
|
345
|
+
if (entry?.profile) {
|
|
346
|
+
// Setting canonicalProfileId to '' forces a re-fetch on the next
|
|
347
|
+
// #getAuthSession call. The falsy check (!auth.profile.canonicalProfileId)
|
|
348
|
+
// treats '' the same as undefined/null — all signal an invalid session.
|
|
349
|
+
entry.profile.canonicalProfileId = '';
|
|
350
|
+
}
|
|
351
|
+
});
|
|
215
352
|
}, _AuthenticationController_snapGetPublicKey =
|
|
216
353
|
/**
|
|
217
354
|
* Returns the auth snap public key.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthenticationController.cjs","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 {\n KeyringControllerGetStateAction,\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type { Json } from '@metamask/utils';\n\nimport type {\n LoginResponse,\n SRPInterface,\n UserProfile,\n UserProfileLineage,\n} from '../../sdk';\nimport {\n assertMessageStartsWithMetamask,\n AuthType,\n Env,\n JwtBearerAuth,\n} from '../../sdk';\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport {\n createSnapPublicKeyRequest,\n createSnapAllPublicKeysRequest,\n createSnapSignMessageRequest,\n} from './auth-snap-requests';\nimport { AuthenticationControllerMethodActions } from './AuthenticationController-method-action-types';\n\nconst controllerName = 'AuthenticationController';\n\n// State\nexport type AuthenticationControllerState = {\n isSignedIn: boolean;\n srpSessionData?: Record<string, LoginResponse>;\n};\nexport const defaultState: AuthenticationControllerState = {\n isSignedIn: false,\n};\nconst metadata: StateMetadata<AuthenticationControllerState> = {\n isSignedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n srpSessionData: {\n // Remove access token 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 sanitizedSrpSessionData[key] = {\n ...value,\n token: tokenWithoutAccessToken,\n };\n return sanitizedSrpSessionData;\n },\n {},\n );\n },\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\ntype ControllerConfig = {\n env: Env;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'performSignIn',\n 'performSignOut',\n 'getBearerToken',\n 'getSessionProfile',\n 'getUserProfileLineage',\n 'isSignedIn',\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 Events = AuthenticationControllerStateChangeEvent;\n\n// Allowed Actions\ntype AllowedActions =\n | KeyringControllerGetStateAction\n | SnapControllerHandleRequestAction;\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 #config: ControllerConfig = {\n env: Env.PRD,\n };\n\n #isUnlocked = false;\n\n #cachedPrimaryEntropySourceId?: 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.#isUnlocked = false;\n });\n },\n };\n\n constructor({\n messenger,\n state,\n config,\n metametrics,\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 }) {\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 this.#config = {\n ...this.#config,\n ...config,\n };\n\n this.#metametrics = metametrics;\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.#snapGetPublicKey.bind(this),\n signMessage: this.#snapSignMessage.bind(this),\n },\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 =\n entropySourceId ?? (await 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 =\n entropySourceId ?? (await 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 state.srpSessionData[resolvedId] = {\n ...loginResponse,\n profile: {\n ...loginResponse.profile,\n metaMetricsId,\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 async #getPrimaryEntropySourceId(): Promise<string> {\n if (this.#cachedPrimaryEntropySourceId) {\n return this.#cachedPrimaryEntropySourceId;\n }\n const allPublicKeys = await this.#snapGetAllPublicKeys();\n\n if (allPublicKeys.length === 0) {\n throw new Error(\n '#getPrimaryEntropySourceId - No entropy sources found from snap',\n );\n }\n\n const primaryId = allPublicKeys[0][0];\n if (!primaryId) {\n throw new Error(\n '#getPrimaryEntropySourceId - Primary entropy source ID is undefined',\n );\n }\n\n this.#cachedPrimaryEntropySourceId = primaryId;\n return this.#cachedPrimaryEntropySourceId;\n }\n\n public async performSignIn(): Promise<string[]> {\n this.#assertIsUnlocked('performSignIn');\n\n const allPublicKeys = await this.#snapGetAllPublicKeys();\n const accessTokens = [];\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 allPublicKeys) {\n const accessToken = await this.#auth.getAccessToken(entropySourceId);\n accessTokens.push(accessToken);\n }\n\n return accessTokens;\n }\n\n public performSignOut(): void {\n this.#cachedPrimaryEntropySourceId = undefined;\n this.update((state) => {\n state.isSignedIn = false;\n state.srpSessionData = undefined;\n });\n }\n\n /**\n * Will return a bearer token.\n * Logs a user in if a user is not logged in.\n *\n * @returns profile for the session.\n */\n\n public async getBearerToken(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('getBearerToken');\n const resolvedId =\n entropySourceId ?? (await this.#getPrimaryEntropySourceId());\n return await this.#auth.getAccessToken(resolvedId);\n }\n\n /**\n * Will return a session profile.\n * Logs a user in if a user is not logged in.\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 =\n entropySourceId ?? (await this.#getPrimaryEntropySourceId());\n return await this.#auth.getUserProfile(resolvedId);\n }\n\n public async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n this.#assertIsUnlocked('getUserProfileLineage');\n const resolvedId =\n entropySourceId ?? (await this.#getPrimaryEntropySourceId());\n return await this.#auth.getUserProfileLineage(resolvedId);\n }\n\n public isSignedIn(): boolean {\n return this.state.isSignedIn;\n }\n\n /**\n * Returns the auth snap public key.\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns The snap public key.\n */\n async #snapGetPublicKey(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('#snapGetPublicKey');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapPublicKeyRequest(entropySourceId),\n )) as string;\n\n return result;\n }\n\n /**\n * Returns a mapping of entropy source IDs to auth snap public keys.\n *\n * @returns A mapping of entropy source IDs to public keys.\n */\n async #snapGetAllPublicKeys(): Promise<[string, string][]> {\n this.#assertIsUnlocked('#snapGetAllPublicKeys');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapAllPublicKeysRequest(),\n )) as [string, string][];\n\n return result;\n }\n\n #_snapSignMessageCache: Record<`metamask:${string}`, string> = {};\n\n /**\n * Signs a specific message using an underlying auth snap.\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 A Signature created by the snap.\n */\n async #snapSignMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n assertMessageStartsWithMetamask(message);\n\n if (this.#_snapSignMessageCache[message]) {\n return this.#_snapSignMessageCache[message];\n }\n\n this.#assertIsUnlocked('#snapSignMessage');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapSignMessageRequest(message, entropySourceId),\n )) as string;\n\n this.#_snapSignMessageCache[message] = result;\n\n return result;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AuthenticationController.cjs","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 {\n KeyringControllerGetStateAction,\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type { Json } from '@metamask/utils';\n\nimport type {\n LoginResponse,\n ProfileAlias,\n SRPInterface,\n UserProfile,\n UserProfileLineage,\n} from '../../sdk';\nimport {\n assertMessageStartsWithMetamask,\n AuthType,\n Env,\n JwtBearerAuth,\n} from '../../sdk';\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport {\n createSnapPublicKeyRequest,\n createSnapAllPublicKeysRequest,\n createSnapSignMessageRequest,\n} from './auth-snap-requests';\nimport { AuthenticationControllerMethodActions } from './AuthenticationController-method-action-types';\n\nconst controllerName = 'AuthenticationController';\n\n// State\nexport type AuthenticationControllerState = {\n isSignedIn: boolean;\n srpSessionData?: Record<string, LoginResponse>;\n /**\n * Whether `performProfilePairing` has ever completed successfully on this\n * device.\n * Monotonic — only flips from `false` to `true`, never back. Used by the\n * client-side `useAutoProfilePairing` hook to decide when to fire the initial\n * pairing call after install/upgrade. Subsequent re-pairs (e.g. when a\n * new SRP is added later) are also fired by the hook based on keyring\n * changes, independently of this flag.\n */\n hasPairedAtLeastOnce?: boolean;\n};\nexport const defaultState: AuthenticationControllerState = {\n isSignedIn: false,\n hasPairedAtLeastOnce: false,\n};\nconst metadata: StateMetadata<AuthenticationControllerState> = {\n isSignedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n hasPairedAtLeastOnce: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n srpSessionData: {\n // Remove access token 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 sanitizedSrpSessionData[key] = {\n ...value,\n token: tokenWithoutAccessToken,\n };\n return sanitizedSrpSessionData;\n },\n {},\n );\n },\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\ntype ControllerConfig = {\n env: Env;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'performSignIn',\n 'performSignOut',\n 'performProfilePairing',\n 'getBearerToken',\n 'getSessionProfile',\n 'refreshCanonicalProfileId',\n 'getUserProfileLineage',\n 'isSignedIn',\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 | SnapControllerHandleRequestAction;\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 #config: ControllerConfig = {\n env: Env.PRD,\n };\n\n #isUnlocked = false;\n\n #cachedPrimaryEntropySourceId?: 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.#isUnlocked = false;\n });\n },\n };\n\n constructor({\n messenger,\n state,\n config,\n metametrics,\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 }) {\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 this.#config = {\n ...this.#config,\n ...config,\n };\n\n this.#metametrics = metametrics;\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.#snapGetPublicKey.bind(this),\n signMessage: this.#snapSignMessage.bind(this),\n },\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 =\n entropySourceId ?? (await 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 =\n entropySourceId ?? (await 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 state.srpSessionData[resolvedId] = {\n ...loginResponse,\n profile: {\n ...loginResponse.profile,\n metaMetricsId,\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 async #getPrimaryEntropySourceId(): Promise<string> {\n if (this.#cachedPrimaryEntropySourceId) {\n return this.#cachedPrimaryEntropySourceId;\n }\n const allPublicKeys = await this.#snapGetAllPublicKeys();\n\n if (allPublicKeys.length === 0) {\n throw new Error(\n '#getPrimaryEntropySourceId - No entropy sources found from snap',\n );\n }\n\n const primaryId = allPublicKeys[0][0];\n if (!primaryId) {\n throw new Error(\n '#getPrimaryEntropySourceId - Primary entropy source ID is undefined',\n );\n }\n\n this.#cachedPrimaryEntropySourceId = primaryId;\n return this.#cachedPrimaryEntropySourceId;\n }\n\n public async performSignIn(): Promise<string[]> {\n this.#assertIsUnlocked('performSignIn');\n\n const allPublicKeys = await this.#snapGetAllPublicKeys();\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 allPublicKeys) {\n const accessToken = await this.#auth.getAccessToken(entropySourceId);\n accessTokens.push(accessToken);\n }\n\n return accessTokens;\n }\n\n /**\n * Pairs all SRPs of the wallet via `POST /profile/pair`, propagates the\n * canonical profile ID into every cached SRP session, and emits\n * `AuthenticationController:profileSignIn` when the canonical changes or\n * new aliases are returned. Sets `hasPairedAtLeastOnce = true` on success.\n *\n * No-op when the wallet has fewer than 2 SRPs (nothing to pair) or when\n * the wallet is locked.\n *\n * Pairing failures are swallowed so the caller (typically the client-side\n * `useAutoProfilePairing` hook) can simply re-invoke on the next trigger.\n */\n public async performProfilePairing(): Promise<void> {\n this.#assertIsUnlocked('performProfilePairing');\n\n const allPublicKeys = await this.#snapGetAllPublicKeys();\n if (allPublicKeys.length < 2) {\n return;\n }\n\n const previousCanonical = await this.#getCanonicalProfileId();\n\n const accessTokens: string[] = [];\n for (const [entropySourceId] of allPublicKeys) {\n accessTokens.push(await this.#auth.getAccessToken(entropySourceId));\n }\n\n try {\n const profileAliases = await this.#pairSrpProfiles(accessTokens);\n const newCanonical = await this.#getCanonicalProfileId();\n\n if (!newCanonical) {\n return;\n }\n\n this.update((state) => {\n state.hasPairedAtLeastOnce = true;\n });\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 } catch {\n // Non-fatal — caller re-invokes on the next trigger.\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 },\n } = await this.#auth.pairSrpProfiles(accessTokens, primaryAccessToken);\n this.#propagateCanonical(canonicalProfileId);\n return profileAliases;\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 async #getCanonicalProfileId(): Promise<string | null> {\n const primaryEntropySourceId = await this.#getPrimaryEntropySourceId();\n return (\n this.state.srpSessionData?.[primaryEntropySourceId]?.profile\n ?.canonicalProfileId ?? null\n );\n }\n\n public performSignOut(): void {\n this.#cachedPrimaryEntropySourceId = undefined;\n this.update((state) => {\n state.isSignedIn = false;\n state.srpSessionData = undefined;\n });\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 =\n entropySourceId ?? (await 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 =\n entropySourceId ?? (await 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 = await this.#getPrimaryEntropySourceId();\n this.#invalidateSrpSession(primaryEntropySourceId);\n await this.#auth.getAccessToken(primaryEntropySourceId);\n\n const canonical = await 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 =\n entropySourceId ?? (await this.#getPrimaryEntropySourceId());\n return await this.#auth.getUserProfileLineage(resolvedId);\n }\n\n public isSignedIn(): boolean {\n return this.state.isSignedIn;\n }\n\n /**\n * Returns the auth snap public key.\n *\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n * @returns The snap public key.\n */\n async #snapGetPublicKey(entropySourceId?: string): Promise<string> {\n this.#assertIsUnlocked('#snapGetPublicKey');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapPublicKeyRequest(entropySourceId),\n )) as string;\n\n return result;\n }\n\n /**\n * Returns a mapping of entropy source IDs to auth snap public keys.\n *\n * @returns A mapping of entropy source IDs to public keys.\n */\n async #snapGetAllPublicKeys(): Promise<[string, string][]> {\n this.#assertIsUnlocked('#snapGetAllPublicKeys');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapAllPublicKeysRequest(),\n )) as [string, string][];\n\n return result;\n }\n\n #_snapSignMessageCache: Record<`metamask:${string}`, string> = {};\n\n /**\n * Signs a specific message using an underlying auth snap.\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 A Signature created by the snap.\n */\n async #snapSignMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n assertMessageStartsWithMetamask(message);\n\n if (this.#_snapSignMessageCache[message]) {\n return this.#_snapSignMessageCache[message];\n }\n\n this.#assertIsUnlocked('#snapSignMessage');\n\n const result = (await this.messenger.call(\n 'SnapController:handleRequest',\n createSnapSignMessageRequest(message, entropySourceId),\n )) as string;\n\n this.#_snapSignMessageCache[message] = result;\n\n return result;\n }\n}\n"]}
|