@metamask-previews/profile-sync-controller 28.0.2-preview-45a82ea8e → 28.0.2-preview-d8ff44d

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +17 -2
  2. package/dist/controllers/authentication/AuthenticationController-method-action-types.cjs.map +1 -1
  3. package/dist/controllers/authentication/AuthenticationController-method-action-types.d.cts +36 -6
  4. package/dist/controllers/authentication/AuthenticationController-method-action-types.d.cts.map +1 -1
  5. package/dist/controllers/authentication/AuthenticationController-method-action-types.d.mts +36 -6
  6. package/dist/controllers/authentication/AuthenticationController-method-action-types.d.mts.map +1 -1
  7. package/dist/controllers/authentication/AuthenticationController-method-action-types.mjs.map +1 -1
  8. package/dist/controllers/authentication/AuthenticationController.cjs +111 -6
  9. package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
  10. package/dist/controllers/authentication/AuthenticationController.d.cts +43 -7
  11. package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
  12. package/dist/controllers/authentication/AuthenticationController.d.mts +43 -7
  13. package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
  14. package/dist/controllers/authentication/AuthenticationController.mjs +111 -6
  15. package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
  16. package/dist/controllers/authentication/index.cjs.map +1 -1
  17. package/dist/controllers/authentication/index.d.cts +1 -1
  18. package/dist/controllers/authentication/index.d.cts.map +1 -1
  19. package/dist/controllers/authentication/index.d.mts +1 -1
  20. package/dist/controllers/authentication/index.d.mts.map +1 -1
  21. package/dist/controllers/authentication/index.mjs.map +1 -1
  22. package/dist/controllers/authentication/mocks/mockResponses.cjs +10 -1
  23. package/dist/controllers/authentication/mocks/mockResponses.cjs.map +1 -1
  24. package/dist/controllers/authentication/mocks/mockResponses.d.cts +17 -32
  25. package/dist/controllers/authentication/mocks/mockResponses.d.cts.map +1 -1
  26. package/dist/controllers/authentication/mocks/mockResponses.d.mts +17 -32
  27. package/dist/controllers/authentication/mocks/mockResponses.d.mts.map +1 -1
  28. package/dist/controllers/authentication/mocks/mockResponses.mjs +9 -1
  29. package/dist/controllers/authentication/mocks/mockResponses.mjs.map +1 -1
  30. package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs +28 -1
  31. package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs.map +1 -1
  32. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts +2 -0
  33. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts.map +1 -1
  34. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts +2 -0
  35. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts.map +1 -1
  36. package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs +29 -2
  37. package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs.map +1 -1
  38. package/dist/sdk/authentication-jwt-bearer/services.cjs +59 -1
  39. package/dist/sdk/authentication-jwt-bearer/services.cjs.map +1 -1
  40. package/dist/sdk/authentication-jwt-bearer/services.d.cts +17 -1
  41. package/dist/sdk/authentication-jwt-bearer/services.d.cts.map +1 -1
  42. package/dist/sdk/authentication-jwt-bearer/services.d.mts +17 -1
  43. package/dist/sdk/authentication-jwt-bearer/services.d.mts.map +1 -1
  44. package/dist/sdk/authentication-jwt-bearer/services.mjs +56 -0
  45. package/dist/sdk/authentication-jwt-bearer/services.mjs.map +1 -1
  46. package/dist/sdk/authentication-jwt-bearer/types.cjs.map +1 -1
  47. package/dist/sdk/authentication-jwt-bearer/types.d.cts +20 -1
  48. package/dist/sdk/authentication-jwt-bearer/types.d.cts.map +1 -1
  49. package/dist/sdk/authentication-jwt-bearer/types.d.mts +20 -1
  50. package/dist/sdk/authentication-jwt-bearer/types.d.mts.map +1 -1
  51. package/dist/sdk/authentication-jwt-bearer/types.mjs.map +1 -1
  52. package/dist/sdk/authentication-jwt-bearer/utils/identifier.cjs +27 -0
  53. package/dist/sdk/authentication-jwt-bearer/utils/identifier.cjs.map +1 -0
  54. package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.cts +13 -0
  55. package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.cts.map +1 -0
  56. package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.mts +13 -0
  57. package/dist/sdk/authentication-jwt-bearer/utils/identifier.d.mts.map +1 -0
  58. package/dist/sdk/authentication-jwt-bearer/utils/identifier.mjs +23 -0
  59. package/dist/sdk/authentication-jwt-bearer/utils/identifier.mjs.map +1 -0
  60. package/dist/sdk/authentication.cjs +4 -0
  61. package/dist/sdk/authentication.cjs.map +1 -1
  62. package/dist/sdk/authentication.d.cts +2 -0
  63. package/dist/sdk/authentication.d.cts.map +1 -1
  64. package/dist/sdk/authentication.d.mts +2 -0
  65. package/dist/sdk/authentication.d.mts.map +1 -1
  66. package/dist/sdk/authentication.mjs +4 -0
  67. package/dist/sdk/authentication.mjs.map +1 -1
  68. package/dist/sdk/mocks/auth.cjs +11 -1
  69. package/dist/sdk/mocks/auth.cjs.map +1 -1
  70. package/dist/sdk/mocks/auth.d.cts +10 -0
  71. package/dist/sdk/mocks/auth.d.cts.map +1 -1
  72. package/dist/sdk/mocks/auth.d.mts +10 -0
  73. package/dist/sdk/mocks/auth.d.mts.map +1 -1
  74. package/dist/sdk/mocks/auth.mjs +11 -1
  75. package/dist/sdk/mocks/auth.mjs.map +1 -1
  76. package/dist/sdk/user-storage.cjs +26 -3
  77. package/dist/sdk/user-storage.cjs.map +1 -1
  78. package/dist/sdk/user-storage.d.cts +7 -0
  79. package/dist/sdk/user-storage.d.cts.map +1 -1
  80. package/dist/sdk/user-storage.d.mts +7 -0
  81. package/dist/sdk/user-storage.d.mts.map +1 -1
  82. package/dist/sdk/user-storage.mjs +26 -3
  83. package/dist/sdk/user-storage.mjs.map +1 -1
  84. package/dist/sdk/utils/validate-pair-response.cjs +29 -0
  85. package/dist/sdk/utils/validate-pair-response.cjs.map +1 -0
  86. package/dist/sdk/utils/validate-pair-response.d.cts +26 -0
  87. package/dist/sdk/utils/validate-pair-response.d.cts.map +1 -0
  88. package/dist/sdk/utils/validate-pair-response.d.mts +26 -0
  89. package/dist/sdk/utils/validate-pair-response.d.mts.map +1 -0
  90. package/dist/sdk/utils/validate-pair-response.mjs +25 -0
  91. package/dist/sdk/utils/validate-pair-response.mjs.map +1 -0
  92. package/dist/shared/types/services.cjs.map +1 -1
  93. package/dist/shared/types/services.d.cts +7 -0
  94. package/dist/shared/types/services.d.cts.map +1 -1
  95. package/dist/shared/types/services.d.mts +7 -0
  96. package/dist/shared/types/services.d.mts.map +1 -1
  97. package/dist/shared/types/services.mjs.map +1 -1
  98. package/package.json +4 -4
@@ -1,3 +1,8 @@
1
+ type MockResponse = {
2
+ url: string;
3
+ requestMethod: 'GET' | 'POST' | 'PUT';
4
+ response: unknown;
5
+ };
1
6
  export declare const MOCK_NONCE_RESPONSE: {
2
7
  nonce: string;
3
8
  identifier: string;
@@ -5,15 +10,7 @@ export declare const MOCK_NONCE_RESPONSE: {
5
10
  };
6
11
  export declare const MOCK_NONCE: string;
7
12
  export declare const MOCK_JWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImIwNzE2N2U2LWJjNWUtNDgyZC1hNjRhLWU1MjQ0MjY2MGU3NyJ9.eyJzdWIiOiI1MzE0ODc5YWM2NDU1OGI3OTQ5ZmI4NWIzMjg2ZjZjNjUwODAzYmFiMTY0Y2QyOWNmMmM3YzdmMjMzMWMwZTRlIiwiaWF0IjoxNzA2MTEzMDYyLCJleHAiOjE3NjkxODUwNjMsImlzcyI6ImF1dGgubWV0YW1hc2suaW8iLCJhdWQiOiJwb3J0Zm9saW8ubWV0YW1hc2suaW8ifQ.E5UL6oABNweS8t5a6IBTqTf7NLOJbrhJSmEcsr7kwLp4bGvcENJzACwnsHDkA6PlzfDV09ZhAGU_F3hlS0j-erbY0k0AFR-GAtyS7E9N02D8RgUDz5oDR65CKmzM8JilgFA8UvruJ6OJGogroaOSOqzRES_s8MjHpP47RJ9lXrUesajsbOudXbuksXWg5QmWip6LLvjwr8UUzcJzNQilyIhiEpo4WdzWM4R3VtTwr4rHnWEvtYnYCov1jmI2w3YQ48y0M-3Y9IOO0ov_vlITRrOnR7Y7fRUGLUFmU5msD8mNWRywjQFLHfJJ1yNP5aJ8TkuCK3sC6kcUH335IVvukQ";
8
- export declare const getMockAuthNonceResponse: () => {
9
- url: string;
10
- requestMethod: "GET";
11
- response: (_?: unknown, path?: string, getE2ESrpIdentifierForPublicKey?: ((publicKey: string) => string) | undefined) => {
12
- nonce: string;
13
- identifier: string;
14
- expires_in: number;
15
- };
16
- };
13
+ export declare const getMockAuthNonceResponse: () => MockResponse;
17
14
  export declare const MOCK_LOGIN_RESPONSE: {
18
15
  token: string;
19
16
  expires_in: number;
@@ -24,24 +21,9 @@ export declare const MOCK_LOGIN_RESPONSE: {
24
21
  identifier_type: string;
25
22
  encrypted_storage_key: string;
26
23
  };
24
+ profile_aliases: never[];
27
25
  };
28
- export declare const getMockAuthLoginResponse: () => {
29
- url: string;
30
- requestMethod: "POST";
31
- response: (requestJsonBody?: {
32
- raw_message: string;
33
- }) => {
34
- token: string;
35
- profile: {
36
- profile_id: string;
37
- identifier_id: string;
38
- metametrics_id: string;
39
- identifier_type: string;
40
- encrypted_storage_key: string;
41
- };
42
- expires_in: number;
43
- };
44
- };
26
+ export declare const getMockAuthLoginResponse: () => MockResponse;
45
27
  export declare const MOCK_OATH_TOKEN_RESPONSE: {
46
28
  access_token: string;
47
29
  expires_in: number;
@@ -55,12 +37,15 @@ export declare const MOCK_OATH_TOKEN_RESPONSE: {
55
37
  * @returns The decoded identifier, or the original token as-is.
56
38
  */
57
39
  export declare const getE2EIdentifierFromJwt: (token: string) => string;
58
- export declare const getMockAuthAccessTokenResponse: () => {
59
- url: string;
60
- requestMethod: "POST";
61
- response: (requestJsonBody?: string) => {
62
- access_token: string;
63
- expires_in: number;
40
+ export declare const MOCK_PAIR_PROFILES_RESPONSE: {
41
+ profile: {
42
+ identifier_id: string;
43
+ metametrics_id: string;
44
+ profile_id: string;
64
45
  };
46
+ profile_aliases: never[];
65
47
  };
48
+ export declare const getMockAuthPairResponse: () => MockResponse;
49
+ export declare const getMockAuthAccessTokenResponse: () => MockResponse;
50
+ export {};
66
51
  //# sourceMappingURL=mockResponses.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.d.mts","sourceRoot":"","sources":["../../../../src/controllers/authentication/mocks/mockResponses.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,mBAAmB;;;;CAA0B,CAAC;AAC3D,eAAO,MAAM,UAAU,QAA4B,CAAC;AACpD,eAAO,MAAM,QAAQ,upBAAe,CAAC;AAErC,eAAO,MAAM,wBAAwB;;;mBAK3B,OAAO,SACJ,MAAM,iDACiC,MAAM,KAAK,MAAM;;;;;CAcpE,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;CAA8B,CAAC;AAE/D,eAAO,MAAM,wBAAwB;;;iCAMJ;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;CAgBvD,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;CAA+B,CAAC;AAoBrE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,UAAW,MAAM,KAAG,MAavD,CAAC;AAEF,eAAO,MAAM,8BAA8B;;;iCAIV,MAAM;;;;CAetC,CAAC"}
1
+ {"version":3,"file":"mockResponses.d.mts","sourceRoot":"","sources":["../../../../src/controllers/authentication/mocks/mockResponses.ts"],"names":[],"mappings":"AAYA,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;CAA0B,CAAC;AAC3D,eAAO,MAAM,UAAU,QAA4B,CAAC;AACpD,eAAO,MAAM,QAAQ,upBAAe,CAAC;AAErC,eAAO,MAAM,wBAAwB,QAAO,YAqB3C,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;CAA8B,CAAC;AAE/D,eAAO,MAAM,wBAAwB,QAAO,YAwB3C,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;CAA+B,CAAC;AAoBrE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,UAAW,MAAM,KAAG,MAavD,CAAC;AAEF,eAAO,MAAM,2BAA2B;;;;;;;CAAkC,CAAC;AAE3E,eAAO,MAAM,uBAAuB,QAAO,YAM1C,CAAC;AAEF,eAAO,MAAM,8BAA8B,QAAO,YAmBjD,CAAC"}
@@ -1,4 +1,4 @@
1
- import { MOCK_NONCE_RESPONSE as SDK_MOCK_NONCE_RESPONSE, MOCK_JWT as SDK_MOCK_JWT, MOCK_SRP_LOGIN_RESPONSE as SDK_MOCK_SRP_LOGIN_RESPONSE, MOCK_OIDC_TOKEN_RESPONSE as SDK_MOCK_OIDC_TOKEN_RESPONSE, MOCK_NONCE_URL, MOCK_SRP_LOGIN_URL, MOCK_OIDC_TOKEN_URL } from "../../../sdk/mocks/auth.mjs";
1
+ import { MOCK_NONCE_RESPONSE as SDK_MOCK_NONCE_RESPONSE, MOCK_JWT as SDK_MOCK_JWT, MOCK_SRP_LOGIN_RESPONSE as SDK_MOCK_SRP_LOGIN_RESPONSE, MOCK_OIDC_TOKEN_RESPONSE as SDK_MOCK_OIDC_TOKEN_RESPONSE, MOCK_PAIR_PROFILES_RESPONSE as SDK_MOCK_PAIR_PROFILES_RESPONSE, MOCK_NONCE_URL, MOCK_SRP_LOGIN_URL, MOCK_OIDC_TOKEN_URL, MOCK_PAIR_PROFILES_URL } from "../../../sdk/mocks/auth.mjs";
2
2
  export const MOCK_NONCE_RESPONSE = SDK_MOCK_NONCE_RESPONSE;
3
3
  export const MOCK_NONCE = MOCK_NONCE_RESPONSE.nonce;
4
4
  export const MOCK_JWT = SDK_MOCK_JWT;
@@ -79,6 +79,14 @@ export const getE2EIdentifierFromJwt = (token) => {
79
79
  }
80
80
  return token;
81
81
  };
82
+ export const MOCK_PAIR_PROFILES_RESPONSE = SDK_MOCK_PAIR_PROFILES_RESPONSE;
83
+ export const getMockAuthPairResponse = () => {
84
+ return {
85
+ url: MOCK_PAIR_PROFILES_URL,
86
+ requestMethod: 'POST',
87
+ response: MOCK_PAIR_PROFILES_RESPONSE,
88
+ };
89
+ };
82
90
  export const getMockAuthAccessTokenResponse = () => {
83
91
  return {
84
92
  url: MOCK_OIDC_TOKEN_URL,
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.mjs","sourceRoot":"","sources":["../../../../src/controllers/authentication/mocks/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n MOCK_NONCE_RESPONSE as SDK_MOCK_NONCE_RESPONSE,\n MOCK_JWT as SDK_MOCK_JWT,\n MOCK_SRP_LOGIN_RESPONSE as SDK_MOCK_SRP_LOGIN_RESPONSE,\n MOCK_OIDC_TOKEN_RESPONSE as SDK_MOCK_OIDC_TOKEN_RESPONSE,\n MOCK_NONCE_URL,\n MOCK_SRP_LOGIN_URL,\n MOCK_OIDC_TOKEN_URL,\n} from '../../../sdk/mocks/auth';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT';\n response: unknown;\n};\n\nexport const MOCK_NONCE_RESPONSE = SDK_MOCK_NONCE_RESPONSE;\nexport const MOCK_NONCE = MOCK_NONCE_RESPONSE.nonce;\nexport const MOCK_JWT = SDK_MOCK_JWT;\n\nexport const getMockAuthNonceResponse = () => {\n return {\n url: MOCK_NONCE_URL,\n requestMethod: 'GET',\n response: (\n _?: unknown,\n path?: string,\n getE2ESrpIdentifierForPublicKey?: (publicKey: string) => string,\n ) => {\n // The goal here is to have this identifier bubble all the way up to being the access token\n // That way, we can use it to segregate data in the test environment\n const identifier = path?.split('?identifier=')[1];\n const e2eIdentifier = getE2ESrpIdentifierForPublicKey?.(identifier ?? '');\n\n return {\n ...MOCK_NONCE_RESPONSE,\n nonce: e2eIdentifier ?? MOCK_NONCE_RESPONSE.nonce,\n identifier: MOCK_NONCE_RESPONSE.identifier,\n };\n },\n } satisfies MockResponse;\n};\n\nexport const MOCK_LOGIN_RESPONSE = SDK_MOCK_SRP_LOGIN_RESPONSE;\n\nexport const getMockAuthLoginResponse = () => {\n return {\n url: MOCK_SRP_LOGIN_URL,\n requestMethod: 'POST',\n // In case this mock is used in an E2E test, we populate token, profile_id and identifier_id with the e2eIdentifier\n // to make it easier to segregate data in the test environment.\n response: (requestJsonBody?: { raw_message: string }) => {\n const splittedRawMessage = requestJsonBody?.raw_message.split(':');\n const e2eIdentifier = splittedRawMessage?.[splittedRawMessage.length - 2];\n\n return {\n ...MOCK_LOGIN_RESPONSE,\n token: e2eIdentifier ?? MOCK_LOGIN_RESPONSE.token,\n profile: {\n ...MOCK_LOGIN_RESPONSE.profile,\n profile_id: e2eIdentifier ?? MOCK_LOGIN_RESPONSE.profile.profile_id,\n identifier_id:\n e2eIdentifier ?? MOCK_LOGIN_RESPONSE.profile.identifier_id,\n },\n };\n },\n } satisfies MockResponse;\n};\n\nexport const MOCK_OATH_TOKEN_RESPONSE = SDK_MOCK_OIDC_TOKEN_RESPONSE;\n\nconst MOCK_JWT_FAR_FUTURE_EXP = 4102444800; // 2100-01-01\n\n/**\n * Wraps a plain-text identifier in a minimal JWT so that client-side\n * JWT validation (exp check) passes in E2E tests. The identifier is\n * stored in the `sub` claim and can be extracted via {@link getE2EIdentifierFromJwt}.\n *\n * @param identifier - The plain-text E2E identifier to wrap.\n * @returns A JWT-shaped string containing the identifier.\n */\nconst wrapInMockJwt = (identifier: string): string => {\n const header = btoa(JSON.stringify({ alg: 'none', typ: 'JWT' }));\n const payload = btoa(\n JSON.stringify({ sub: identifier, exp: MOCK_JWT_FAR_FUTURE_EXP }),\n );\n return `${header}.${payload}.mock`;\n};\n\n/**\n * Extracts the E2E identifier (`sub` claim) from a mock JWT created\n * by {@link wrapInMockJwt}. Falls back to returning the raw token if\n * decoding fails (backward compatibility with raw-identifier headers).\n *\n * @param token - A bearer token string (JWT or raw identifier).\n * @returns The decoded identifier, or the original token as-is.\n */\nexport const getE2EIdentifierFromJwt = (token: string): string => {\n try {\n const parts = token.split('.');\n if (parts.length === 3) {\n const { sub } = JSON.parse(atob(parts[1]));\n if (typeof sub === 'string' && sub.length > 0) {\n return sub;\n }\n }\n } catch {\n // not a JWT — fall through\n }\n return token;\n};\n\nexport const getMockAuthAccessTokenResponse = () => {\n return {\n url: MOCK_OIDC_TOKEN_URL,\n requestMethod: 'POST',\n response: (requestJsonBody?: string) => {\n // We wrap the e2eIdentifier in a JWT so client-side JWT validation passes.\n // The mock server extracts the identifier back via getE2EIdentifierFromJwt.\n const e2eIdentifier = new URLSearchParams(requestJsonBody).get(\n 'assertion',\n );\n\n return {\n ...MOCK_OATH_TOKEN_RESPONSE,\n access_token: e2eIdentifier\n ? wrapInMockJwt(e2eIdentifier)\n : MOCK_OATH_TOKEN_RESPONSE.access_token,\n };\n },\n } satisfies MockResponse;\n};\n"]}
1
+ {"version":3,"file":"mockResponses.mjs","sourceRoot":"","sources":["../../../../src/controllers/authentication/mocks/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n MOCK_NONCE_RESPONSE as SDK_MOCK_NONCE_RESPONSE,\n MOCK_JWT as SDK_MOCK_JWT,\n MOCK_SRP_LOGIN_RESPONSE as SDK_MOCK_SRP_LOGIN_RESPONSE,\n MOCK_OIDC_TOKEN_RESPONSE as SDK_MOCK_OIDC_TOKEN_RESPONSE,\n MOCK_PAIR_PROFILES_RESPONSE as SDK_MOCK_PAIR_PROFILES_RESPONSE,\n MOCK_NONCE_URL,\n MOCK_SRP_LOGIN_URL,\n MOCK_OIDC_TOKEN_URL,\n MOCK_PAIR_PROFILES_URL,\n} from '../../../sdk/mocks/auth';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT';\n response: unknown;\n};\n\nexport const MOCK_NONCE_RESPONSE = SDK_MOCK_NONCE_RESPONSE;\nexport const MOCK_NONCE = MOCK_NONCE_RESPONSE.nonce;\nexport const MOCK_JWT = SDK_MOCK_JWT;\n\nexport const getMockAuthNonceResponse = (): MockResponse => {\n return {\n url: MOCK_NONCE_URL,\n requestMethod: 'GET',\n response: (\n _?: unknown,\n path?: string,\n getE2ESrpIdentifierForPublicKey?: (publicKey: string) => string,\n ): typeof MOCK_NONCE_RESPONSE => {\n // The goal here is to have this identifier bubble all the way up to being the access token\n // That way, we can use it to segregate data in the test environment\n const identifier = path?.split('?identifier=')[1];\n const e2eIdentifier = getE2ESrpIdentifierForPublicKey?.(identifier ?? '');\n\n return {\n ...MOCK_NONCE_RESPONSE,\n nonce: e2eIdentifier ?? MOCK_NONCE_RESPONSE.nonce,\n identifier: MOCK_NONCE_RESPONSE.identifier,\n };\n },\n } satisfies MockResponse;\n};\n\nexport const MOCK_LOGIN_RESPONSE = SDK_MOCK_SRP_LOGIN_RESPONSE;\n\nexport const getMockAuthLoginResponse = (): MockResponse => {\n return {\n url: MOCK_SRP_LOGIN_URL,\n requestMethod: 'POST',\n // In case this mock is used in an E2E test, we populate token, profile_id and identifier_id with the e2eIdentifier\n // to make it easier to segregate data in the test environment.\n response: (requestJsonBody?: {\n raw_message: string;\n }): typeof MOCK_LOGIN_RESPONSE => {\n const splittedRawMessage = requestJsonBody?.raw_message.split(':');\n const e2eIdentifier = splittedRawMessage?.[splittedRawMessage.length - 2];\n\n return {\n ...MOCK_LOGIN_RESPONSE,\n token: e2eIdentifier ?? MOCK_LOGIN_RESPONSE.token,\n profile: {\n ...MOCK_LOGIN_RESPONSE.profile,\n profile_id: e2eIdentifier ?? MOCK_LOGIN_RESPONSE.profile.profile_id,\n identifier_id:\n e2eIdentifier ?? MOCK_LOGIN_RESPONSE.profile.identifier_id,\n },\n };\n },\n } satisfies MockResponse;\n};\n\nexport const MOCK_OATH_TOKEN_RESPONSE = SDK_MOCK_OIDC_TOKEN_RESPONSE;\n\nconst MOCK_JWT_FAR_FUTURE_EXP = 4102444800; // 2100-01-01\n\n/**\n * Wraps a plain-text identifier in a minimal JWT so that client-side\n * JWT validation (exp check) passes in E2E tests. The identifier is\n * stored in the `sub` claim and can be extracted via {@link getE2EIdentifierFromJwt}.\n *\n * @param identifier - The plain-text E2E identifier to wrap.\n * @returns A JWT-shaped string containing the identifier.\n */\nconst wrapInMockJwt = (identifier: string): string => {\n const header = btoa(JSON.stringify({ alg: 'none', typ: 'JWT' }));\n const payload = btoa(\n JSON.stringify({ sub: identifier, exp: MOCK_JWT_FAR_FUTURE_EXP }),\n );\n return `${header}.${payload}.mock`;\n};\n\n/**\n * Extracts the E2E identifier (`sub` claim) from a mock JWT created\n * by {@link wrapInMockJwt}. Falls back to returning the raw token if\n * decoding fails (backward compatibility with raw-identifier headers).\n *\n * @param token - A bearer token string (JWT or raw identifier).\n * @returns The decoded identifier, or the original token as-is.\n */\nexport const getE2EIdentifierFromJwt = (token: string): string => {\n try {\n const parts = token.split('.');\n if (parts.length === 3) {\n const { sub } = JSON.parse(atob(parts[1]));\n if (typeof sub === 'string' && sub.length > 0) {\n return sub;\n }\n }\n } catch {\n // not a JWT — fall through\n }\n return token;\n};\n\nexport const MOCK_PAIR_PROFILES_RESPONSE = SDK_MOCK_PAIR_PROFILES_RESPONSE;\n\nexport const getMockAuthPairResponse = (): MockResponse => {\n return {\n url: MOCK_PAIR_PROFILES_URL,\n requestMethod: 'POST',\n response: MOCK_PAIR_PROFILES_RESPONSE,\n } satisfies MockResponse;\n};\n\nexport const getMockAuthAccessTokenResponse = (): MockResponse => {\n return {\n url: MOCK_OIDC_TOKEN_URL,\n requestMethod: 'POST',\n response: (requestJsonBody?: string): typeof MOCK_OATH_TOKEN_RESPONSE => {\n // We wrap the e2eIdentifier in a JWT so client-side JWT validation passes.\n // The mock server extracts the identifier back via getE2EIdentifierFromJwt.\n const e2eIdentifier = new URLSearchParams(requestJsonBody).get(\n 'assertion',\n );\n\n return {\n ...MOCK_OATH_TOKEN_RESPONSE,\n access_token: e2eIdentifier\n ? wrapInMockJwt(e2eIdentifier)\n : MOCK_OATH_TOKEN_RESPONSE.access_token,\n };\n },\n } satisfies MockResponse;\n};\n"]}
@@ -41,6 +41,7 @@ const eip_6963_metamask_provider_1 = require("../utils/eip-6963-metamask-provide
41
41
  const messaging_signing_snap_requests_1 = require("../utils/messaging-signing-snap-requests.cjs");
42
42
  const validate_login_response_1 = require("../utils/validate-login-response.cjs");
43
43
  const services_1 = require("./services.cjs");
44
+ const identifier_1 = require("./utils/identifier.cjs");
44
45
  const timeUtils = __importStar(require("./utils/time.cjs"));
45
46
  const getDefaultEIP6963Provider = async () => {
46
47
  const provider = await (0, eip_6963_metamask_provider_1.getMetaMaskProviderEIP6963)();
@@ -113,6 +114,9 @@ class SRPJwtBearerAuth {
113
114
  const accessToken = await this.getAccessToken(entropySourceId);
114
115
  return await (0, services_1.getUserProfileLineage)(__classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, accessToken);
115
116
  }
117
+ async pairSrpProfiles(accessTokens, authAccessToken) {
118
+ return await (0, services_1.pairProfiles)(accessTokens, authAccessToken, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env);
119
+ }
116
120
  async signMessage(message, entropySourceId) {
117
121
  return await __classPrivateFieldGet(this, _SRPJwtBearerAuth_options, "f").signing.signMessage(message, entropySourceId);
118
122
  }
@@ -138,6 +142,10 @@ async function _SRPJwtBearerAuth_getAuthSession(entropySourceId) {
138
142
  if (!(0, validate_login_response_1.validateLoginResponse)(auth)) {
139
143
  return null;
140
144
  }
145
+ // get canonical profile id from server if not present in the cached session
146
+ if (!auth.profile.canonicalProfileId) {
147
+ return null;
148
+ }
141
149
  const currentTime = Date.now();
142
150
  const sessionAge = currentTime - auth.token.obtainedAt;
143
151
  const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;
@@ -156,11 +164,30 @@ async function _SRPJwtBearerAuth_getAuthSession(entropySourceId) {
156
164
  const signature = await this.signMessage(rawMessage, entropySourceId);
157
165
  // Authenticate
158
166
  const authResponse = await (0, services_1.authenticate)(rawMessage, signature, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").type, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, __classPrivateFieldGet(this, _SRPJwtBearerAuth_metametrics, "f"));
167
+ // Resolve original profileId from aliases.
168
+ // This is done mainly to preserve the original profileId for storage key derivation
169
+ // until we migrate to the canonical profileId storage system.
170
+ const canonicalProfileId = authResponse.profile.profileId;
171
+ const profile = { ...authResponse.profile };
172
+ if (authResponse.profileAliases?.length > 0) {
173
+ const targetIdentifierId = (0, identifier_1.computeIdentifierId)(publicKey, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env);
174
+ const matchingAliases = authResponse.profileAliases.filter((alias) => alias.identifierIds.some((id) => id.id === targetIdentifierId));
175
+ // Prefer the leaf alias (single identifier) — it's the original profile
176
+ // created for this SRP. Multi-identifier aliases are former canonicals
177
+ // that absorbed other profiles; they are correct only when this SRP's
178
+ // original profile was itself a canonical before being absorbed.
179
+ const targetAlias = matchingAliases.find((alias) => alias.identifierIds.length === 1) ??
180
+ matchingAliases[0];
181
+ if (targetAlias) {
182
+ profile.profileId = targetAlias.aliasProfileId;
183
+ }
184
+ }
185
+ profile.canonicalProfileId = canonicalProfileId;
159
186
  // Authorize
160
187
  const tokenResponse = await (0, services_1.authorizeOIDC)(authResponse.token, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").platform);
161
188
  // Save
162
189
  const result = {
163
- profile: authResponse.profile,
190
+ profile,
164
191
  token: tokenResponse,
165
192
  };
166
193
  await __classPrivateFieldGet(this, _SRPJwtBearerAuth_options, "f").storage.setLoginResponse(result, entropySourceId);
@@ -1 +1 @@
1
- {"version":3,"file":"flow-srp.cjs","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Eip1193Provider } from 'ethers';\n\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport { ValidationError, RateLimitedError } from '../errors';\nimport { getMetaMaskProviderEIP6963 } from '../utils/eip-6963-metamask-provider';\nimport {\n MESSAGE_SIGNING_SNAP,\n assertMessageStartsWithMetamask,\n connectSnap,\n isSnapConnected,\n} from '../utils/messaging-signing-snap-requests';\nimport { validateLoginResponse } from '../utils/validate-login-response';\nimport {\n authenticate,\n authorizeOIDC,\n getNonce,\n getUserProfileLineage,\n} from './services';\nimport type {\n AuthConfig,\n AuthSigningOptions,\n AuthStorageOptions,\n AuthType,\n IBaseAuth,\n LoginResponse,\n UserProfile,\n UserProfileLineage,\n} from './types';\nimport * as timeUtils from './utils/time';\n\ntype JwtBearerAuth_SRP_Options = {\n storage: AuthStorageOptions;\n signing?: AuthSigningOptions;\n rateLimitRetry?: {\n cooldownDefaultMs?: number; // default cooldown when 429 has no Retry-After\n maxLoginRetries?: number; // maximum number of login retries on rate limit\n };\n};\n\nconst getDefaultEIP6963Provider = async () => {\n const provider = await getMetaMaskProviderEIP6963();\n if (!provider) {\n throw new ValidationError('No MetaMask wallet connected');\n }\n return provider;\n};\n\nconst getDefaultEIP6963SigningOptions = (\n customProvider?: Eip1193Provider,\n): AuthSigningOptions => ({\n getIdentifier: async (entropySourceId?: string): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n return await MESSAGE_SIGNING_SNAP.getPublicKey(provider, entropySourceId);\n },\n signMessage: async (\n message: string,\n entropySourceId?: string,\n ): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n assertMessageStartsWithMetamask(message);\n return await MESSAGE_SIGNING_SNAP.signMessage(\n provider,\n message,\n entropySourceId,\n );\n },\n});\n\nexport class SRPJwtBearerAuth implements IBaseAuth {\n readonly #config: AuthConfig;\n\n readonly #options: {\n storage: AuthStorageOptions;\n signing: AuthSigningOptions;\n };\n\n readonly #metametrics?: MetaMetricsAuth;\n\n // Map to store ongoing login promises by entropySourceId\n readonly #ongoingLogins = new Map<\n string | undefined,\n Promise<LoginResponse>\n >();\n\n // Default cooldown when 429 has no Retry-After header\n readonly #cooldownDefaultMs: number;\n\n // Maximum number of login retries on rate limit errors\n readonly #maxLoginRetries: number;\n\n #customProvider?: Eip1193Provider;\n\n constructor(\n config: AuthConfig & { type: AuthType.SRP },\n options: JwtBearerAuth_SRP_Options & {\n customProvider?: Eip1193Provider;\n metametrics?: MetaMetricsAuth;\n },\n ) {\n this.#config = config;\n this.#customProvider = options.customProvider;\n this.#options = {\n storage: options.storage,\n signing:\n options.signing ??\n getDefaultEIP6963SigningOptions(this.#customProvider),\n };\n this.#metametrics = options.metametrics;\n\n // Apply rate limit retry config if provided\n this.#cooldownDefaultMs =\n options.rateLimitRetry?.cooldownDefaultMs ?? 10000;\n this.#maxLoginRetries = options.rateLimitRetry?.maxLoginRetries ?? 1;\n }\n\n setCustomProvider(provider: Eip1193Provider) {\n this.#customProvider = provider;\n this.#options.signing = getDefaultEIP6963SigningOptions(provider);\n }\n\n // TODO: might be easier to keep entropySourceId as a class param and use multiple SRPJwtBearerAuth instances where needed\n async getAccessToken(entropySourceId?: string): Promise<string> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.token.accessToken;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.token.accessToken;\n }\n\n async getUserProfile(entropySourceId?: string): Promise<UserProfile> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.profile;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.profile;\n }\n\n async getIdentifier(entropySourceId?: string): Promise<string> {\n return await this.#options.signing.getIdentifier(entropySourceId);\n }\n\n async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n const accessToken = await this.getAccessToken(entropySourceId);\n return await getUserProfileLineage(this.#config.env, accessToken);\n }\n\n async signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n return await this.#options.signing.signMessage(message, entropySourceId);\n }\n\n async isSnapConnected(): Promise<boolean> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n if (!provider) {\n return false;\n }\n\n const isConnected = await isSnapConnected(provider);\n return isConnected;\n }\n\n async connectSnap(): Promise<string> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n\n const res = await connectSnap(provider);\n return res;\n }\n\n // convert expiresIn from seconds to milliseconds and use 90% of expiresIn\n async #getAuthSession(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const auth = await this.#options.storage.getLoginResponse(entropySourceId);\n if (!validateLoginResponse(auth)) {\n return null;\n }\n\n const currentTime = Date.now();\n const sessionAge = currentTime - auth.token.obtainedAt;\n const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;\n\n if (sessionAge < refreshThreshold) {\n return auth;\n }\n return null;\n }\n\n async #login(entropySourceId?: string): Promise<LoginResponse> {\n // Use a deferred login to avoid race conditions\n return await this.#deferredLogin(entropySourceId);\n }\n\n async #performLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Nonce\n const publicKey = await this.getIdentifier(entropySourceId);\n const nonceRes = await getNonce(publicKey, this.#config.env);\n\n const rawMessage = this.#createSrpLoginRawMessage(\n nonceRes.nonce,\n publicKey,\n );\n const signature = await this.signMessage(rawMessage, entropySourceId);\n\n // Authenticate\n const authResponse = await authenticate(\n rawMessage,\n signature,\n this.#config.type,\n this.#config.env,\n this.#metametrics,\n );\n\n // Authorize\n const tokenResponse = await authorizeOIDC(\n authResponse.token,\n this.#config.env,\n this.#config.platform,\n );\n\n // Save\n const result: LoginResponse = {\n profile: authResponse.profile,\n token: tokenResponse,\n };\n\n await this.#options.storage.setLoginResponse(result, entropySourceId);\n\n return result;\n }\n\n async #deferredLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Check if there's already an ongoing login for this entropySourceId\n const existingLogin = this.#ongoingLogins.get(entropySourceId);\n if (existingLogin) {\n return existingLogin;\n }\n\n // Create a new login promise\n const loginPromise = this.#loginWithRetry(entropySourceId);\n\n // Store the promise in the map\n this.#ongoingLogins.set(entropySourceId, loginPromise);\n\n try {\n // Wait for the login to complete\n return await loginPromise;\n } finally {\n // Always clean up the ongoing login promise when done\n this.#ongoingLogins.delete(entropySourceId);\n }\n }\n\n async #loginWithRetry(entropySourceId?: string): Promise<LoginResponse> {\n // Allow max attempts: initial + maxLoginRetries on 429\n for (let attempt = 0; attempt < 1 + this.#maxLoginRetries; attempt += 1) {\n try {\n return await this.#performLogin(entropySourceId);\n } catch (e) {\n // Only retry on rate-limit (429) errors\n if (!RateLimitedError.isRateLimitError(e)) {\n throw e;\n }\n\n // If we've exhausted attempts, rethrow\n if (attempt >= this.#maxLoginRetries) {\n throw e;\n }\n\n // Wait for Retry-After or default cooldown\n const waitMs = e.retryAfterMs ?? this.#cooldownDefaultMs;\n await timeUtils.delay(waitMs);\n\n // Loop continues to retry\n }\n }\n\n // Should never reach here due to loop logic, but TypeScript needs a return\n throw new Error('Unexpected: login loop exhausted without result');\n }\n\n #createSrpLoginRawMessage(\n nonce: string,\n publicKey: string,\n ): `metamask:${string}:${string}` {\n return `metamask:${nonce}:${publicKey}` as const;\n }\n}\n"]}
1
+ {"version":3,"file":"flow-srp.cjs","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Eip1193Provider } from 'ethers';\n\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport { ValidationError, RateLimitedError } from '../errors';\nimport { getMetaMaskProviderEIP6963 } from '../utils/eip-6963-metamask-provider';\nimport {\n MESSAGE_SIGNING_SNAP,\n assertMessageStartsWithMetamask,\n connectSnap,\n isSnapConnected,\n} from '../utils/messaging-signing-snap-requests';\nimport { validateLoginResponse } from '../utils/validate-login-response';\nimport {\n authenticate,\n authorizeOIDC,\n getNonce,\n getUserProfileLineage,\n pairProfiles,\n} from './services';\nimport type { PairProfilesResponse } from './services';\nimport type {\n AuthConfig,\n AuthSigningOptions,\n AuthStorageOptions,\n AuthType,\n IBaseAuth,\n LoginResponse,\n UserProfile,\n UserProfileLineage,\n} from './types';\nimport { computeIdentifierId } from './utils/identifier';\nimport * as timeUtils from './utils/time';\n\ntype JwtBearerAuth_SRP_Options = {\n storage: AuthStorageOptions;\n signing?: AuthSigningOptions;\n rateLimitRetry?: {\n cooldownDefaultMs?: number; // default cooldown when 429 has no Retry-After\n maxLoginRetries?: number; // maximum number of login retries on rate limit\n };\n};\n\nconst getDefaultEIP6963Provider = async () => {\n const provider = await getMetaMaskProviderEIP6963();\n if (!provider) {\n throw new ValidationError('No MetaMask wallet connected');\n }\n return provider;\n};\n\nconst getDefaultEIP6963SigningOptions = (\n customProvider?: Eip1193Provider,\n): AuthSigningOptions => ({\n getIdentifier: async (entropySourceId?: string): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n return await MESSAGE_SIGNING_SNAP.getPublicKey(provider, entropySourceId);\n },\n signMessage: async (\n message: string,\n entropySourceId?: string,\n ): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n assertMessageStartsWithMetamask(message);\n return await MESSAGE_SIGNING_SNAP.signMessage(\n provider,\n message,\n entropySourceId,\n );\n },\n});\n\nexport class SRPJwtBearerAuth implements IBaseAuth {\n readonly #config: AuthConfig;\n\n readonly #options: {\n storage: AuthStorageOptions;\n signing: AuthSigningOptions;\n };\n\n readonly #metametrics?: MetaMetricsAuth;\n\n // Map to store ongoing login promises by entropySourceId\n readonly #ongoingLogins = new Map<\n string | undefined,\n Promise<LoginResponse>\n >();\n\n // Default cooldown when 429 has no Retry-After header\n readonly #cooldownDefaultMs: number;\n\n // Maximum number of login retries on rate limit errors\n readonly #maxLoginRetries: number;\n\n #customProvider?: Eip1193Provider;\n\n constructor(\n config: AuthConfig & { type: AuthType.SRP },\n options: JwtBearerAuth_SRP_Options & {\n customProvider?: Eip1193Provider;\n metametrics?: MetaMetricsAuth;\n },\n ) {\n this.#config = config;\n this.#customProvider = options.customProvider;\n this.#options = {\n storage: options.storage,\n signing:\n options.signing ??\n getDefaultEIP6963SigningOptions(this.#customProvider),\n };\n this.#metametrics = options.metametrics;\n\n // Apply rate limit retry config if provided\n this.#cooldownDefaultMs =\n options.rateLimitRetry?.cooldownDefaultMs ?? 10000;\n this.#maxLoginRetries = options.rateLimitRetry?.maxLoginRetries ?? 1;\n }\n\n setCustomProvider(provider: Eip1193Provider) {\n this.#customProvider = provider;\n this.#options.signing = getDefaultEIP6963SigningOptions(provider);\n }\n\n // TODO: might be easier to keep entropySourceId as a class param and use multiple SRPJwtBearerAuth instances where needed\n async getAccessToken(entropySourceId?: string): Promise<string> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.token.accessToken;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.token.accessToken;\n }\n\n async getUserProfile(entropySourceId?: string): Promise<UserProfile> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.profile;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.profile;\n }\n\n async getIdentifier(entropySourceId?: string): Promise<string> {\n return await this.#options.signing.getIdentifier(entropySourceId);\n }\n\n async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n const accessToken = await this.getAccessToken(entropySourceId);\n return await getUserProfileLineage(this.#config.env, accessToken);\n }\n\n async pairSrpProfiles(\n accessTokens: string[],\n authAccessToken: string,\n ): Promise<PairProfilesResponse> {\n return await pairProfiles(accessTokens, authAccessToken, this.#config.env);\n }\n\n async signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n return await this.#options.signing.signMessage(message, entropySourceId);\n }\n\n async isSnapConnected(): Promise<boolean> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n if (!provider) {\n return false;\n }\n\n const isConnected = await isSnapConnected(provider);\n return isConnected;\n }\n\n async connectSnap(): Promise<string> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n\n const res = await connectSnap(provider);\n return res;\n }\n\n // convert expiresIn from seconds to milliseconds and use 90% of expiresIn\n async #getAuthSession(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const auth = await this.#options.storage.getLoginResponse(entropySourceId);\n if (!validateLoginResponse(auth)) {\n return null;\n }\n\n // get canonical profile id from server if not present in the cached session\n if (!auth.profile.canonicalProfileId) {\n return null;\n }\n\n const currentTime = Date.now();\n const sessionAge = currentTime - auth.token.obtainedAt;\n const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;\n\n if (sessionAge < refreshThreshold) {\n return auth;\n }\n return null;\n }\n\n async #login(entropySourceId?: string): Promise<LoginResponse> {\n // Use a deferred login to avoid race conditions\n return await this.#deferredLogin(entropySourceId);\n }\n\n async #performLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Nonce\n const publicKey = await this.getIdentifier(entropySourceId);\n const nonceRes = await getNonce(publicKey, this.#config.env);\n\n const rawMessage = this.#createSrpLoginRawMessage(\n nonceRes.nonce,\n publicKey,\n );\n const signature = await this.signMessage(rawMessage, entropySourceId);\n\n // Authenticate\n const authResponse = await authenticate(\n rawMessage,\n signature,\n this.#config.type,\n this.#config.env,\n this.#metametrics,\n );\n\n // Resolve original profileId from aliases.\n // This is done mainly to preserve the original profileId for storage key derivation\n // until we migrate to the canonical profileId storage system.\n const canonicalProfileId = authResponse.profile.profileId;\n const profile = { ...authResponse.profile };\n\n if (authResponse.profileAliases?.length > 0) {\n const targetIdentifierId = computeIdentifierId(\n publicKey,\n this.#config.env,\n );\n\n const matchingAliases = authResponse.profileAliases.filter((alias) =>\n alias.identifierIds.some((id) => id.id === targetIdentifierId),\n );\n\n // Prefer the leaf alias (single identifier) — it's the original profile\n // created for this SRP. Multi-identifier aliases are former canonicals\n // that absorbed other profiles; they are correct only when this SRP's\n // original profile was itself a canonical before being absorbed.\n const targetAlias =\n matchingAliases.find((alias) => alias.identifierIds.length === 1) ??\n matchingAliases[0];\n\n if (targetAlias) {\n profile.profileId = targetAlias.aliasProfileId;\n }\n }\n\n profile.canonicalProfileId = canonicalProfileId;\n\n // Authorize\n const tokenResponse = await authorizeOIDC(\n authResponse.token,\n this.#config.env,\n this.#config.platform,\n );\n\n // Save\n const result: LoginResponse = {\n profile,\n token: tokenResponse,\n };\n\n await this.#options.storage.setLoginResponse(result, entropySourceId);\n\n return result;\n }\n\n async #deferredLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Check if there's already an ongoing login for this entropySourceId\n const existingLogin = this.#ongoingLogins.get(entropySourceId);\n if (existingLogin) {\n return existingLogin;\n }\n\n // Create a new login promise\n const loginPromise = this.#loginWithRetry(entropySourceId);\n\n // Store the promise in the map\n this.#ongoingLogins.set(entropySourceId, loginPromise);\n\n try {\n // Wait for the login to complete\n return await loginPromise;\n } finally {\n // Always clean up the ongoing login promise when done\n this.#ongoingLogins.delete(entropySourceId);\n }\n }\n\n async #loginWithRetry(entropySourceId?: string): Promise<LoginResponse> {\n // Allow max attempts: initial + maxLoginRetries on 429\n for (let attempt = 0; attempt < 1 + this.#maxLoginRetries; attempt += 1) {\n try {\n return await this.#performLogin(entropySourceId);\n } catch (e) {\n // Only retry on rate-limit (429) errors\n if (!RateLimitedError.isRateLimitError(e)) {\n throw e;\n }\n\n // If we've exhausted attempts, rethrow\n if (attempt >= this.#maxLoginRetries) {\n throw e;\n }\n\n // Wait for Retry-After or default cooldown\n const waitMs = e.retryAfterMs ?? this.#cooldownDefaultMs;\n await timeUtils.delay(waitMs);\n\n // Loop continues to retry\n }\n }\n\n // Should never reach here due to loop logic, but TypeScript needs a return\n throw new Error('Unexpected: login loop exhausted without result');\n }\n\n #createSrpLoginRawMessage(\n nonce: string,\n publicKey: string,\n ): `metamask:${string}:${string}` {\n return `metamask:${nonce}:${publicKey}` as const;\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { Eip1193Provider } from "ethers";
2
2
  import type { MetaMetricsAuth } from "../../shared/types/services.cjs";
3
+ import type { PairProfilesResponse } from "./services.cjs";
3
4
  import type { AuthConfig, AuthSigningOptions, AuthStorageOptions, AuthType, IBaseAuth, UserProfile, UserProfileLineage } from "./types.cjs";
4
5
  type JwtBearerAuth_SRP_Options = {
5
6
  storage: AuthStorageOptions;
@@ -22,6 +23,7 @@ export declare class SRPJwtBearerAuth implements IBaseAuth {
22
23
  getUserProfile(entropySourceId?: string): Promise<UserProfile>;
23
24
  getIdentifier(entropySourceId?: string): Promise<string>;
24
25
  getUserProfileLineage(entropySourceId?: string): Promise<UserProfileLineage>;
26
+ pairSrpProfiles(accessTokens: string[], authAccessToken: string): Promise<PairProfilesResponse>;
25
27
  signMessage(message: string, entropySourceId?: string): Promise<string>;
26
28
  isSnapConnected(): Promise<boolean>;
27
29
  connectSnap(): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"flow-srp.d.cts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,wCAAoC;AAgBnE,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EAET,WAAW,EACX,kBAAkB,EACnB,oBAAgB;AAGjB,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AA+BF,qBAAa,gBAAiB,YAAW,SAAS;;gBAyB9C,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;IAkBH,iBAAiB,CAAC,QAAQ,EAAE,eAAe;IAMrC,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU9D,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD,qBAAqB,CACzB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC;IAKxB,WAAW,CACf,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC;IAIZ,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAWnC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;CA8HrC"}
1
+ {"version":3,"file":"flow-srp.d.cts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,wCAAoC;AAiBnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAmB;AACvD,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EAET,WAAW,EACX,kBAAkB,EACnB,oBAAgB;AAIjB,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AA+BF,qBAAa,gBAAiB,YAAW,SAAS;;gBAyB9C,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;IAkBH,iBAAiB,CAAC,QAAQ,EAAE,eAAe;IAMrC,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU9D,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD,qBAAqB,CACzB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC;IAKxB,eAAe,CACnB,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,oBAAoB,CAAC;IAI1B,WAAW,CACf,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC;IAIZ,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAWnC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;CAkKrC"}
@@ -1,5 +1,6 @@
1
1
  import type { Eip1193Provider } from "ethers";
2
2
  import type { MetaMetricsAuth } from "../../shared/types/services.mjs";
3
+ import type { PairProfilesResponse } from "./services.mjs";
3
4
  import type { AuthConfig, AuthSigningOptions, AuthStorageOptions, AuthType, IBaseAuth, UserProfile, UserProfileLineage } from "./types.mjs";
4
5
  type JwtBearerAuth_SRP_Options = {
5
6
  storage: AuthStorageOptions;
@@ -22,6 +23,7 @@ export declare class SRPJwtBearerAuth implements IBaseAuth {
22
23
  getUserProfile(entropySourceId?: string): Promise<UserProfile>;
23
24
  getIdentifier(entropySourceId?: string): Promise<string>;
24
25
  getUserProfileLineage(entropySourceId?: string): Promise<UserProfileLineage>;
26
+ pairSrpProfiles(accessTokens: string[], authAccessToken: string): Promise<PairProfilesResponse>;
25
27
  signMessage(message: string, entropySourceId?: string): Promise<string>;
26
28
  isSnapConnected(): Promise<boolean>;
27
29
  connectSnap(): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"flow-srp.d.mts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,wCAAoC;AAgBnE,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EAET,WAAW,EACX,kBAAkB,EACnB,oBAAgB;AAGjB,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AA+BF,qBAAa,gBAAiB,YAAW,SAAS;;gBAyB9C,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;IAkBH,iBAAiB,CAAC,QAAQ,EAAE,eAAe;IAMrC,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU9D,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD,qBAAqB,CACzB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC;IAKxB,WAAW,CACf,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC;IAIZ,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAWnC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;CA8HrC"}
1
+ {"version":3,"file":"flow-srp.d.mts","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,wCAAoC;AAiBnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAmB;AACvD,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EAET,WAAW,EACX,kBAAkB,EACnB,oBAAgB;AAIjB,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AA+BF,qBAAa,gBAAiB,YAAW,SAAS;;gBAyB9C,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;IAkBH,iBAAiB,CAAC,QAAQ,EAAE,eAAe;IAMrC,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU9D,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD,qBAAqB,CACzB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC;IAKxB,eAAe,CACnB,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,oBAAoB,CAAC;IAI1B,WAAW,CACf,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC;IAIZ,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAWnC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;CAkKrC"}
@@ -14,7 +14,8 @@ import { ValidationError, RateLimitedError } from "../errors.mjs";
14
14
  import { getMetaMaskProviderEIP6963 } from "../utils/eip-6963-metamask-provider.mjs";
15
15
  import { MESSAGE_SIGNING_SNAP, assertMessageStartsWithMetamask, connectSnap, isSnapConnected } from "../utils/messaging-signing-snap-requests.mjs";
16
16
  import { validateLoginResponse } from "../utils/validate-login-response.mjs";
17
- import { authenticate, authorizeOIDC, getNonce, getUserProfileLineage } from "./services.mjs";
17
+ import { authenticate, authorizeOIDC, getNonce, getUserProfileLineage, pairProfiles } from "./services.mjs";
18
+ import { computeIdentifierId } from "./utils/identifier.mjs";
18
19
  import * as timeUtils from "./utils/time.mjs";
19
20
  const getDefaultEIP6963Provider = async () => {
20
21
  const provider = await getMetaMaskProviderEIP6963();
@@ -87,6 +88,9 @@ export class SRPJwtBearerAuth {
87
88
  const accessToken = await this.getAccessToken(entropySourceId);
88
89
  return await getUserProfileLineage(__classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, accessToken);
89
90
  }
91
+ async pairSrpProfiles(accessTokens, authAccessToken) {
92
+ return await pairProfiles(accessTokens, authAccessToken, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env);
93
+ }
90
94
  async signMessage(message, entropySourceId) {
91
95
  return await __classPrivateFieldGet(this, _SRPJwtBearerAuth_options, "f").signing.signMessage(message, entropySourceId);
92
96
  }
@@ -111,6 +115,10 @@ async function _SRPJwtBearerAuth_getAuthSession(entropySourceId) {
111
115
  if (!validateLoginResponse(auth)) {
112
116
  return null;
113
117
  }
118
+ // get canonical profile id from server if not present in the cached session
119
+ if (!auth.profile.canonicalProfileId) {
120
+ return null;
121
+ }
114
122
  const currentTime = Date.now();
115
123
  const sessionAge = currentTime - auth.token.obtainedAt;
116
124
  const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;
@@ -129,11 +137,30 @@ async function _SRPJwtBearerAuth_getAuthSession(entropySourceId) {
129
137
  const signature = await this.signMessage(rawMessage, entropySourceId);
130
138
  // Authenticate
131
139
  const authResponse = await authenticate(rawMessage, signature, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").type, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, __classPrivateFieldGet(this, _SRPJwtBearerAuth_metametrics, "f"));
140
+ // Resolve original profileId from aliases.
141
+ // This is done mainly to preserve the original profileId for storage key derivation
142
+ // until we migrate to the canonical profileId storage system.
143
+ const canonicalProfileId = authResponse.profile.profileId;
144
+ const profile = { ...authResponse.profile };
145
+ if (authResponse.profileAliases?.length > 0) {
146
+ const targetIdentifierId = computeIdentifierId(publicKey, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env);
147
+ const matchingAliases = authResponse.profileAliases.filter((alias) => alias.identifierIds.some((id) => id.id === targetIdentifierId));
148
+ // Prefer the leaf alias (single identifier) — it's the original profile
149
+ // created for this SRP. Multi-identifier aliases are former canonicals
150
+ // that absorbed other profiles; they are correct only when this SRP's
151
+ // original profile was itself a canonical before being absorbed.
152
+ const targetAlias = matchingAliases.find((alias) => alias.identifierIds.length === 1) ??
153
+ matchingAliases[0];
154
+ if (targetAlias) {
155
+ profile.profileId = targetAlias.aliasProfileId;
156
+ }
157
+ }
158
+ profile.canonicalProfileId = canonicalProfileId;
132
159
  // Authorize
133
160
  const tokenResponse = await authorizeOIDC(authResponse.token, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").env, __classPrivateFieldGet(this, _SRPJwtBearerAuth_config, "f").platform);
134
161
  // Save
135
162
  const result = {
136
- profile: authResponse.profile,
163
+ profile,
137
164
  token: tokenResponse,
138
165
  };
139
166
  await __classPrivateFieldGet(this, _SRPJwtBearerAuth_options, "f").storage.setLoginResponse(result, entropySourceId);
@@ -1 +1 @@
1
- {"version":3,"file":"flow-srp.mjs","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Eip1193Provider } from 'ethers';\n\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport { ValidationError, RateLimitedError } from '../errors';\nimport { getMetaMaskProviderEIP6963 } from '../utils/eip-6963-metamask-provider';\nimport {\n MESSAGE_SIGNING_SNAP,\n assertMessageStartsWithMetamask,\n connectSnap,\n isSnapConnected,\n} from '../utils/messaging-signing-snap-requests';\nimport { validateLoginResponse } from '../utils/validate-login-response';\nimport {\n authenticate,\n authorizeOIDC,\n getNonce,\n getUserProfileLineage,\n} from './services';\nimport type {\n AuthConfig,\n AuthSigningOptions,\n AuthStorageOptions,\n AuthType,\n IBaseAuth,\n LoginResponse,\n UserProfile,\n UserProfileLineage,\n} from './types';\nimport * as timeUtils from './utils/time';\n\ntype JwtBearerAuth_SRP_Options = {\n storage: AuthStorageOptions;\n signing?: AuthSigningOptions;\n rateLimitRetry?: {\n cooldownDefaultMs?: number; // default cooldown when 429 has no Retry-After\n maxLoginRetries?: number; // maximum number of login retries on rate limit\n };\n};\n\nconst getDefaultEIP6963Provider = async () => {\n const provider = await getMetaMaskProviderEIP6963();\n if (!provider) {\n throw new ValidationError('No MetaMask wallet connected');\n }\n return provider;\n};\n\nconst getDefaultEIP6963SigningOptions = (\n customProvider?: Eip1193Provider,\n): AuthSigningOptions => ({\n getIdentifier: async (entropySourceId?: string): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n return await MESSAGE_SIGNING_SNAP.getPublicKey(provider, entropySourceId);\n },\n signMessage: async (\n message: string,\n entropySourceId?: string,\n ): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n assertMessageStartsWithMetamask(message);\n return await MESSAGE_SIGNING_SNAP.signMessage(\n provider,\n message,\n entropySourceId,\n );\n },\n});\n\nexport class SRPJwtBearerAuth implements IBaseAuth {\n readonly #config: AuthConfig;\n\n readonly #options: {\n storage: AuthStorageOptions;\n signing: AuthSigningOptions;\n };\n\n readonly #metametrics?: MetaMetricsAuth;\n\n // Map to store ongoing login promises by entropySourceId\n readonly #ongoingLogins = new Map<\n string | undefined,\n Promise<LoginResponse>\n >();\n\n // Default cooldown when 429 has no Retry-After header\n readonly #cooldownDefaultMs: number;\n\n // Maximum number of login retries on rate limit errors\n readonly #maxLoginRetries: number;\n\n #customProvider?: Eip1193Provider;\n\n constructor(\n config: AuthConfig & { type: AuthType.SRP },\n options: JwtBearerAuth_SRP_Options & {\n customProvider?: Eip1193Provider;\n metametrics?: MetaMetricsAuth;\n },\n ) {\n this.#config = config;\n this.#customProvider = options.customProvider;\n this.#options = {\n storage: options.storage,\n signing:\n options.signing ??\n getDefaultEIP6963SigningOptions(this.#customProvider),\n };\n this.#metametrics = options.metametrics;\n\n // Apply rate limit retry config if provided\n this.#cooldownDefaultMs =\n options.rateLimitRetry?.cooldownDefaultMs ?? 10000;\n this.#maxLoginRetries = options.rateLimitRetry?.maxLoginRetries ?? 1;\n }\n\n setCustomProvider(provider: Eip1193Provider) {\n this.#customProvider = provider;\n this.#options.signing = getDefaultEIP6963SigningOptions(provider);\n }\n\n // TODO: might be easier to keep entropySourceId as a class param and use multiple SRPJwtBearerAuth instances where needed\n async getAccessToken(entropySourceId?: string): Promise<string> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.token.accessToken;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.token.accessToken;\n }\n\n async getUserProfile(entropySourceId?: string): Promise<UserProfile> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.profile;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.profile;\n }\n\n async getIdentifier(entropySourceId?: string): Promise<string> {\n return await this.#options.signing.getIdentifier(entropySourceId);\n }\n\n async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n const accessToken = await this.getAccessToken(entropySourceId);\n return await getUserProfileLineage(this.#config.env, accessToken);\n }\n\n async signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n return await this.#options.signing.signMessage(message, entropySourceId);\n }\n\n async isSnapConnected(): Promise<boolean> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n if (!provider) {\n return false;\n }\n\n const isConnected = await isSnapConnected(provider);\n return isConnected;\n }\n\n async connectSnap(): Promise<string> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n\n const res = await connectSnap(provider);\n return res;\n }\n\n // convert expiresIn from seconds to milliseconds and use 90% of expiresIn\n async #getAuthSession(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const auth = await this.#options.storage.getLoginResponse(entropySourceId);\n if (!validateLoginResponse(auth)) {\n return null;\n }\n\n const currentTime = Date.now();\n const sessionAge = currentTime - auth.token.obtainedAt;\n const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;\n\n if (sessionAge < refreshThreshold) {\n return auth;\n }\n return null;\n }\n\n async #login(entropySourceId?: string): Promise<LoginResponse> {\n // Use a deferred login to avoid race conditions\n return await this.#deferredLogin(entropySourceId);\n }\n\n async #performLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Nonce\n const publicKey = await this.getIdentifier(entropySourceId);\n const nonceRes = await getNonce(publicKey, this.#config.env);\n\n const rawMessage = this.#createSrpLoginRawMessage(\n nonceRes.nonce,\n publicKey,\n );\n const signature = await this.signMessage(rawMessage, entropySourceId);\n\n // Authenticate\n const authResponse = await authenticate(\n rawMessage,\n signature,\n this.#config.type,\n this.#config.env,\n this.#metametrics,\n );\n\n // Authorize\n const tokenResponse = await authorizeOIDC(\n authResponse.token,\n this.#config.env,\n this.#config.platform,\n );\n\n // Save\n const result: LoginResponse = {\n profile: authResponse.profile,\n token: tokenResponse,\n };\n\n await this.#options.storage.setLoginResponse(result, entropySourceId);\n\n return result;\n }\n\n async #deferredLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Check if there's already an ongoing login for this entropySourceId\n const existingLogin = this.#ongoingLogins.get(entropySourceId);\n if (existingLogin) {\n return existingLogin;\n }\n\n // Create a new login promise\n const loginPromise = this.#loginWithRetry(entropySourceId);\n\n // Store the promise in the map\n this.#ongoingLogins.set(entropySourceId, loginPromise);\n\n try {\n // Wait for the login to complete\n return await loginPromise;\n } finally {\n // Always clean up the ongoing login promise when done\n this.#ongoingLogins.delete(entropySourceId);\n }\n }\n\n async #loginWithRetry(entropySourceId?: string): Promise<LoginResponse> {\n // Allow max attempts: initial + maxLoginRetries on 429\n for (let attempt = 0; attempt < 1 + this.#maxLoginRetries; attempt += 1) {\n try {\n return await this.#performLogin(entropySourceId);\n } catch (e) {\n // Only retry on rate-limit (429) errors\n if (!RateLimitedError.isRateLimitError(e)) {\n throw e;\n }\n\n // If we've exhausted attempts, rethrow\n if (attempt >= this.#maxLoginRetries) {\n throw e;\n }\n\n // Wait for Retry-After or default cooldown\n const waitMs = e.retryAfterMs ?? this.#cooldownDefaultMs;\n await timeUtils.delay(waitMs);\n\n // Loop continues to retry\n }\n }\n\n // Should never reach here due to loop logic, but TypeScript needs a return\n throw new Error('Unexpected: login loop exhausted without result');\n }\n\n #createSrpLoginRawMessage(\n nonce: string,\n publicKey: string,\n ): `metamask:${string}:${string}` {\n return `metamask:${nonce}:${publicKey}` as const;\n }\n}\n"]}
1
+ {"version":3,"file":"flow-srp.mjs","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Eip1193Provider } from 'ethers';\n\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport { ValidationError, RateLimitedError } from '../errors';\nimport { getMetaMaskProviderEIP6963 } from '../utils/eip-6963-metamask-provider';\nimport {\n MESSAGE_SIGNING_SNAP,\n assertMessageStartsWithMetamask,\n connectSnap,\n isSnapConnected,\n} from '../utils/messaging-signing-snap-requests';\nimport { validateLoginResponse } from '../utils/validate-login-response';\nimport {\n authenticate,\n authorizeOIDC,\n getNonce,\n getUserProfileLineage,\n pairProfiles,\n} from './services';\nimport type { PairProfilesResponse } from './services';\nimport type {\n AuthConfig,\n AuthSigningOptions,\n AuthStorageOptions,\n AuthType,\n IBaseAuth,\n LoginResponse,\n UserProfile,\n UserProfileLineage,\n} from './types';\nimport { computeIdentifierId } from './utils/identifier';\nimport * as timeUtils from './utils/time';\n\ntype JwtBearerAuth_SRP_Options = {\n storage: AuthStorageOptions;\n signing?: AuthSigningOptions;\n rateLimitRetry?: {\n cooldownDefaultMs?: number; // default cooldown when 429 has no Retry-After\n maxLoginRetries?: number; // maximum number of login retries on rate limit\n };\n};\n\nconst getDefaultEIP6963Provider = async () => {\n const provider = await getMetaMaskProviderEIP6963();\n if (!provider) {\n throw new ValidationError('No MetaMask wallet connected');\n }\n return provider;\n};\n\nconst getDefaultEIP6963SigningOptions = (\n customProvider?: Eip1193Provider,\n): AuthSigningOptions => ({\n getIdentifier: async (entropySourceId?: string): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n return await MESSAGE_SIGNING_SNAP.getPublicKey(provider, entropySourceId);\n },\n signMessage: async (\n message: string,\n entropySourceId?: string,\n ): Promise<string> => {\n const provider = customProvider ?? (await getDefaultEIP6963Provider());\n assertMessageStartsWithMetamask(message);\n return await MESSAGE_SIGNING_SNAP.signMessage(\n provider,\n message,\n entropySourceId,\n );\n },\n});\n\nexport class SRPJwtBearerAuth implements IBaseAuth {\n readonly #config: AuthConfig;\n\n readonly #options: {\n storage: AuthStorageOptions;\n signing: AuthSigningOptions;\n };\n\n readonly #metametrics?: MetaMetricsAuth;\n\n // Map to store ongoing login promises by entropySourceId\n readonly #ongoingLogins = new Map<\n string | undefined,\n Promise<LoginResponse>\n >();\n\n // Default cooldown when 429 has no Retry-After header\n readonly #cooldownDefaultMs: number;\n\n // Maximum number of login retries on rate limit errors\n readonly #maxLoginRetries: number;\n\n #customProvider?: Eip1193Provider;\n\n constructor(\n config: AuthConfig & { type: AuthType.SRP },\n options: JwtBearerAuth_SRP_Options & {\n customProvider?: Eip1193Provider;\n metametrics?: MetaMetricsAuth;\n },\n ) {\n this.#config = config;\n this.#customProvider = options.customProvider;\n this.#options = {\n storage: options.storage,\n signing:\n options.signing ??\n getDefaultEIP6963SigningOptions(this.#customProvider),\n };\n this.#metametrics = options.metametrics;\n\n // Apply rate limit retry config if provided\n this.#cooldownDefaultMs =\n options.rateLimitRetry?.cooldownDefaultMs ?? 10000;\n this.#maxLoginRetries = options.rateLimitRetry?.maxLoginRetries ?? 1;\n }\n\n setCustomProvider(provider: Eip1193Provider) {\n this.#customProvider = provider;\n this.#options.signing = getDefaultEIP6963SigningOptions(provider);\n }\n\n // TODO: might be easier to keep entropySourceId as a class param and use multiple SRPJwtBearerAuth instances where needed\n async getAccessToken(entropySourceId?: string): Promise<string> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.token.accessToken;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.token.accessToken;\n }\n\n async getUserProfile(entropySourceId?: string): Promise<UserProfile> {\n const session = await this.#getAuthSession(entropySourceId);\n if (session) {\n return session.profile;\n }\n\n const loginResponse = await this.#login(entropySourceId);\n return loginResponse.profile;\n }\n\n async getIdentifier(entropySourceId?: string): Promise<string> {\n return await this.#options.signing.getIdentifier(entropySourceId);\n }\n\n async getUserProfileLineage(\n entropySourceId?: string,\n ): Promise<UserProfileLineage> {\n const accessToken = await this.getAccessToken(entropySourceId);\n return await getUserProfileLineage(this.#config.env, accessToken);\n }\n\n async pairSrpProfiles(\n accessTokens: string[],\n authAccessToken: string,\n ): Promise<PairProfilesResponse> {\n return await pairProfiles(accessTokens, authAccessToken, this.#config.env);\n }\n\n async signMessage(\n message: string,\n entropySourceId?: string,\n ): Promise<string> {\n return await this.#options.signing.signMessage(message, entropySourceId);\n }\n\n async isSnapConnected(): Promise<boolean> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n if (!provider) {\n return false;\n }\n\n const isConnected = await isSnapConnected(provider);\n return isConnected;\n }\n\n async connectSnap(): Promise<string> {\n const provider =\n this.#customProvider ?? (await getDefaultEIP6963Provider());\n\n const res = await connectSnap(provider);\n return res;\n }\n\n // convert expiresIn from seconds to milliseconds and use 90% of expiresIn\n async #getAuthSession(\n entropySourceId?: string,\n ): Promise<LoginResponse | null> {\n const auth = await this.#options.storage.getLoginResponse(entropySourceId);\n if (!validateLoginResponse(auth)) {\n return null;\n }\n\n // get canonical profile id from server if not present in the cached session\n if (!auth.profile.canonicalProfileId) {\n return null;\n }\n\n const currentTime = Date.now();\n const sessionAge = currentTime - auth.token.obtainedAt;\n const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;\n\n if (sessionAge < refreshThreshold) {\n return auth;\n }\n return null;\n }\n\n async #login(entropySourceId?: string): Promise<LoginResponse> {\n // Use a deferred login to avoid race conditions\n return await this.#deferredLogin(entropySourceId);\n }\n\n async #performLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Nonce\n const publicKey = await this.getIdentifier(entropySourceId);\n const nonceRes = await getNonce(publicKey, this.#config.env);\n\n const rawMessage = this.#createSrpLoginRawMessage(\n nonceRes.nonce,\n publicKey,\n );\n const signature = await this.signMessage(rawMessage, entropySourceId);\n\n // Authenticate\n const authResponse = await authenticate(\n rawMessage,\n signature,\n this.#config.type,\n this.#config.env,\n this.#metametrics,\n );\n\n // Resolve original profileId from aliases.\n // This is done mainly to preserve the original profileId for storage key derivation\n // until we migrate to the canonical profileId storage system.\n const canonicalProfileId = authResponse.profile.profileId;\n const profile = { ...authResponse.profile };\n\n if (authResponse.profileAliases?.length > 0) {\n const targetIdentifierId = computeIdentifierId(\n publicKey,\n this.#config.env,\n );\n\n const matchingAliases = authResponse.profileAliases.filter((alias) =>\n alias.identifierIds.some((id) => id.id === targetIdentifierId),\n );\n\n // Prefer the leaf alias (single identifier) — it's the original profile\n // created for this SRP. Multi-identifier aliases are former canonicals\n // that absorbed other profiles; they are correct only when this SRP's\n // original profile was itself a canonical before being absorbed.\n const targetAlias =\n matchingAliases.find((alias) => alias.identifierIds.length === 1) ??\n matchingAliases[0];\n\n if (targetAlias) {\n profile.profileId = targetAlias.aliasProfileId;\n }\n }\n\n profile.canonicalProfileId = canonicalProfileId;\n\n // Authorize\n const tokenResponse = await authorizeOIDC(\n authResponse.token,\n this.#config.env,\n this.#config.platform,\n );\n\n // Save\n const result: LoginResponse = {\n profile,\n token: tokenResponse,\n };\n\n await this.#options.storage.setLoginResponse(result, entropySourceId);\n\n return result;\n }\n\n async #deferredLogin(entropySourceId?: string): Promise<LoginResponse> {\n // Check if there's already an ongoing login for this entropySourceId\n const existingLogin = this.#ongoingLogins.get(entropySourceId);\n if (existingLogin) {\n return existingLogin;\n }\n\n // Create a new login promise\n const loginPromise = this.#loginWithRetry(entropySourceId);\n\n // Store the promise in the map\n this.#ongoingLogins.set(entropySourceId, loginPromise);\n\n try {\n // Wait for the login to complete\n return await loginPromise;\n } finally {\n // Always clean up the ongoing login promise when done\n this.#ongoingLogins.delete(entropySourceId);\n }\n }\n\n async #loginWithRetry(entropySourceId?: string): Promise<LoginResponse> {\n // Allow max attempts: initial + maxLoginRetries on 429\n for (let attempt = 0; attempt < 1 + this.#maxLoginRetries; attempt += 1) {\n try {\n return await this.#performLogin(entropySourceId);\n } catch (e) {\n // Only retry on rate-limit (429) errors\n if (!RateLimitedError.isRateLimitError(e)) {\n throw e;\n }\n\n // If we've exhausted attempts, rethrow\n if (attempt >= this.#maxLoginRetries) {\n throw e;\n }\n\n // Wait for Retry-After or default cooldown\n const waitMs = e.retryAfterMs ?? this.#cooldownDefaultMs;\n await timeUtils.delay(waitMs);\n\n // Loop continues to retry\n }\n }\n\n // Should never reach here due to loop logic, but TypeScript needs a return\n throw new Error('Unexpected: login loop exhausted without result');\n }\n\n #createSrpLoginRawMessage(\n nonce: string,\n publicKey: string,\n ): `metamask:${string}:${string}` {\n return `metamask:${nonce}:${publicKey}` as const;\n }\n}\n"]}
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getUserProfileLineage = exports.authenticate = exports.authorizeOIDC = exports.getNonce = exports.pairIdentifiers = exports.PROFILE_LINEAGE_URL = exports.SIWE_LOGIN_URL = exports.SRP_LOGIN_URL = exports.OIDC_TOKEN_URL = exports.PAIR_IDENTIFIERS = exports.NONCE_URL = void 0;
3
+ exports.getUserProfileLineage = exports.authenticate = exports.authorizeOIDC = exports.getNonce = exports.pairProfiles = exports.pairIdentifiers = exports.PROFILE_LINEAGE_URL = exports.PAIR_PROFILES_URL = exports.SIWE_LOGIN_URL = exports.SRP_LOGIN_URL = exports.OIDC_TOKEN_URL = exports.PAIR_IDENTIFIERS = exports.NONCE_URL = void 0;
4
4
  const env_1 = require("../../shared/env.cjs");
5
5
  const constants_1 = require("../constants.cjs");
6
6
  const errors_1 = require("../errors.cjs");
7
+ const validate_pair_response_1 = require("../utils/validate-pair-response.cjs");
7
8
  const types_1 = require("./types.cjs");
8
9
  /**
9
10
  * Parse Retry-After header into milliseconds if possible.
@@ -112,6 +113,8 @@ const SRP_LOGIN_URL = (env) => `${(0, env_1.getEnvUrls)(env).authApiUrl}/api/v2/
112
113
  exports.SRP_LOGIN_URL = SRP_LOGIN_URL;
113
114
  const SIWE_LOGIN_URL = (env) => `${(0, env_1.getEnvUrls)(env).authApiUrl}/api/v2/siwe/login`;
114
115
  exports.SIWE_LOGIN_URL = SIWE_LOGIN_URL;
116
+ const PAIR_PROFILES_URL = (env) => `${(0, env_1.getEnvUrls)(env).authApiUrl}/api/v2/profile/pair`;
117
+ exports.PAIR_PROFILES_URL = PAIR_PROFILES_URL;
115
118
  const PROFILE_LINEAGE_URL = (env) => `${(0, env_1.getEnvUrls)(env).authApiUrl}/api/v2/profile/lineage`;
116
119
  exports.PROFILE_LINEAGE_URL = PROFILE_LINEAGE_URL;
117
120
  const getAuthenticationUrl = (authType, env) => {
@@ -125,6 +128,13 @@ const getAuthenticationUrl = (authType, env) => {
125
128
  throw new errors_1.ValidationError(`Invalid AuthType: ${authType} - unable to create Auth URL`);
126
129
  }
127
130
  };
131
+ const parseProfileAliases = (raw) => {
132
+ return raw.map((alias) => ({
133
+ aliasProfileId: alias.alias_profile_id,
134
+ canonicalProfileId: alias.canonical_profile_id,
135
+ identifierIds: alias.identifier_ids ?? [],
136
+ }));
137
+ };
128
138
  /**
129
139
  * Pair multiple identifiers under a single profile
130
140
  *
@@ -158,6 +168,48 @@ async function pairIdentifiers(nonce, logins, accessToken, env) {
158
168
  }
159
169
  }
160
170
  exports.pairIdentifiers = pairIdentifiers;
171
+ /**
172
+ * Pair multiple profiles using their OIDC access tokens.
173
+ * Idempotent — calling with already-paired tokens is a no-op.
174
+ *
175
+ * @param accessTokens - Two or more OIDC access tokens to pair
176
+ * @param authAccessToken - A valid access token for the Authorization header
177
+ * @param env - server environment
178
+ * @returns The pair response containing the canonical profile and aliases
179
+ */
180
+ async function pairProfiles(accessTokens, authAccessToken, env) {
181
+ const pairUrl = new URL((0, exports.PAIR_PROFILES_URL)(env));
182
+ try {
183
+ const response = await fetch(pairUrl, {
184
+ method: 'POST',
185
+ headers: {
186
+ 'Content-Type': 'application/json',
187
+ Authorization: `Bearer ${authAccessToken}`,
188
+ },
189
+ body: JSON.stringify({
190
+ jwts: accessTokens,
191
+ }),
192
+ });
193
+ if (!response.ok) {
194
+ return await throwServiceError(response, 'Failed to pair profiles', errors_1.PairError);
195
+ }
196
+ const pairResponse = await response.json();
197
+ (0, validate_pair_response_1.validatePairResponse)(pairResponse);
198
+ return {
199
+ profile: {
200
+ identifierId: pairResponse.profile.identifier_id,
201
+ metaMetricsId: pairResponse.profile.metametrics_id ?? '',
202
+ profileId: pairResponse.profile.profile_id,
203
+ canonicalProfileId: pairResponse.profile.profile_id,
204
+ },
205
+ profileAliases: parseProfileAliases(pairResponse.profile_aliases ?? []),
206
+ };
207
+ }
208
+ catch (error) {
209
+ return await throwServiceError(error, 'Failed to pair profiles', errors_1.PairError);
210
+ }
211
+ }
212
+ exports.pairProfiles = pairProfiles;
161
213
  /**
162
214
  * Service to Get Nonce for JWT Bearer Flow
163
215
  *
@@ -240,6 +292,9 @@ async function authenticate(rawMessage, signature, authType, env, metametrics) {
240
292
  method: 'POST',
241
293
  headers: {
242
294
  'Content-Type': 'application/json',
295
+ ...(authType === types_1.AuthType.SRP
296
+ ? { 'X-MetaMask-Profile-Pairing': 'enabled' }
297
+ : {}),
243
298
  },
244
299
  body: JSON.stringify({
245
300
  signature,
@@ -249,6 +304,7 @@ async function authenticate(rawMessage, signature, authType, env, metametrics) {
249
304
  metametrics: {
250
305
  metametrics_id: await metametrics.getMetaMetricsId(),
251
306
  agent: metametrics.agent,
307
+ app_version: await metametrics.getAppVersion?.(),
252
308
  },
253
309
  }
254
310
  : {}),
@@ -265,7 +321,9 @@ async function authenticate(rawMessage, signature, authType, env, metametrics) {
265
321
  identifierId: loginResponse.profile.identifier_id,
266
322
  metaMetricsId: loginResponse.profile.metametrics_id,
267
323
  profileId: loginResponse.profile.profile_id,
324
+ canonicalProfileId: loginResponse.profile.profile_id,
268
325
  },
326
+ profileAliases: parseProfileAliases(loginResponse.profile_aliases ?? []),
269
327
  };
270
328
  }
271
329
  catch (error) {