@azure/msal-browser 4.8.0 → 4.9.0

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 (204) hide show
  1. package/dist/app/IPublicClientApplication.mjs +1 -1
  2. package/dist/app/PublicClientApplication.mjs +1 -1
  3. package/dist/app/PublicClientNext.mjs +1 -1
  4. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +1 -1
  5. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +1 -1
  6. package/dist/cache/AccountManager.mjs +1 -1
  7. package/dist/cache/AsyncMemoryStorage.mjs +1 -1
  8. package/dist/cache/BrowserCacheManager.d.ts +4 -41
  9. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  10. package/dist/cache/BrowserCacheManager.mjs +18 -143
  11. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  12. package/dist/cache/CacheHelpers.mjs +1 -1
  13. package/dist/cache/CookieStorage.mjs +1 -1
  14. package/dist/cache/DatabaseStorage.mjs +1 -1
  15. package/dist/cache/LocalStorage.mjs +1 -1
  16. package/dist/cache/MemoryStorage.mjs +1 -1
  17. package/dist/cache/SessionStorage.mjs +1 -1
  18. package/dist/cache/TokenCache.mjs +1 -1
  19. package/dist/config/Configuration.d.ts.map +1 -1
  20. package/dist/config/Configuration.mjs +12 -3
  21. package/dist/config/Configuration.mjs.map +1 -1
  22. package/dist/controllers/ControllerFactory.mjs +1 -1
  23. package/dist/controllers/NestedAppAuthController.d.ts +2 -2
  24. package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
  25. package/dist/controllers/NestedAppAuthController.mjs +1 -1
  26. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  27. package/dist/controllers/StandardController.d.ts.map +1 -1
  28. package/dist/controllers/StandardController.mjs +28 -16
  29. package/dist/controllers/StandardController.mjs.map +1 -1
  30. package/dist/controllers/UnknownOperatingContextController.d.ts +2 -2
  31. package/dist/controllers/UnknownOperatingContextController.d.ts.map +1 -1
  32. package/dist/controllers/UnknownOperatingContextController.mjs +1 -1
  33. package/dist/controllers/UnknownOperatingContextController.mjs.map +1 -1
  34. package/dist/crypto/BrowserCrypto.d.ts +11 -0
  35. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  36. package/dist/crypto/BrowserCrypto.mjs +68 -5
  37. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  38. package/dist/crypto/CryptoOps.mjs +1 -1
  39. package/dist/crypto/PkceGenerator.mjs +1 -1
  40. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  41. package/dist/encode/Base64Decode.mjs +1 -1
  42. package/dist/encode/Base64Encode.mjs +1 -1
  43. package/dist/error/BrowserAuthError.d.ts +3 -5
  44. package/dist/error/BrowserAuthError.d.ts.map +1 -1
  45. package/dist/error/BrowserAuthError.mjs +5 -7
  46. package/dist/error/BrowserAuthError.mjs.map +1 -1
  47. package/dist/error/BrowserAuthErrorCodes.d.ts +3 -1
  48. package/dist/error/BrowserAuthErrorCodes.d.ts.map +1 -1
  49. package/dist/error/BrowserAuthErrorCodes.mjs +6 -4
  50. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -1
  51. package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
  52. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
  53. package/dist/error/NativeAuthError.mjs +1 -1
  54. package/dist/error/NativeAuthErrorCodes.mjs +1 -1
  55. package/dist/error/NestedAppAuthError.mjs +1 -1
  56. package/dist/event/EventHandler.mjs +1 -1
  57. package/dist/event/EventMessage.mjs +1 -1
  58. package/dist/event/EventType.mjs +1 -1
  59. package/dist/index.mjs +1 -1
  60. package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
  61. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  62. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  63. package/dist/interaction_client/NativeInteractionClient.mjs +1 -3
  64. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  65. package/dist/interaction_client/PopupClient.d.ts +14 -1
  66. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  67. package/dist/interaction_client/PopupClient.mjs +65 -64
  68. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  69. package/dist/interaction_client/RedirectClient.d.ts +20 -3
  70. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  71. package/dist/interaction_client/RedirectClient.mjs +132 -76
  72. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  73. package/dist/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  74. package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
  75. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  76. package/dist/interaction_client/SilentCacheClient.mjs +1 -1
  77. package/dist/interaction_client/SilentIframeClient.d.ts +13 -3
  78. package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
  79. package/dist/interaction_client/SilentIframeClient.mjs +57 -44
  80. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  81. package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
  82. package/dist/interaction_client/StandardInteractionClient.d.ts +2 -3
  83. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  84. package/dist/interaction_client/StandardInteractionClient.mjs +1 -1
  85. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  86. package/dist/interaction_handler/InteractionHandler.d.ts +4 -5
  87. package/dist/interaction_handler/InteractionHandler.d.ts.map +1 -1
  88. package/dist/interaction_handler/InteractionHandler.mjs +1 -1
  89. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  90. package/dist/interaction_handler/SilentHandler.d.ts +4 -2
  91. package/dist/interaction_handler/SilentHandler.d.ts.map +1 -1
  92. package/dist/interaction_handler/SilentHandler.mjs +13 -3
  93. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  94. package/dist/naa/BridgeError.mjs +1 -1
  95. package/dist/naa/BridgeProxy.mjs +1 -1
  96. package/dist/naa/BridgeStatusCode.mjs +1 -1
  97. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +1 -1
  98. package/dist/navigation/NavigationClient.mjs +1 -1
  99. package/dist/network/FetchClient.mjs +1 -1
  100. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  101. package/dist/operatingcontext/NestedAppOperatingContext.mjs +1 -1
  102. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  103. package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
  104. package/dist/packageMetadata.d.ts +1 -1
  105. package/dist/packageMetadata.mjs +2 -2
  106. package/dist/protocol/Authorize.d.ts +53 -1
  107. package/dist/protocol/Authorize.d.ts.map +1 -1
  108. package/dist/protocol/Authorize.mjs +147 -4
  109. package/dist/protocol/Authorize.mjs.map +1 -1
  110. package/dist/request/AuthorizationUrlRequest.d.ts +2 -4
  111. package/dist/request/AuthorizationUrlRequest.d.ts.map +1 -1
  112. package/dist/request/PopupRequest.d.ts +1 -1
  113. package/dist/request/PopupRequest.d.ts.map +1 -1
  114. package/dist/request/RedirectRequest.d.ts +1 -1
  115. package/dist/request/RedirectRequest.d.ts.map +1 -1
  116. package/dist/request/RequestHelpers.mjs +1 -1
  117. package/dist/request/SsoSilentRequest.d.ts +1 -1
  118. package/dist/request/SsoSilentRequest.d.ts.map +1 -1
  119. package/dist/response/ResponseHandler.mjs +1 -1
  120. package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
  121. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  122. package/dist/utils/BrowserConstants.d.ts +1 -10
  123. package/dist/utils/BrowserConstants.d.ts.map +1 -1
  124. package/dist/utils/BrowserConstants.mjs +2 -11
  125. package/dist/utils/BrowserConstants.mjs.map +1 -1
  126. package/dist/utils/BrowserProtocolUtils.mjs +1 -1
  127. package/dist/utils/BrowserUtils.mjs +1 -1
  128. package/lib/msal-browser.cjs +1595 -1514
  129. package/lib/msal-browser.cjs.map +1 -1
  130. package/lib/msal-browser.js +1595 -1514
  131. package/lib/msal-browser.js.map +1 -1
  132. package/lib/msal-browser.min.js +68 -67
  133. package/lib/types/cache/BrowserCacheManager.d.ts +4 -41
  134. package/lib/types/cache/BrowserCacheManager.d.ts.map +1 -1
  135. package/lib/types/config/Configuration.d.ts.map +1 -1
  136. package/lib/types/controllers/NestedAppAuthController.d.ts +2 -2
  137. package/lib/types/controllers/NestedAppAuthController.d.ts.map +1 -1
  138. package/lib/types/controllers/StandardController.d.ts.map +1 -1
  139. package/lib/types/controllers/UnknownOperatingContextController.d.ts +2 -2
  140. package/lib/types/controllers/UnknownOperatingContextController.d.ts.map +1 -1
  141. package/lib/types/crypto/BrowserCrypto.d.ts +11 -0
  142. package/lib/types/crypto/BrowserCrypto.d.ts.map +1 -1
  143. package/lib/types/error/BrowserAuthError.d.ts +3 -5
  144. package/lib/types/error/BrowserAuthError.d.ts.map +1 -1
  145. package/lib/types/error/BrowserAuthErrorCodes.d.ts +3 -1
  146. package/lib/types/error/BrowserAuthErrorCodes.d.ts.map +1 -1
  147. package/lib/types/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  148. package/lib/types/interaction_client/PopupClient.d.ts +14 -1
  149. package/lib/types/interaction_client/PopupClient.d.ts.map +1 -1
  150. package/lib/types/interaction_client/RedirectClient.d.ts +20 -3
  151. package/lib/types/interaction_client/RedirectClient.d.ts.map +1 -1
  152. package/lib/types/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  153. package/lib/types/interaction_client/SilentIframeClient.d.ts +13 -3
  154. package/lib/types/interaction_client/SilentIframeClient.d.ts.map +1 -1
  155. package/lib/types/interaction_client/StandardInteractionClient.d.ts +2 -3
  156. package/lib/types/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  157. package/lib/types/interaction_handler/InteractionHandler.d.ts +4 -5
  158. package/lib/types/interaction_handler/InteractionHandler.d.ts.map +1 -1
  159. package/lib/types/interaction_handler/SilentHandler.d.ts +4 -2
  160. package/lib/types/interaction_handler/SilentHandler.d.ts.map +1 -1
  161. package/lib/types/packageMetadata.d.ts +1 -1
  162. package/lib/types/protocol/Authorize.d.ts +53 -1
  163. package/lib/types/protocol/Authorize.d.ts.map +1 -1
  164. package/lib/types/request/AuthorizationUrlRequest.d.ts +2 -4
  165. package/lib/types/request/AuthorizationUrlRequest.d.ts.map +1 -1
  166. package/lib/types/request/PopupRequest.d.ts +1 -1
  167. package/lib/types/request/PopupRequest.d.ts.map +1 -1
  168. package/lib/types/request/RedirectRequest.d.ts +1 -1
  169. package/lib/types/request/RedirectRequest.d.ts.map +1 -1
  170. package/lib/types/request/SsoSilentRequest.d.ts +1 -1
  171. package/lib/types/request/SsoSilentRequest.d.ts.map +1 -1
  172. package/lib/types/utils/BrowserConstants.d.ts +1 -10
  173. package/lib/types/utils/BrowserConstants.d.ts.map +1 -1
  174. package/package.json +2 -2
  175. package/src/cache/BrowserCacheManager.ts +30 -205
  176. package/src/config/Configuration.ts +14 -2
  177. package/src/controllers/NestedAppAuthController.ts +2 -0
  178. package/src/controllers/StandardController.ts +35 -24
  179. package/src/controllers/UnknownOperatingContextController.ts +2 -0
  180. package/src/crypto/BrowserCrypto.ts +91 -2
  181. package/src/error/BrowserAuthError.ts +6 -8
  182. package/src/error/BrowserAuthErrorCodes.ts +3 -1
  183. package/src/interaction_client/NativeInteractionClient.ts +0 -2
  184. package/src/interaction_client/PopupClient.ts +163 -119
  185. package/src/interaction_client/RedirectClient.ts +258 -140
  186. package/src/interaction_client/SilentAuthCodeClient.ts +2 -2
  187. package/src/interaction_client/SilentIframeClient.ts +154 -99
  188. package/src/interaction_client/StandardInteractionClient.ts +3 -3
  189. package/src/interaction_handler/InteractionHandler.ts +4 -4
  190. package/src/interaction_handler/SilentHandler.ts +31 -2
  191. package/src/packageMetadata.ts +1 -1
  192. package/src/protocol/Authorize.ts +356 -2
  193. package/src/request/AuthorizationUrlRequest.ts +3 -5
  194. package/src/request/PopupRequest.ts +1 -0
  195. package/src/request/RedirectRequest.ts +1 -0
  196. package/src/request/SsoSilentRequest.ts +1 -0
  197. package/src/utils/BrowserConstants.ts +1 -10
  198. package/dist/interaction_handler/RedirectHandler.d.ts +0 -33
  199. package/dist/interaction_handler/RedirectHandler.d.ts.map +0 -1
  200. package/dist/interaction_handler/RedirectHandler.mjs +0 -144
  201. package/dist/interaction_handler/RedirectHandler.mjs.map +0 -1
  202. package/lib/types/interaction_handler/RedirectHandler.d.ts +0 -33
  203. package/lib/types/interaction_handler/RedirectHandler.d.ts.map +0 -1
  204. package/src/interaction_handler/RedirectHandler.ts +0 -252
@@ -1,8 +1,8 @@
1
- /*! @azure/msal-browser v4.8.0 2025-03-20 */
1
+ /*! @azure/msal-browser v4.9.0 2025-03-25 */
2
2
  'use strict';
3
3
  'use strict';
4
4
 
5
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6
6
  /*
7
7
  * Copyright (c) Microsoft Corporation. All rights reserved.
8
8
  * Licensed under the MIT License.
@@ -37,11 +37,8 @@ const Constants = {
37
37
  PROFILE_SCOPE: "profile",
38
38
  OFFLINE_ACCESS_SCOPE: "offline_access",
39
39
  EMAIL_SCOPE: "email",
40
- // Default response type for authorization code flow
41
- CODE_RESPONSE_TYPE: "code",
42
40
  CODE_GRANT_TYPE: "authorization_code",
43
41
  RT_GRANT_TYPE: "refresh_token",
44
- FRAGMENT_RESPONSE_MODE: "fragment",
45
42
  S256_CODE_CHALLENGE_METHOD: "S256",
46
43
  URL_FORM_CONTENT_TYPE: "application/x-www-form-urlencoded;charset=utf-8",
47
44
  AUTHORIZATION_PENDING: "authorization_pending",
@@ -61,8 +58,6 @@ const Constants = {
61
58
  "login.microsoft.com",
62
59
  "sts.windows.net",
63
60
  ],
64
- TOKEN_RESPONSE_TYPE: "token",
65
- ID_TOKEN_RESPONSE_TYPE: "id_token",
66
61
  SHR_NONCE_VALIDITY: 240,
67
62
  INVALID_INSTANCE: "invalid_instance",
68
63
  };
@@ -146,8 +141,17 @@ const CodeChallengeMethodValues = {
146
141
  PLAIN: "plain",
147
142
  S256: "S256",
148
143
  };
144
+ /**
145
+ * Allowed values for response_type
146
+ */
147
+ const OAuthResponseType = {
148
+ CODE: "code",
149
+ IDTOKEN_TOKEN: "id_token token",
150
+ IDTOKEN_TOKEN_REFRESHTOKEN: "id_token token refresh_token",
151
+ };
149
152
  /**
150
153
  * allowed values for server response type
154
+ * @deprecated Use ResponseMode instead
151
155
  */
152
156
  const ServerResponseType = {
153
157
  QUERY: "query",
@@ -157,7 +161,8 @@ const ServerResponseType = {
157
161
  * allowed values for response_mode
158
162
  */
159
163
  const ResponseMode = {
160
- ...ServerResponseType,
164
+ QUERY: "query",
165
+ FRAGMENT: "fragment",
161
166
  FORM_POST: "form_post",
162
167
  };
163
168
  /**
@@ -299,7 +304,7 @@ const JsonWebTokenTypes = {
299
304
  // Token renewal offset default in seconds
300
305
  const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
301
306
 
302
- /*! @azure/msal-common v15.3.0 2025-03-20 */
307
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
303
308
  /*
304
309
  * Copyright (c) Microsoft Corporation. All rights reserved.
305
310
  * Licensed under the MIT License.
@@ -316,7 +321,7 @@ var AuthErrorCodes = /*#__PURE__*/Object.freeze({
316
321
  unexpectedError: unexpectedError
317
322
  });
318
323
 
319
- /*! @azure/msal-common v15.3.0 2025-03-20 */
324
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
320
325
 
321
326
  /*
322
327
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -365,7 +370,7 @@ function createAuthError(code, additionalMessage) {
365
370
  : AuthErrorMessages[code]);
366
371
  }
367
372
 
368
- /*! @azure/msal-common v15.3.0 2025-03-20 */
373
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
369
374
  /*
370
375
  * Copyright (c) Microsoft Corporation. All rights reserved.
371
376
  * Licensed under the MIT License.
@@ -463,7 +468,7 @@ var ClientAuthErrorCodes = /*#__PURE__*/Object.freeze({
463
468
  userTimeoutReached: userTimeoutReached
464
469
  });
465
470
 
466
- /*! @azure/msal-common v15.3.0 2025-03-20 */
471
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
467
472
 
468
473
  /*
469
474
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -715,7 +720,7 @@ function createClientAuthError(errorCode, additionalMessage) {
715
720
  return new ClientAuthError(errorCode, additionalMessage);
716
721
  }
717
722
 
718
- /*! @azure/msal-common v15.3.0 2025-03-20 */
723
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
719
724
 
720
725
  /*
721
726
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -754,7 +759,7 @@ const DEFAULT_CRYPTO_IMPLEMENTATION = {
754
759
  },
755
760
  };
756
761
 
757
- /*! @azure/msal-common v15.3.0 2025-03-20 */
762
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
758
763
 
759
764
  /*
760
765
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -945,12 +950,12 @@ class Logger {
945
950
  }
946
951
  }
947
952
 
948
- /*! @azure/msal-common v15.3.0 2025-03-20 */
953
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
949
954
  /* eslint-disable header/header */
950
955
  const name$1 = "@azure/msal-common";
951
- const version$1 = "15.3.0";
956
+ const version$1 = "15.4.0";
952
957
 
953
- /*! @azure/msal-common v15.3.0 2025-03-20 */
958
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
954
959
  /*
955
960
  * Copyright (c) Microsoft Corporation. All rights reserved.
956
961
  * Licensed under the MIT License.
@@ -970,7 +975,7 @@ const AzureCloudInstance = {
970
975
  AzureUsGovernment: "https://login.microsoftonline.us",
971
976
  };
972
977
 
973
- /*! @azure/msal-common v15.3.0 2025-03-20 */
978
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
974
979
 
975
980
  /*
976
981
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1031,7 +1036,7 @@ function checkMaxAge(authTime, maxAge) {
1031
1036
  }
1032
1037
  }
1033
1038
 
1034
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1039
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1035
1040
  /*
1036
1041
  * Copyright (c) Microsoft Corporation. All rights reserved.
1037
1042
  * Licensed under the MIT License.
@@ -1086,7 +1091,7 @@ function wasClockTurnedBack(cachedAt) {
1086
1091
  return cachedAtSec > nowSeconds();
1087
1092
  }
1088
1093
 
1089
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1094
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1090
1095
 
1091
1096
  /*
1092
1097
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1413,7 +1418,7 @@ function isAuthorityMetadataExpired(metadata) {
1413
1418
  return metadata.expiresAt <= nowSeconds();
1414
1419
  }
1415
1420
 
1416
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1421
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1417
1422
  /*
1418
1423
  * Copyright (c) Microsoft Corporation. All rights reserved.
1419
1424
  * Licensed under the MIT License.
@@ -1467,7 +1472,7 @@ var ClientConfigurationErrorCodes = /*#__PURE__*/Object.freeze({
1467
1472
  urlParseError: urlParseError
1468
1473
  });
1469
1474
 
1470
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1475
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1471
1476
 
1472
1477
  /*
1473
1478
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1605,7 +1610,7 @@ function createClientConfigurationError(errorCode) {
1605
1610
  return new ClientConfigurationError(errorCode);
1606
1611
  }
1607
1612
 
1608
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1613
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1609
1614
  /*
1610
1615
  * Copyright (c) Microsoft Corporation. All rights reserved.
1611
1616
  * Licensed under the MIT License.
@@ -1702,7 +1707,7 @@ class StringUtils {
1702
1707
  }
1703
1708
  }
1704
1709
 
1705
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1710
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1706
1711
 
1707
1712
  /*
1708
1713
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1893,7 +1898,7 @@ class ScopeSet {
1893
1898
  }
1894
1899
  }
1895
1900
 
1896
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1901
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1897
1902
 
1898
1903
  /*
1899
1904
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1933,7 +1938,7 @@ function buildClientInfoFromHomeAccountId(homeAccountId) {
1933
1938
  };
1934
1939
  }
1935
1940
 
1936
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1941
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1937
1942
  /*
1938
1943
  * Copyright (c) Microsoft Corporation. All rights reserved.
1939
1944
  * Licensed under the MIT License.
@@ -2012,7 +2017,7 @@ function updateAccountTenantProfileData(baseAccountInfo, tenantProfile, idTokenC
2012
2017
  return updatedAccountInfo;
2013
2018
  }
2014
2019
 
2015
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2020
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2016
2021
  /*
2017
2022
  * Copyright (c) Microsoft Corporation. All rights reserved.
2018
2023
  * Licensed under the MIT License.
@@ -2027,7 +2032,7 @@ const AuthorityType = {
2027
2032
  Ciam: 3,
2028
2033
  };
2029
2034
 
2030
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2035
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2031
2036
  /*
2032
2037
  * Copyright (c) Microsoft Corporation. All rights reserved.
2033
2038
  * Licensed under the MIT License.
@@ -2049,7 +2054,7 @@ function getTenantIdFromIdTokenClaims(idTokenClaims) {
2049
2054
  return null;
2050
2055
  }
2051
2056
 
2052
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2057
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2053
2058
  /*
2054
2059
  * Copyright (c) Microsoft Corporation. All rights reserved.
2055
2060
  * Licensed under the MIT License.
@@ -2058,11 +2063,22 @@ function getTenantIdFromIdTokenClaims(idTokenClaims) {
2058
2063
  * Protocol modes supported by MSAL.
2059
2064
  */
2060
2065
  const ProtocolMode = {
2066
+ /**
2067
+ * Auth Code + PKCE with Entra ID (formerly AAD) specific optimizations and features
2068
+ */
2061
2069
  AAD: "AAD",
2070
+ /**
2071
+ * Auth Code + PKCE without Entra ID specific optimizations and features. For use only with non-Microsoft owned authorities.
2072
+ * Support is limited for this mode.
2073
+ */
2062
2074
  OIDC: "OIDC",
2075
+ /**
2076
+ * Encrypted Authorize Response (EAR) with Entra ID specific optimizations and features
2077
+ */
2078
+ EAR: "EAR",
2063
2079
  };
2064
2080
 
2065
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2081
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2066
2082
 
2067
2083
  /*
2068
2084
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2158,11 +2174,11 @@ class AccountEntity {
2158
2174
  if (authority.authorityType === AuthorityType.Adfs) {
2159
2175
  account.authorityType = CacheAccountType.ADFS_ACCOUNT_TYPE;
2160
2176
  }
2161
- else if (authority.protocolMode === ProtocolMode.AAD) {
2162
- account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2177
+ else if (authority.protocolMode === ProtocolMode.OIDC) {
2178
+ account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2163
2179
  }
2164
2180
  else {
2165
- account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2181
+ account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2166
2182
  }
2167
2183
  let clientInfo;
2168
2184
  if (accountDetails.clientInfo && base64Decode) {
@@ -2305,7 +2321,7 @@ class AccountEntity {
2305
2321
  }
2306
2322
  }
2307
2323
 
2308
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2324
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2309
2325
 
2310
2326
  /*
2311
2327
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2340,6 +2356,7 @@ function getDeserializedResponse(responseString) {
2340
2356
  const deserializedHash = Object.fromEntries(new URLSearchParams(normalizedResponse));
2341
2357
  // Check for known response properties
2342
2358
  if (deserializedHash.code ||
2359
+ deserializedHash.ear_jwe ||
2343
2360
  deserializedHash.error ||
2344
2361
  deserializedHash.error_description ||
2345
2362
  deserializedHash.state) {
@@ -2362,7 +2379,7 @@ function mapToQueryString(parameters) {
2362
2379
  return queryParameterArray.join("&");
2363
2380
  }
2364
2381
 
2365
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2382
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2366
2383
 
2367
2384
  /*
2368
2385
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2526,7 +2543,7 @@ class UrlString {
2526
2543
  }
2527
2544
  }
2528
2545
 
2529
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2546
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2530
2547
 
2531
2548
  /*
2532
2549
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2666,7 +2683,7 @@ function getCloudDiscoveryMetadataFromNetworkResponse(response, authorityHost) {
2666
2683
  return null;
2667
2684
  }
2668
2685
 
2669
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2686
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2670
2687
  /*
2671
2688
  * Copyright (c) Microsoft Corporation. All rights reserved.
2672
2689
  * Licensed under the MIT License.
@@ -2674,7 +2691,7 @@ function getCloudDiscoveryMetadataFromNetworkResponse(response, authorityHost) {
2674
2691
  const cacheQuotaExceededErrorCode = "cache_quota_exceeded";
2675
2692
  const cacheUnknownErrorCode = "cache_error_unknown";
2676
2693
 
2677
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2694
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2678
2695
 
2679
2696
  /*
2680
2697
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2701,7 +2718,7 @@ class CacheError extends Error {
2701
2718
  }
2702
2719
  }
2703
2720
 
2704
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2721
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2705
2722
 
2706
2723
  /*
2707
2724
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3886,7 +3903,7 @@ class DefaultStorageClass extends CacheManager {
3886
3903
  }
3887
3904
  }
3888
3905
 
3889
- /*! @azure/msal-common v15.3.0 2025-03-20 */
3906
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
3890
3907
 
3891
3908
  /*
3892
3909
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3985,7 +4002,7 @@ function isOidcProtocolMode(config) {
3985
4002
  return (config.authOptions.authority.options.protocolMode === ProtocolMode.OIDC);
3986
4003
  }
3987
4004
 
3988
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4005
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
3989
4006
  /*
3990
4007
  * Copyright (c) Microsoft Corporation. All rights reserved.
3991
4008
  * Licensed under the MIT License.
@@ -3995,7 +4012,7 @@ const CcsCredentialType = {
3995
4012
  UPN: "UPN",
3996
4013
  };
3997
4014
 
3998
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4015
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
3999
4016
  /*
4000
4017
  * Copyright (c) Microsoft Corporation. All rights reserved.
4001
4018
  * Licensed under the MIT License.
@@ -4041,9 +4058,11 @@ const DOMAIN_HINT = "domain_hint";
4041
4058
  const X_CLIENT_EXTRA_SKU = "x-client-xtra-sku";
4042
4059
  const BROKER_CLIENT_ID = "brk_client_id";
4043
4060
  const BROKER_REDIRECT_URI = "brk_redirect_uri";
4044
- const INSTANCE_AWARE = "instance_aware";
4061
+ const INSTANCE_AWARE = "instance_aware";
4062
+ const EAR_JWK = "ear_jwk";
4063
+ const EAR_JWE_CRYPTO = "ear_jwe_crypto";
4045
4064
 
4046
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4065
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4047
4066
 
4048
4067
  /*
4049
4068
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -4062,10 +4081,12 @@ function instrumentBrokerParams(parameters, correlationId, performanceClient) {
4062
4081
  }
4063
4082
  }
4064
4083
  /**
4065
- * add response_type = code
4084
+ * Add the given response_type
4085
+ * @param parameters
4086
+ * @param responseType
4066
4087
  */
4067
- function addResponseTypeCode(parameters) {
4068
- parameters.set(RESPONSE_TYPE, Constants.CODE_RESPONSE_TYPE);
4088
+ function addResponseType(parameters, responseType) {
4089
+ parameters.set(RESPONSE_TYPE, responseType);
4069
4090
  }
4070
4091
  /**
4071
4092
  * add response_mode. defaults to query.
@@ -4396,9 +4417,20 @@ function addBrokerParameters(parameters, brokerClientId, brokerRedirectUri) {
4396
4417
  if (!parameters.has(BROKER_REDIRECT_URI)) {
4397
4418
  parameters.set(BROKER_REDIRECT_URI, brokerRedirectUri);
4398
4419
  }
4420
+ }
4421
+ /**
4422
+ * Add EAR (Encrypted Authorize Response) request parameters
4423
+ * @param parameters
4424
+ * @param jwk
4425
+ */
4426
+ function addEARParameters(parameters, jwk) {
4427
+ parameters.set(EAR_JWK, encodeURIComponent(jwk));
4428
+ // ear_jwe_crypto will always have value: {"alg":"dir","enc":"A256GCM"} so we can hardcode this
4429
+ const jweCryptoB64Encoded = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0";
4430
+ parameters.set(EAR_JWE_CRYPTO, jweCryptoB64Encoded);
4399
4431
  }
4400
4432
 
4401
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4433
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4402
4434
  /*
4403
4435
  * Copyright (c) Microsoft Corporation. All rights reserved.
4404
4436
  * Licensed under the MIT License.
@@ -4410,7 +4442,7 @@ function isOpenIdConfigResponse(response) {
4410
4442
  response.hasOwnProperty("jwks_uri"));
4411
4443
  }
4412
4444
 
4413
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4445
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4414
4446
  /*
4415
4447
  * Copyright (c) Microsoft Corporation. All rights reserved.
4416
4448
  * Licensed under the MIT License.
@@ -4420,7 +4452,7 @@ function isCloudInstanceDiscoveryResponse(response) {
4420
4452
  response.hasOwnProperty("metadata"));
4421
4453
  }
4422
4454
 
4423
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4455
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4424
4456
  /*
4425
4457
  * Copyright (c) Microsoft Corporation. All rights reserved.
4426
4458
  * Licensed under the MIT License.
@@ -4430,7 +4462,7 @@ function isCloudInstanceDiscoveryErrorResponse(response) {
4430
4462
  response.hasOwnProperty("error_description"));
4431
4463
  }
4432
4464
 
4433
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4465
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4434
4466
  /*
4435
4467
  * Copyright (c) Microsoft Corporation. All rights reserved.
4436
4468
  * Licensed under the MIT License.
@@ -4616,6 +4648,9 @@ const PerformanceEvents = {
4616
4648
  */
4617
4649
  HandleCodeResponseFromServer: "handleCodeResponseFromServer",
4618
4650
  HandleCodeResponse: "handleCodeResponse",
4651
+ HandleResponseEar: "handleResponseEar",
4652
+ HandleResponsePlatformBroker: "handleResponsePlatformBroker",
4653
+ HandleResponseCode: "handleResponseCode",
4619
4654
  UpdateTokenEndpointAuthority: "updateTokenEndpointAuthority",
4620
4655
  /**
4621
4656
  * APIs in Authorization Code Client (msal-common)
@@ -4682,6 +4717,8 @@ const PerformanceEvents = {
4682
4717
  UrlEncodeArr: "urlEncodeArr",
4683
4718
  Encrypt: "encrypt",
4684
4719
  Decrypt: "decrypt",
4720
+ GenerateEarKey: "generateEarKey",
4721
+ DecryptEarResponse: "decryptEarResponse",
4685
4722
  };
4686
4723
  const PerformanceEventAbbreviations = new Map([
4687
4724
  [PerformanceEvents.AcquireTokenByCode, "ATByCode"],
@@ -4799,6 +4836,12 @@ const PerformanceEventAbbreviations = new Map([
4799
4836
  "HandleCodeResFromServer",
4800
4837
  ],
4801
4838
  [PerformanceEvents.HandleCodeResponse, "HandleCodeResp"],
4839
+ [PerformanceEvents.HandleResponseEar, "HandleRespEar"],
4840
+ [PerformanceEvents.HandleResponseCode, "HandleRespCode"],
4841
+ [
4842
+ PerformanceEvents.HandleResponsePlatformBroker,
4843
+ "HandleRespPlatBroker",
4844
+ ],
4802
4845
  [PerformanceEvents.UpdateTokenEndpointAuthority, "UpdTEndpointAuth"],
4803
4846
  [PerformanceEvents.AuthClientAcquireToken, "AuthClientAT"],
4804
4847
  [PerformanceEvents.AuthClientExecuteTokenRequest, "AuthClientExecTReq"],
@@ -4901,6 +4944,8 @@ const PerformanceEventAbbreviations = new Map([
4901
4944
  [PerformanceEvents.UrlEncodeArr, "urlEncArr"],
4902
4945
  [PerformanceEvents.Encrypt, "encrypt"],
4903
4946
  [PerformanceEvents.Decrypt, "decrypt"],
4947
+ [PerformanceEvents.GenerateEarKey, "genEarKey"],
4948
+ [PerformanceEvents.DecryptEarResponse, "decryptEarResp"],
4904
4949
  ]);
4905
4950
  /**
4906
4951
  * State of the performance event.
@@ -4930,7 +4975,7 @@ const IntFields = new Set([
4930
4975
  "encryptedCacheExpiredCount",
4931
4976
  ]);
4932
4977
 
4933
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4978
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4934
4979
  /*
4935
4980
  * Copyright (c) Microsoft Corporation. All rights reserved.
4936
4981
  * Licensed under the MIT License.
@@ -5026,7 +5071,7 @@ const invokeAsync = (callback, eventName, logger, telemetryClient, correlationId
5026
5071
  };
5027
5072
  };
5028
5073
 
5029
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5074
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5030
5075
 
5031
5076
  /*
5032
5077
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5135,7 +5180,7 @@ RegionDiscovery.IMDS_OPTIONS = {
5135
5180
  },
5136
5181
  };
5137
5182
 
5138
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5183
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5139
5184
 
5140
5185
  /*
5141
5186
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5313,7 +5358,7 @@ class Authority {
5313
5358
  return (authorityUri.PathSegments.length === 1 &&
5314
5359
  !Authority.reservedTenantDomains.has(authorityUri.PathSegments[0]) &&
5315
5360
  this.getAuthorityType(authorityUri) === AuthorityType.Default &&
5316
- this.protocolMode === ProtocolMode.AAD);
5361
+ this.protocolMode !== ProtocolMode.OIDC);
5317
5362
  }
5318
5363
  /**
5319
5364
  * Replaces tenant in url path with current tenant. Defaults to common.
@@ -5360,7 +5405,7 @@ class Authority {
5360
5405
  const canonicalAuthorityHost = this.hostnameAndPort;
5361
5406
  if (this.canonicalAuthority.endsWith("v2.0/") ||
5362
5407
  this.authorityType === AuthorityType.Adfs ||
5363
- (this.protocolMode !== ProtocolMode.AAD &&
5408
+ (this.protocolMode === ProtocolMode.OIDC &&
5364
5409
  !this.isAliasOfKnownMicrosoftAuthority(canonicalAuthorityHost))) {
5365
5410
  return `${this.canonicalAuthority}.well-known/openid-configuration`;
5366
5411
  }
@@ -5974,7 +6019,7 @@ function buildStaticAuthorityOptions(authOptions) {
5974
6019
  };
5975
6020
  }
5976
6021
 
5977
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6022
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5978
6023
 
5979
6024
  /*
5980
6025
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6005,7 +6050,7 @@ async function createDiscoveredInstance(authorityUri, networkClient, cacheManage
6005
6050
  }
6006
6051
  }
6007
6052
 
6008
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6053
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6009
6054
 
6010
6055
  /*
6011
6056
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6024,7 +6069,7 @@ class ServerError extends AuthError {
6024
6069
  }
6025
6070
  }
6026
6071
 
6027
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6072
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6028
6073
  /*
6029
6074
  * Copyright (c) Microsoft Corporation. All rights reserved.
6030
6075
  * Licensed under the MIT License.
@@ -6045,7 +6090,7 @@ function getRequestThumbprint(clientId, request, homeAccountId) {
6045
6090
  };
6046
6091
  }
6047
6092
 
6048
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6093
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6049
6094
 
6050
6095
  /*
6051
6096
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6132,7 +6177,7 @@ class ThrottlingUtils {
6132
6177
  }
6133
6178
  }
6134
6179
 
6135
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6180
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6136
6181
 
6137
6182
  /*
6138
6183
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6162,7 +6207,7 @@ function createNetworkError(error, httpStatus, responseHeaders) {
6162
6207
  return new NetworkError(error, httpStatus, responseHeaders);
6163
6208
  }
6164
6209
 
6165
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6210
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6166
6211
 
6167
6212
  /*
6168
6213
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6310,7 +6355,7 @@ class BaseClient {
6310
6355
  }
6311
6356
  }
6312
6357
 
6313
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6358
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6314
6359
  /*
6315
6360
  * Copyright (c) Microsoft Corporation. All rights reserved.
6316
6361
  * Licensed under the MIT License.
@@ -6336,7 +6381,7 @@ var InteractionRequiredAuthErrorCodes = /*#__PURE__*/Object.freeze({
6336
6381
  refreshTokenExpired: refreshTokenExpired
6337
6382
  });
6338
6383
 
6339
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6384
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6340
6385
 
6341
6386
  /*
6342
6387
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6424,7 +6469,7 @@ function createInteractionRequiredAuthError(errorCode) {
6424
6469
  return new InteractionRequiredAuthError(errorCode, InteractionRequiredAuthErrorMessages[errorCode]);
6425
6470
  }
6426
6471
 
6427
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6472
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6428
6473
 
6429
6474
  /*
6430
6475
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6496,7 +6541,7 @@ class ProtocolUtils {
6496
6541
  }
6497
6542
  }
6498
6543
 
6499
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6544
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6500
6545
 
6501
6546
  /*
6502
6547
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6580,7 +6625,7 @@ class PopTokenGenerator {
6580
6625
  }
6581
6626
  }
6582
6627
 
6583
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6628
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6584
6629
  /*
6585
6630
  * Copyright (c) Microsoft Corporation. All rights reserved.
6586
6631
  * Licensed under the MIT License.
@@ -6607,7 +6652,7 @@ class PopTokenGenerator {
6607
6652
  }
6608
6653
  }
6609
6654
 
6610
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6655
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6611
6656
 
6612
6657
  /*
6613
6658
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6938,7 +6983,7 @@ function buildAccountToCache(cacheStorage, authority, homeAccountId, base64Decod
6938
6983
  return baseAccount;
6939
6984
  }
6940
6985
 
6941
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6986
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6942
6987
 
6943
6988
  /*
6944
6989
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7005,7 +7050,7 @@ class RequestValidator {
7005
7050
  }
7006
7051
  }
7007
7052
 
7008
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7053
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7009
7054
  /*
7010
7055
  * Copyright (c) Microsoft Corporation. All rights reserved.
7011
7056
  * Licensed under the MIT License.
@@ -7023,7 +7068,7 @@ async function getClientAssertion(clientAssertion, clientId, tokenEndpoint) {
7023
7068
  }
7024
7069
  }
7025
7070
 
7026
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7071
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7027
7072
 
7028
7073
  /*
7029
7074
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7256,7 +7301,7 @@ class AuthorizationCodeClient extends BaseClient {
7256
7301
  }
7257
7302
  }
7258
7303
 
7259
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7304
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7260
7305
 
7261
7306
  /*
7262
7307
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7465,7 +7510,7 @@ class RefreshTokenClient extends BaseClient {
7465
7510
  }
7466
7511
  }
7467
7512
 
7468
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7513
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7469
7514
 
7470
7515
  /*
7471
7516
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7563,7 +7608,7 @@ class SilentFlowClient extends BaseClient {
7563
7608
  }
7564
7609
  }
7565
7610
 
7566
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7611
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7567
7612
 
7568
7613
  /*
7569
7614
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7578,7 +7623,7 @@ const StubbedNetworkModule = {
7578
7623
  },
7579
7624
  };
7580
7625
 
7581
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7626
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7582
7627
 
7583
7628
  /*
7584
7629
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7707,10 +7752,10 @@ function getStandardAuthorizeRequestParameters(authOptions, request, logger, per
7707
7752
  if (request.embeddedClientId) {
7708
7753
  addBrokerParameters(parameters, authOptions.clientId, authOptions.redirectUri);
7709
7754
  }
7710
- if (request.extraQueryParameters) {
7711
- addExtraQueryParameters(parameters, request.extraQueryParameters);
7712
- }
7713
- if (authOptions.instanceAware) {
7755
+ // If extraQueryParameters includes instance_aware its value will be added when extraQueryParameters are added
7756
+ if (authOptions.instanceAware &&
7757
+ (!request.extraQueryParameters ||
7758
+ !Object.keys(request.extraQueryParameters).includes(INSTANCE_AWARE))) {
7714
7759
  addInstanceAware(parameters);
7715
7760
  }
7716
7761
  return parameters;
@@ -7802,7 +7847,7 @@ function extractLoginHint(account) {
7802
7847
  return account.idTokenClaims?.login_hint || null;
7803
7848
  }
7804
7849
 
7805
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7850
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7806
7851
 
7807
7852
  /*
7808
7853
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7860,7 +7905,7 @@ class AuthenticationHeaderParser {
7860
7905
  }
7861
7906
  }
7862
7907
 
7863
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7908
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7864
7909
 
7865
7910
  /*
7866
7911
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8123,7 +8168,7 @@ class ServerTelemetryManager {
8123
8168
  }
8124
8169
  }
8125
8170
 
8126
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8171
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8127
8172
  /*
8128
8173
  * Copyright (c) Microsoft Corporation. All rights reserved.
8129
8174
  * Licensed under the MIT License.
@@ -8131,7 +8176,7 @@ class ServerTelemetryManager {
8131
8176
  const missingKidError = "missing_kid_error";
8132
8177
  const missingAlgError = "missing_alg_error";
8133
8178
 
8134
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8179
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8135
8180
 
8136
8181
  /*
8137
8182
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8156,7 +8201,7 @@ function createJoseHeaderError(code) {
8156
8201
  return new JoseHeaderError(code, JoseHeaderErrorMessages[code]);
8157
8202
  }
8158
8203
 
8159
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8204
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8160
8205
 
8161
8206
  /*
8162
8207
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8196,7 +8241,7 @@ class JoseHeader {
8196
8241
  }
8197
8242
  }
8198
8243
 
8199
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8244
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8200
8245
 
8201
8246
  /*
8202
8247
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8275,7 +8320,7 @@ class StubPerformanceClient {
8275
8320
  }
8276
8321
  }
8277
8322
 
8278
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8323
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8279
8324
 
8280
8325
  /*
8281
8326
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8889,6 +8934,8 @@ class PerformanceClient {
8889
8934
  * Licensed under the MIT License.
8890
8935
  */
8891
8936
  const pkceNotCreated = "pkce_not_created";
8937
+ const earJwkEmpty = "ear_jwk_empty";
8938
+ const earJweEmpty = "ear_jwe_empty";
8892
8939
  const cryptoNonExistent = "crypto_nonexistent";
8893
8940
  const emptyNavigateUri = "empty_navigate_uri";
8894
8941
  const hashEmptyError = "hash_empty_error";
@@ -8911,7 +8958,6 @@ const noAccountError = "no_account_error";
8911
8958
  const silentPromptValueError = "silent_prompt_value_error";
8912
8959
  const noTokenRequestCacheError = "no_token_request_cache_error";
8913
8960
  const unableToParseTokenRequestCacheError = "unable_to_parse_token_request_cache_error";
8914
- const noCachedAuthorityError = "no_cached_authority_error";
8915
8961
  const authRequestNotSetError = "auth_request_not_set_error";
8916
8962
  const invalidCacheType = "invalid_cache_type";
8917
8963
  const nonBrowserEnvironment = "non_browser_environment";
@@ -8935,7 +8981,8 @@ const nativePromptNotSupported = "native_prompt_not_supported";
8935
8981
  const invalidBase64String = "invalid_base64_string";
8936
8982
  const invalidPopTokenRequest = "invalid_pop_token_request";
8937
8983
  const failedToBuildHeaders = "failed_to_build_headers";
8938
- const failedToParseHeaders = "failed_to_parse_headers";
8984
+ const failedToParseHeaders = "failed_to_parse_headers";
8985
+ const failedToDecryptEarResponse = "failed_to_decrypt_ear_response";
8939
8986
 
8940
8987
  var BrowserAuthErrorCodes = /*#__PURE__*/Object.freeze({
8941
8988
  __proto__: null,
@@ -8948,9 +8995,12 @@ var BrowserAuthErrorCodes = /*#__PURE__*/Object.freeze({
8948
8995
  cryptoNonExistent: cryptoNonExistent,
8949
8996
  databaseNotOpen: databaseNotOpen,
8950
8997
  databaseUnavailable: databaseUnavailable,
8998
+ earJweEmpty: earJweEmpty,
8999
+ earJwkEmpty: earJwkEmpty,
8951
9000
  emptyNavigateUri: emptyNavigateUri,
8952
9001
  emptyWindowError: emptyWindowError,
8953
9002
  failedToBuildHeaders: failedToBuildHeaders,
9003
+ failedToDecryptEarResponse: failedToDecryptEarResponse,
8954
9004
  failedToParseHeaders: failedToParseHeaders,
8955
9005
  failedToParseResponse: failedToParseResponse,
8956
9006
  getRequestFailed: getRequestFailed,
@@ -8968,7 +9018,6 @@ var BrowserAuthErrorCodes = /*#__PURE__*/Object.freeze({
8968
9018
  nativeHandshakeTimeout: nativeHandshakeTimeout,
8969
9019
  nativePromptNotSupported: nativePromptNotSupported,
8970
9020
  noAccountError: noAccountError,
8971
- noCachedAuthorityError: noCachedAuthorityError,
8972
9021
  noNetworkConnectivity: noNetworkConnectivity,
8973
9022
  noStateInHash: noStateInHash,
8974
9023
  noTokenRequestCacheError: noTokenRequestCacheError,
@@ -8999,6 +9048,8 @@ const ErrorLink = "For more visit: aka.ms/msaljs/browser-errors";
8999
9048
  */
9000
9049
  const BrowserAuthErrorMessages = {
9001
9050
  [pkceNotCreated]: "The PKCE code challenge and verifier could not be generated.",
9051
+ [earJwkEmpty]: "No EAR encryption key provided. This is unexpected.",
9052
+ [earJweEmpty]: "Server response does not contain ear_jwe property. This is unexpected.",
9002
9053
  [cryptoNonExistent]: "The crypto object or function is not available.",
9003
9054
  [emptyNavigateUri]: "Navigation URI is empty. Please check stack trace for more info.",
9004
9055
  [hashEmptyError]: `Hash value cannot be processed because it is empty. Please verify that your redirectUri is not clearing the hash. ${ErrorLink}`,
@@ -9021,7 +9072,6 @@ const BrowserAuthErrorMessages = {
9021
9072
  [silentPromptValueError]: "The value given for the prompt value is not valid for silent requests - must be set to 'none' or 'no_session'.",
9022
9073
  [noTokenRequestCacheError]: "No token request found in cache.",
9023
9074
  [unableToParseTokenRequestCacheError]: "The cached token request could not be parsed.",
9024
- [noCachedAuthorityError]: "No cached authority found.",
9025
9075
  [authRequestNotSetError]: "Auth Request not set. Please ensure initiateAuthRequest was called from the InteractionHandler",
9026
9076
  [invalidCacheType]: "Invalid cache type",
9027
9077
  [nonBrowserEnvironment]: "Login and token requests are not supported in non-browser environments.",
@@ -9046,6 +9096,7 @@ const BrowserAuthErrorMessages = {
9046
9096
  [invalidPopTokenRequest]: "Invalid PoP token request. The request should not have both a popKid value and signPopToken set to true.",
9047
9097
  [failedToBuildHeaders]: "Failed to build request headers object.",
9048
9098
  [failedToParseHeaders]: "Failed to parse response headers",
9099
+ [failedToDecryptEarResponse]: "Failed to decrypt ear response",
9049
9100
  };
9050
9101
  /**
9051
9102
  * BrowserAuthErrorMessage class containing string constants used by error codes and messages.
@@ -9148,10 +9199,6 @@ const BrowserAuthErrorMessage = {
9148
9199
  code: unableToParseTokenRequestCacheError,
9149
9200
  desc: BrowserAuthErrorMessages[unableToParseTokenRequestCacheError],
9150
9201
  },
9151
- noCachedAuthorityError: {
9152
- code: noCachedAuthorityError,
9153
- desc: BrowserAuthErrorMessages[noCachedAuthorityError],
9154
- },
9155
9202
  authRequestNotSet: {
9156
9203
  code: authRequestNotSetError,
9157
9204
  desc: BrowserAuthErrorMessages[authRequestNotSetError],
@@ -9319,21 +9366,12 @@ const HTTP_REQUEST_TYPE = {
9319
9366
  * Temporary cache keys for MSAL, deleted after any request.
9320
9367
  */
9321
9368
  const TemporaryCacheKeys = {
9322
- AUTHORITY: "authority",
9323
- ACQUIRE_TOKEN_ACCOUNT: "acquireToken.account",
9324
- SESSION_STATE: "session.state",
9325
- REQUEST_STATE: "request.state",
9326
- NONCE_IDTOKEN: "nonce.id_token",
9327
9369
  ORIGIN_URI: "request.origin",
9328
- RENEW_STATUS: "token.renew.status",
9329
9370
  URL_HASH: "urlHash",
9330
9371
  REQUEST_PARAMS: "request.params",
9331
- SCOPES: "scopes",
9372
+ VERIFIER: "code.verifier",
9332
9373
  INTERACTION_STATUS_KEY: "interaction.status",
9333
- CCS_CREDENTIAL: "ccs.credential",
9334
- CORRELATION_ID: "request.correlationId",
9335
9374
  NATIVE_REQUEST: "request.native",
9336
- REDIRECT_CONTEXT: "request.redirect.context",
9337
9375
  };
9338
9376
  const StaticCacheKeys = {
9339
9377
  ACCOUNT_KEYS: "msal.account.keys",
@@ -9703,6 +9741,69 @@ async function importJwk(key, extractable, usages) {
9703
9741
  async function sign(key, data) {
9704
9742
  return window.crypto.subtle.sign(keygenAlgorithmOptions, key, data);
9705
9743
  }
9744
+ /**
9745
+ * Generates Base64 encoded jwk used in the Encrypted Authorize Response (EAR) flow
9746
+ */
9747
+ async function generateEarKey() {
9748
+ const key = await generateBaseKey();
9749
+ const keyStr = urlEncodeArr(new Uint8Array(key));
9750
+ const jwk = {
9751
+ alg: "dir",
9752
+ kty: "oct",
9753
+ k: keyStr,
9754
+ };
9755
+ return base64Encode(JSON.stringify(jwk));
9756
+ }
9757
+ /**
9758
+ * Parses earJwk for encryption key and returns CryptoKey object
9759
+ * @param earJwk
9760
+ * @returns
9761
+ */
9762
+ async function importEarKey(earJwk) {
9763
+ const b64DecodedJwk = base64Decode(earJwk);
9764
+ const jwkJson = JSON.parse(b64DecodedJwk);
9765
+ const rawKey = jwkJson.k;
9766
+ const keyBuffer = base64DecToArr(rawKey);
9767
+ return window.crypto.subtle.importKey(RAW, keyBuffer, AES_GCM, false, [
9768
+ DECRYPT,
9769
+ ]);
9770
+ }
9771
+ /**
9772
+ * Decrypt ear_jwe response returned in the Encrypted Authorize Response (EAR) flow
9773
+ * @param earJwk
9774
+ * @param earJwe
9775
+ * @returns
9776
+ */
9777
+ async function decryptEarResponse(earJwk, earJwe) {
9778
+ const earJweParts = earJwe.split(".");
9779
+ if (earJweParts.length !== 5) {
9780
+ throw createBrowserAuthError(failedToDecryptEarResponse, "jwe_length");
9781
+ }
9782
+ const key = await importEarKey(earJwk).catch(() => {
9783
+ throw createBrowserAuthError(failedToDecryptEarResponse, "import_key");
9784
+ });
9785
+ try {
9786
+ const header = new TextEncoder().encode(earJweParts[0]);
9787
+ const iv = base64DecToArr(earJweParts[2]);
9788
+ const ciphertext = base64DecToArr(earJweParts[3]);
9789
+ const tag = base64DecToArr(earJweParts[4]);
9790
+ const tagLengthBits = tag.byteLength * 8;
9791
+ // Concat ciphertext and tag
9792
+ const encryptedData = new Uint8Array(ciphertext.length + tag.length);
9793
+ encryptedData.set(ciphertext);
9794
+ encryptedData.set(tag, ciphertext.length);
9795
+ const decryptedData = await window.crypto.subtle.decrypt({
9796
+ name: AES_GCM,
9797
+ iv: iv,
9798
+ tagLength: tagLengthBits,
9799
+ additionalData: header,
9800
+ }, key, encryptedData);
9801
+ return new TextDecoder().decode(decryptedData);
9802
+ }
9803
+ catch (e) {
9804
+ throw createBrowserAuthError(failedToDecryptEarResponse, "decrypt");
9805
+ }
9806
+ }
9706
9807
  /**
9707
9808
  * Generates symmetric base encryption key. This may be stored as all encryption/decryption keys will be derived from this one.
9708
9809
  */
@@ -10288,9 +10389,9 @@ function buildConfiguration({ auth: userInputAuth, cache: userInputCache, system
10288
10389
  const logger = new Logger(providedSystemOptions.loggerOptions);
10289
10390
  logger.warning(JSON.stringify(createClientConfigurationError(cannotSetOIDCOptions)));
10290
10391
  }
10291
- // Throw an error if user has set allowPlatformBroker to true without being in AAD protocol mode
10392
+ // Throw an error if user has set allowPlatformBroker to true with OIDC protocol mode
10292
10393
  if (userInputAuth?.protocolMode &&
10293
- userInputAuth.protocolMode !== ProtocolMode.AAD &&
10394
+ userInputAuth.protocolMode === ProtocolMode.OIDC &&
10294
10395
  providedSystemOptions?.allowPlatformBroker) {
10295
10396
  throw createClientConfigurationError(cannotAllowPlatformBroker);
10296
10397
  }
@@ -10307,12 +10408,21 @@ function buildConfiguration({ auth: userInputAuth, cache: userInputCache, system
10307
10408
  system: providedSystemOptions,
10308
10409
  telemetry: { ...DEFAULT_TELEMETRY_OPTIONS, ...userInputTelemetry },
10309
10410
  };
10411
+ /**
10412
+ * Temporarily disable EAR until implementation is complete
10413
+ * TODO: Remove this
10414
+ */
10415
+ if (overlayedConfig.auth.protocolMode === ProtocolMode.EAR) {
10416
+ const logger = new Logger(providedSystemOptions.loggerOptions);
10417
+ logger.warning("EAR Protocol Mode is not yet supported. Overriding to use PKCE auth");
10418
+ overlayedConfig.auth.protocolMode = ProtocolMode.AAD;
10419
+ }
10310
10420
  return overlayedConfig;
10311
10421
  }
10312
10422
 
10313
10423
  /* eslint-disable header/header */
10314
10424
  const name = "@azure/msal-browser";
10315
- const version = "4.8.0";
10425
+ const version = "4.9.0";
10316
10426
 
10317
10427
  /*
10318
10428
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -11633,28 +11743,6 @@ class SessionStorage {
11633
11743
  }
11634
11744
  }
11635
11745
 
11636
- /*
11637
- * Copyright (c) Microsoft Corporation. All rights reserved.
11638
- * Licensed under the MIT License.
11639
- */
11640
- /**
11641
- * Extracts the BrowserStateObject from the state string.
11642
- * @param browserCrypto
11643
- * @param state
11644
- */
11645
- function extractBrowserRequestState(browserCrypto, state) {
11646
- if (!state) {
11647
- return null;
11648
- }
11649
- try {
11650
- const requestStateObj = ProtocolUtils.parseRequestState(browserCrypto, state);
11651
- return requestStateObj.libraryState.meta;
11652
- }
11653
- catch (e) {
11654
- throw createClientAuthError(invalidState);
11655
- }
11656
- }
11657
-
11658
11746
  /*
11659
11747
  * Copyright (c) Microsoft Corporation. All rights reserved.
11660
11748
  * Licensed under the MIT License.
@@ -12354,177 +12442,53 @@ class BrowserCacheManager extends CacheManager {
12354
12442
  }
12355
12443
  return JSON.stringify(key);
12356
12444
  }
12357
- /**
12358
- * Create authorityKey to cache authority
12359
- * @param state
12360
- */
12361
- generateAuthorityKey(stateString) {
12362
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12363
- return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
12364
- }
12365
- /**
12366
- * Create Nonce key to cache nonce
12367
- * @param state
12368
- */
12369
- generateNonceKey(stateString) {
12370
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12371
- return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
12372
- }
12373
- /**
12374
- * Creates full cache key for the request state
12375
- * @param stateString State string for the request
12376
- */
12377
- generateStateKey(stateString) {
12378
- // Use the library state id to key temp storage for uniqueness for multiple concurrent requests
12379
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12380
- return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
12381
- }
12382
- /**
12383
- * Gets the cached authority based on the cached state. Returns empty if no cached state found.
12384
- */
12385
- getCachedAuthority(cachedState) {
12386
- const stateCacheKey = this.generateStateKey(cachedState);
12387
- const state = this.getTemporaryCache(stateCacheKey);
12388
- if (!state) {
12389
- return null;
12390
- }
12391
- const authorityCacheKey = this.generateAuthorityKey(state);
12392
- return this.getTemporaryCache(authorityCacheKey);
12393
- }
12394
- /**
12395
- * Updates account, authority, and state in cache
12396
- * @param serverAuthenticationRequest
12397
- * @param account
12398
- */
12399
- updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
12400
- this.logger.trace("BrowserCacheManager.updateCacheEntries called");
12401
- // Cache the request state
12402
- const stateCacheKey = this.generateStateKey(state);
12403
- this.setTemporaryCache(stateCacheKey, state, false);
12404
- // Cache the nonce
12405
- const nonceCacheKey = this.generateNonceKey(state);
12406
- this.setTemporaryCache(nonceCacheKey, nonce, false);
12407
- // Cache authorityKey
12408
- const authorityCacheKey = this.generateAuthorityKey(state);
12409
- this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
12410
- if (account) {
12411
- const ccsCredential = {
12412
- credential: account.homeAccountId,
12413
- type: CcsCredentialType.HOME_ACCOUNT_ID,
12414
- };
12415
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
12416
- }
12417
- else if (loginHint) {
12418
- const ccsCredential = {
12419
- credential: loginHint,
12420
- type: CcsCredentialType.UPN,
12421
- };
12422
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
12423
- }
12424
- }
12425
12445
  /**
12426
12446
  * Reset all temporary cache items
12427
12447
  * @param state
12428
12448
  */
12429
- resetRequestCache(state) {
12449
+ resetRequestCache() {
12430
12450
  this.logger.trace("BrowserCacheManager.resetRequestCache called");
12431
- // check state and remove associated cache items
12432
- if (state) {
12433
- this.temporaryCacheStorage.getKeys().forEach((key) => {
12434
- if (key.indexOf(state) !== -1) {
12435
- this.removeTemporaryItem(key);
12436
- }
12437
- });
12438
- // delete generic interactive request parameters
12439
- this.removeTemporaryItem(this.generateStateKey(state));
12440
- this.removeTemporaryItem(this.generateNonceKey(state));
12441
- this.removeTemporaryItem(this.generateAuthorityKey(state));
12442
- }
12443
12451
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
12452
+ this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.VERIFIER));
12444
12453
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
12445
12454
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
12446
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
12447
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
12448
12455
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
12449
12456
  this.setInteractionInProgress(false);
12450
12457
  }
12451
- /**
12452
- * Removes temporary cache for the provided state
12453
- * @param stateString
12454
- */
12455
- cleanRequestByState(stateString) {
12456
- this.logger.trace("BrowserCacheManager.cleanRequestByState called");
12457
- // Interaction is completed - remove interaction status.
12458
- if (stateString) {
12459
- const stateKey = this.generateStateKey(stateString);
12460
- const cachedState = this.temporaryCacheStorage.getItem(stateKey);
12461
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
12462
- this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
12463
- }
12464
- }
12465
- /**
12466
- * Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
12467
- * Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
12468
- * @param interactionType
12469
- */
12470
- cleanRequestByInteractionType(interactionType) {
12471
- this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
12472
- // Loop through all keys to find state key
12473
- this.temporaryCacheStorage.getKeys().forEach((key) => {
12474
- // If this key is not the state key, move on
12475
- if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
12476
- return;
12477
- }
12478
- // Retrieve state value, return if not a valid value
12479
- const stateValue = this.temporaryCacheStorage.getItem(key);
12480
- if (!stateValue) {
12481
- return;
12482
- }
12483
- // Extract state and ensure it matches given InteractionType, then clean request cache
12484
- const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
12485
- if (parsedState &&
12486
- parsedState.interactionType === interactionType) {
12487
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
12488
- this.resetRequestCache(stateValue);
12489
- }
12490
- });
12491
- this.setInteractionInProgress(false);
12492
- }
12493
- cacheCodeRequest(authCodeRequest) {
12494
- this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
12458
+ cacheAuthorizeRequest(authCodeRequest, codeVerifier) {
12459
+ this.logger.trace("BrowserCacheManager.cacheAuthorizeRequest called");
12495
12460
  const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
12496
12461
  this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
12462
+ if (codeVerifier) {
12463
+ const encodedVerifier = base64Encode(codeVerifier);
12464
+ this.setTemporaryCache(TemporaryCacheKeys.VERIFIER, encodedVerifier, true);
12465
+ }
12497
12466
  }
12498
12467
  /**
12499
12468
  * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
12500
12469
  */
12501
- getCachedRequest(state) {
12470
+ getCachedRequest() {
12502
12471
  this.logger.trace("BrowserCacheManager.getCachedRequest called");
12503
12472
  // Get token request from cache and parse as TokenExchangeParameters.
12504
12473
  const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
12505
12474
  if (!encodedTokenRequest) {
12506
12475
  throw createBrowserAuthError(noTokenRequestCacheError);
12507
12476
  }
12477
+ const encodedVerifier = this.getTemporaryCache(TemporaryCacheKeys.VERIFIER, true);
12508
12478
  let parsedRequest;
12479
+ let verifier = "";
12509
12480
  try {
12510
12481
  parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
12482
+ if (encodedVerifier) {
12483
+ verifier = base64Decode(encodedVerifier);
12484
+ }
12511
12485
  }
12512
12486
  catch (e) {
12513
12487
  this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
12514
12488
  this.logger.error(`Parsing cached token request threw with error: ${e}`);
12515
12489
  throw createBrowserAuthError(unableToParseTokenRequestCacheError);
12516
12490
  }
12517
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
12518
- // Get cached authority and use if no authority is cached with request.
12519
- if (!parsedRequest.authority) {
12520
- const authorityCacheKey = this.generateAuthorityKey(state);
12521
- const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
12522
- if (!cachedAuthority) {
12523
- throw createBrowserAuthError(noCachedAuthorityError);
12524
- }
12525
- parsedRequest.authority = cachedAuthority;
12526
- }
12527
- return parsedRequest;
12491
+ return [parsedRequest, verifier];
12528
12492
  }
12529
12493
  /**
12530
12494
  * Gets cached native request for redirect flows
@@ -13350,1083 +13314,1103 @@ function createNativeAuthError(code, description, ext) {
13350
13314
  * Copyright (c) Microsoft Corporation. All rights reserved.
13351
13315
  * Licensed under the MIT License.
13352
13316
  */
13353
- class SilentCacheClient extends StandardInteractionClient {
13317
+ class NativeMessageHandler {
13318
+ constructor(logger, handshakeTimeoutMs, performanceClient, extensionId) {
13319
+ this.logger = logger;
13320
+ this.handshakeTimeoutMs = handshakeTimeoutMs;
13321
+ this.extensionId = extensionId;
13322
+ this.resolvers = new Map(); // Used for non-handshake messages
13323
+ this.handshakeResolvers = new Map(); // Used for handshake messages
13324
+ this.messageChannel = new MessageChannel();
13325
+ this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
13326
+ this.performanceClient = performanceClient;
13327
+ this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
13328
+ }
13354
13329
  /**
13355
- * Returns unexpired tokens from the cache, if available
13356
- * @param silentRequest
13330
+ * Sends a given message to the extension and resolves with the extension response
13331
+ * @param body
13357
13332
  */
13358
- async acquireToken(silentRequest) {
13359
- this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentCacheClientAcquireToken, silentRequest.correlationId);
13360
- // Telemetry manager only used to increment cacheHits here
13361
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent_silentFlow);
13362
- const clientConfig = await invokeAsync(this.getClientConfiguration.bind(this), PerformanceEvents.StandardInteractionClientGetClientConfiguration, this.logger, this.performanceClient, this.correlationId)({
13363
- serverTelemetryManager,
13364
- requestAuthority: silentRequest.authority,
13365
- requestAzureCloudOptions: silentRequest.azureCloudOptions,
13366
- account: silentRequest.account,
13333
+ async sendMessage(body) {
13334
+ this.logger.trace("NativeMessageHandler - sendMessage called.");
13335
+ const req = {
13336
+ channel: NativeConstants.CHANNEL_ID,
13337
+ extensionId: this.extensionId,
13338
+ responseId: createNewGuid(),
13339
+ body: body,
13340
+ };
13341
+ this.logger.trace("NativeMessageHandler - Sending request to browser extension");
13342
+ this.logger.tracePii(`NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(req)}`);
13343
+ this.messageChannel.port1.postMessage(req);
13344
+ return new Promise((resolve, reject) => {
13345
+ this.resolvers.set(req.responseId, { resolve, reject });
13367
13346
  });
13368
- const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13369
- this.logger.verbose("Silent auth client created");
13347
+ }
13348
+ /**
13349
+ * Returns an instance of the MessageHandler that has successfully established a connection with an extension
13350
+ * @param {Logger} logger
13351
+ * @param {number} handshakeTimeoutMs
13352
+ * @param {IPerformanceClient} performanceClient
13353
+ * @param {ICrypto} crypto
13354
+ */
13355
+ static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
13356
+ logger.trace("NativeMessageHandler - createProvider called.");
13370
13357
  try {
13371
- const response = await invokeAsync(silentAuthClient.acquireCachedToken.bind(silentAuthClient), PerformanceEvents.SilentFlowClientAcquireCachedToken, this.logger, this.performanceClient, silentRequest.correlationId)(silentRequest);
13372
- const authResponse = response[0];
13373
- this.performanceClient.addFields({
13374
- fromCache: true,
13375
- }, silentRequest.correlationId);
13376
- return authResponse;
13358
+ const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
13359
+ await preferredProvider.sendHandshakeRequest();
13360
+ return preferredProvider;
13377
13361
  }
13378
- catch (error) {
13379
- if (error instanceof BrowserAuthError &&
13380
- error.errorCode === cryptoKeyNotFound) {
13381
- this.logger.verbose("Signing keypair for bound access token not found. Refreshing bound access token and generating a new crypto keypair.");
13382
- }
13383
- throw error;
13362
+ catch (e) {
13363
+ // If preferred extension fails for whatever reason, fallback to using any installed extension
13364
+ const backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
13365
+ await backupProvider.sendHandshakeRequest();
13366
+ return backupProvider;
13384
13367
  }
13385
13368
  }
13386
13369
  /**
13387
- * API to silenty clear the browser cache.
13388
- * @param logoutRequest
13370
+ * Send handshake request helper.
13389
13371
  */
13390
- logout(logoutRequest) {
13391
- this.logger.verbose("logoutRedirect called");
13392
- const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
13393
- return this.clearCacheOnLogout(validLogoutRequest?.account);
13394
- }
13395
- }
13396
-
13397
- /*
13398
- * Copyright (c) Microsoft Corporation. All rights reserved.
13399
- * Licensed under the MIT License.
13400
- */
13401
- class NativeInteractionClient extends BaseInteractionClient {
13402
- constructor(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, provider, accountId, nativeStorageImpl, correlationId) {
13403
- super(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13404
- this.apiId = apiId;
13405
- this.accountId = accountId;
13406
- this.nativeMessageHandler = provider;
13407
- this.nativeStorageManager = nativeStorageImpl;
13408
- this.silentCacheClient = new SilentCacheClient(config, this.nativeStorageManager, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13409
- const extensionName = this.nativeMessageHandler.getExtensionId() ===
13410
- NativeConstants.PREFERRED_EXTENSION_ID
13411
- ? "chrome"
13412
- : this.nativeMessageHandler.getExtensionId()?.length
13413
- ? "unknown"
13414
- : undefined;
13415
- this.skus = ServerTelemetryManager.makeExtraSkuString({
13416
- libraryName: BrowserConstants.MSAL_SKU,
13417
- libraryVersion: version,
13418
- extensionName: extensionName,
13419
- extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
13372
+ async sendHandshakeRequest() {
13373
+ this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
13374
+ // Register this event listener before sending handshake
13375
+ window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
13376
+ const req = {
13377
+ channel: NativeConstants.CHANNEL_ID,
13378
+ extensionId: this.extensionId,
13379
+ responseId: createNewGuid(),
13380
+ body: {
13381
+ method: NativeExtensionMethod.HandshakeRequest,
13382
+ },
13383
+ };
13384
+ this.handshakeEvent.add({
13385
+ extensionId: this.extensionId,
13386
+ extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
13420
13387
  });
13421
- }
13422
- /**
13423
- * Adds SKUs to request extra query parameters
13424
- * @param request {NativeTokenRequest}
13425
- * @private
13426
- */
13427
- addRequestSKUs(request) {
13428
- request.extraParameters = {
13429
- ...request.extraParameters,
13430
- [X_CLIENT_EXTRA_SKU]: this.skus,
13388
+ this.messageChannel.port1.onmessage = (event) => {
13389
+ this.onChannelMessage(event);
13431
13390
  };
13432
- }
13433
- /**
13434
- * Acquire token from native platform via browser extension
13435
- * @param request
13436
- */
13437
- async acquireToken(request, cacheLookupPolicy) {
13438
- this.performanceClient.addQueueMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13439
- this.logger.trace("NativeInteractionClient - acquireToken called.");
13440
- // start the perf measurement
13441
- const nativeATMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13442
- const reqTimestamp = nowSeconds();
13443
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13444
- try {
13445
- // initialize native request
13446
- const nativeRequest = await this.initializeNativeRequest(request);
13447
- // check if the tokens can be retrieved from internal cache
13448
- try {
13449
- const result = await this.acquireTokensFromCache(this.accountId, nativeRequest);
13450
- nativeATMeasurement.end({
13451
- success: true,
13452
- isNativeBroker: false,
13453
- fromCache: true,
13454
- });
13455
- return result;
13456
- }
13457
- catch (e) {
13458
- if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
13459
- this.logger.info("MSAL internal Cache does not contain tokens, return error as per cache policy");
13460
- throw e;
13461
- }
13462
- // continue with a native call for any and all errors
13463
- this.logger.info("MSAL internal Cache does not contain tokens, proceed to make a native call");
13464
- }
13465
- const { ...nativeTokenRequest } = nativeRequest;
13466
- // fall back to native calls
13467
- const messageBody = {
13468
- method: NativeExtensionMethod.GetToken,
13469
- request: nativeTokenRequest,
13470
- };
13471
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13472
- const validatedResponse = this.validateNativeResponse(response);
13473
- return await this.handleNativeResponse(validatedResponse, nativeRequest, reqTimestamp)
13474
- .then((result) => {
13475
- nativeATMeasurement.end({
13476
- success: true,
13477
- isNativeBroker: true,
13478
- requestId: result.requestId,
13479
- });
13480
- serverTelemetryManager.clearNativeBrokerErrorCode();
13481
- return result;
13482
- })
13483
- .catch((error) => {
13484
- nativeATMeasurement.end({
13391
+ window.postMessage(req, window.origin, [this.messageChannel.port2]);
13392
+ return new Promise((resolve, reject) => {
13393
+ this.handshakeResolvers.set(req.responseId, { resolve, reject });
13394
+ this.timeoutId = window.setTimeout(() => {
13395
+ /*
13396
+ * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
13397
+ * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
13398
+ */
13399
+ window.removeEventListener("message", this.windowListener, false);
13400
+ this.messageChannel.port1.close();
13401
+ this.messageChannel.port2.close();
13402
+ this.handshakeEvent.end({
13403
+ extensionHandshakeTimedOut: true,
13485
13404
  success: false,
13486
- errorCode: error.errorCode,
13487
- subErrorCode: error.subError,
13488
- isNativeBroker: true,
13489
13405
  });
13490
- throw error;
13491
- });
13492
- }
13493
- catch (e) {
13494
- if (e instanceof NativeAuthError) {
13495
- serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13496
- }
13497
- throw e;
13498
- }
13499
- }
13500
- /**
13501
- * Creates silent flow request
13502
- * @param request
13503
- * @param cachedAccount
13504
- * @returns CommonSilentFlowRequest
13505
- */
13506
- createSilentCacheRequest(request, cachedAccount) {
13507
- return {
13508
- authority: request.authority,
13509
- correlationId: this.correlationId,
13510
- scopes: ScopeSet.fromString(request.scope).asArray(),
13511
- account: cachedAccount,
13512
- forceRefresh: false,
13513
- };
13406
+ reject(createBrowserAuthError(nativeHandshakeTimeout));
13407
+ this.handshakeResolvers.delete(req.responseId);
13408
+ }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
13409
+ });
13514
13410
  }
13515
13411
  /**
13516
- * Fetches the tokens from the cache if un-expired
13517
- * @param nativeAccountId
13518
- * @param request
13519
- * @returns authenticationResult
13412
+ * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
13413
+ * @param event
13520
13414
  */
13521
- async acquireTokensFromCache(nativeAccountId, request) {
13522
- if (!nativeAccountId) {
13523
- this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13524
- throw createClientAuthError(noAccountFound);
13415
+ onWindowMessage(event) {
13416
+ this.logger.trace("NativeMessageHandler - onWindowMessage called");
13417
+ // We only accept messages from ourselves
13418
+ if (event.source !== window) {
13419
+ return;
13525
13420
  }
13526
- // fetch the account from browser cache
13527
- const account = this.browserStorage.getBaseAccountInfo({
13528
- nativeAccountId,
13529
- });
13530
- if (!account) {
13531
- throw createClientAuthError(noAccountFound);
13421
+ const request = event.data;
13422
+ if (!request.channel ||
13423
+ request.channel !== NativeConstants.CHANNEL_ID) {
13424
+ return;
13532
13425
  }
13533
- // leverage silent flow for cached tokens retrieval
13534
- try {
13535
- const silentRequest = this.createSilentCacheRequest(request, account);
13536
- const result = await this.silentCacheClient.acquireToken(silentRequest);
13537
- const fullAccount = {
13538
- ...account,
13539
- idTokenClaims: result?.idTokenClaims,
13540
- idToken: result?.idToken,
13541
- };
13542
- return {
13543
- ...result,
13544
- account: fullAccount,
13545
- };
13426
+ if (request.extensionId && request.extensionId !== this.extensionId) {
13427
+ return;
13546
13428
  }
13547
- catch (e) {
13548
- throw e;
13429
+ if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
13430
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13431
+ /*
13432
+ * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
13433
+ * the proper response.
13434
+ */
13435
+ if (!handshakeResolver) {
13436
+ this.logger.trace(`NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`);
13437
+ return;
13438
+ }
13439
+ // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
13440
+ this.logger.verbose(request.extensionId
13441
+ ? `Extension with id: ${request.extensionId} not installed`
13442
+ : "No extension installed");
13443
+ clearTimeout(this.timeoutId);
13444
+ this.messageChannel.port1.close();
13445
+ this.messageChannel.port2.close();
13446
+ window.removeEventListener("message", this.windowListener, false);
13447
+ this.handshakeEvent.end({
13448
+ success: false,
13449
+ extensionInstalled: false,
13450
+ });
13451
+ handshakeResolver.reject(createBrowserAuthError(nativeExtensionNotInstalled));
13549
13452
  }
13550
13453
  }
13551
13454
  /**
13552
- * Acquires a token from native platform then redirects to the redirectUri instead of returning the response
13553
- * @param {RedirectRequest} request
13554
- * @param {InProgressPerformanceEvent} rootMeasurement
13455
+ * Invoked when a message is received from the extension on the MessageChannel port
13456
+ * @param event
13555
13457
  */
13556
- async acquireTokenRedirect(request, rootMeasurement) {
13557
- this.logger.trace("NativeInteractionClient - acquireTokenRedirect called.");
13558
- const { ...remainingParameters } = request;
13559
- delete remainingParameters.onRedirectNavigate;
13560
- const nativeRequest = await this.initializeNativeRequest(remainingParameters);
13561
- const messageBody = {
13562
- method: NativeExtensionMethod.GetToken,
13563
- request: nativeRequest,
13564
- };
13458
+ onChannelMessage(event) {
13459
+ this.logger.trace("NativeMessageHandler - onChannelMessage called.");
13460
+ const request = event.data;
13461
+ const resolver = this.resolvers.get(request.responseId);
13462
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13565
13463
  try {
13566
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13567
- this.validateNativeResponse(response);
13568
- }
13569
- catch (e) {
13570
- // Only throw fatal errors here to allow application to fallback to regular redirect. Otherwise proceed and the error will be thrown in handleRedirectPromise
13571
- if (e instanceof NativeAuthError) {
13572
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13573
- serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13574
- if (isFatalNativeAuthError(e)) {
13575
- throw e;
13464
+ const method = request.body.method;
13465
+ if (method === NativeExtensionMethod.Response) {
13466
+ if (!resolver) {
13467
+ return;
13468
+ }
13469
+ const response = request.body.response;
13470
+ this.logger.trace("NativeMessageHandler - Received response from browser extension");
13471
+ this.logger.tracePii(`NativeMessageHandler - Received response from browser extension: ${JSON.stringify(response)}`);
13472
+ if (response.status !== "Success") {
13473
+ resolver.reject(createNativeAuthError(response.code, response.description, response.ext));
13474
+ }
13475
+ else if (response.result) {
13476
+ if (response.result["code"] &&
13477
+ response.result["description"]) {
13478
+ resolver.reject(createNativeAuthError(response.result["code"], response.result["description"], response.result["ext"]));
13479
+ }
13480
+ else {
13481
+ resolver.resolve(response.result);
13482
+ }
13483
+ }
13484
+ else {
13485
+ throw createAuthError(unexpectedError, "Event does not contain result.");
13576
13486
  }
13487
+ this.resolvers.delete(request.responseId);
13488
+ }
13489
+ else if (method === NativeExtensionMethod.HandshakeResponse) {
13490
+ if (!handshakeResolver) {
13491
+ this.logger.trace(`NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`);
13492
+ return;
13493
+ }
13494
+ clearTimeout(this.timeoutId); // Clear setTimeout
13495
+ window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
13496
+ this.extensionId = request.extensionId;
13497
+ this.extensionVersion = request.body.version;
13498
+ this.logger.verbose(`NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`);
13499
+ this.handshakeEvent.end({
13500
+ extensionInstalled: true,
13501
+ success: true,
13502
+ });
13503
+ handshakeResolver.resolve();
13504
+ this.handshakeResolvers.delete(request.responseId);
13505
+ }
13506
+ // Do nothing if method is not Response or HandshakeResponse
13507
+ }
13508
+ catch (err) {
13509
+ this.logger.error("Error parsing response from WAM Extension");
13510
+ this.logger.errorPii(`Error parsing response from WAM Extension: ${err}`);
13511
+ this.logger.errorPii(`Unable to parse ${event}`);
13512
+ if (resolver) {
13513
+ resolver.reject(err);
13514
+ }
13515
+ else if (handshakeResolver) {
13516
+ handshakeResolver.reject(err);
13577
13517
  }
13578
13518
  }
13579
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, JSON.stringify(nativeRequest), true);
13580
- const navigationOptions = {
13581
- apiId: ApiId.acquireTokenRedirect,
13582
- timeout: this.config.system.redirectNavigationTimeout,
13583
- noHistory: false,
13584
- };
13585
- const redirectUri = this.config.auth.navigateToLoginRequestUrl
13586
- ? window.location.href
13587
- : this.getRedirectUri(request.redirectUri);
13588
- rootMeasurement.end({ success: true });
13589
- await this.navigationClient.navigateExternal(redirectUri, navigationOptions); // Need to treat this as external to ensure handleRedirectPromise is run again
13590
13519
  }
13591
13520
  /**
13592
- * If the previous page called native platform for a token using redirect APIs, send the same request again and return the response
13593
- * @param performanceClient {IPerformanceClient?}
13594
- * @param correlationId {string?} correlation identifier
13521
+ * Returns the Id for the browser extension this handler is communicating with
13522
+ * @returns
13595
13523
  */
13596
- async handleRedirectPromise(performanceClient, correlationId) {
13597
- this.logger.trace("NativeInteractionClient - handleRedirectPromise called.");
13598
- if (!this.browserStorage.isInteractionInProgress(true)) {
13599
- this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
13600
- return null;
13601
- }
13602
- // remove prompt from the request to prevent WAM from prompting twice
13603
- const cachedRequest = this.browserStorage.getCachedNativeRequest();
13604
- if (!cachedRequest) {
13605
- this.logger.verbose("NativeInteractionClient - handleRedirectPromise called but there is no cached request, returning null.");
13606
- if (performanceClient && correlationId) {
13607
- performanceClient?.addFields({ errorCode: "no_cached_request" }, correlationId);
13608
- }
13609
- return null;
13610
- }
13611
- const { prompt, ...request } = cachedRequest;
13612
- if (prompt) {
13613
- this.logger.verbose("NativeInteractionClient - handleRedirectPromise called and prompt was included in the original request, removing prompt from cached request to prevent second interaction with native broker window.");
13614
- }
13615
- this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
13616
- const messageBody = {
13617
- method: NativeExtensionMethod.GetToken,
13618
- request: request,
13619
- };
13620
- const reqTimestamp = nowSeconds();
13621
- try {
13622
- this.logger.verbose("NativeInteractionClient - handleRedirectPromise sending message to native broker.");
13623
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13624
- this.validateNativeResponse(response);
13625
- const result = this.handleNativeResponse(response, request, reqTimestamp);
13626
- this.browserStorage.setInteractionInProgress(false);
13627
- const res = await result;
13628
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13629
- serverTelemetryManager.clearNativeBrokerErrorCode();
13630
- return res;
13631
- }
13632
- catch (e) {
13633
- this.browserStorage.setInteractionInProgress(false);
13634
- throw e;
13635
- }
13524
+ getExtensionId() {
13525
+ return this.extensionId;
13636
13526
  }
13637
13527
  /**
13638
- * Logout from native platform via browser extension
13639
- * @param request
13528
+ * Returns the version for the browser extension this handler is communicating with
13529
+ * @returns
13640
13530
  */
13641
- logout() {
13642
- this.logger.trace("NativeInteractionClient - logout called.");
13643
- return Promise.reject("Logout not implemented yet");
13531
+ getExtensionVersion() {
13532
+ return this.extensionVersion;
13644
13533
  }
13645
13534
  /**
13646
- * Transform response from native platform into AuthenticationResult object which will be returned to the end user
13647
- * @param response
13648
- * @param request
13649
- * @param reqTimestamp
13535
+ * Returns boolean indicating whether or not the request should attempt to use native broker
13536
+ * @param logger
13537
+ * @param config
13538
+ * @param nativeExtensionProvider
13539
+ * @param authenticationScheme
13650
13540
  */
13651
- async handleNativeResponse(response, request, reqTimestamp) {
13652
- this.logger.trace("NativeInteractionClient - handleNativeResponse called.");
13653
- // generate identifiers
13654
- const idTokenClaims = extractTokenClaims(response.id_token, base64Decode);
13655
- const homeAccountIdentifier = this.createHomeAccountIdentifier(response, idTokenClaims);
13656
- const cachedhomeAccountId = this.browserStorage.getAccountInfoFilteredBy({
13657
- nativeAccountId: request.accountId,
13658
- })?.homeAccountId;
13659
- // add exception for double brokering, please note this is temporary and will be fortified in future
13660
- if (request.extraParameters?.child_client_id &&
13661
- response.account.id !== request.accountId) {
13662
- this.logger.info("handleNativeServerResponse: Double broker flow detected, ignoring accountId mismatch");
13541
+ static isPlatformBrokerAvailable(config, logger, nativeExtensionProvider, authenticationScheme) {
13542
+ logger.trace("isPlatformBrokerAvailable called");
13543
+ if (!config.system.allowPlatformBroker) {
13544
+ logger.trace("isPlatformBrokerAvailable: allowPlatformBroker is not enabled, returning false");
13545
+ // Developer disabled WAM
13546
+ return false;
13663
13547
  }
13664
- else if (homeAccountIdentifier !== cachedhomeAccountId &&
13665
- response.account.id !== request.accountId) {
13666
- // User switch in native broker prompt is not supported. All users must first sign in through web flow to ensure server state is in sync
13667
- throw createNativeAuthError(userSwitch);
13548
+ if (!nativeExtensionProvider) {
13549
+ logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
13550
+ // Extension is not available
13551
+ return false;
13668
13552
  }
13669
- // Get the preferred_cache domain for the given authority
13670
- const authority = await this.getDiscoveredAuthority({
13671
- requestAuthority: request.authority,
13672
- });
13673
- const baseAccount = buildAccountToCache(this.browserStorage, authority, homeAccountIdentifier, base64Decode, idTokenClaims, response.client_info, undefined, // environment
13674
- idTokenClaims.tid, undefined, // auth code payload
13675
- response.account.id, this.logger);
13676
- // Ensure expires_in is in number format
13677
- response.expires_in = Number(response.expires_in);
13678
- // generate authenticationResult
13679
- const result = await this.generateAuthenticationResult(response, request, idTokenClaims, baseAccount, authority.canonicalAuthority, reqTimestamp);
13680
- // cache accounts and tokens in the appropriate storage
13681
- await this.cacheAccount(baseAccount);
13682
- await this.cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, response.access_token, result.tenantId, reqTimestamp);
13683
- return result;
13553
+ if (authenticationScheme) {
13554
+ switch (authenticationScheme) {
13555
+ case AuthenticationScheme.BEARER:
13556
+ case AuthenticationScheme.POP:
13557
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is supported, returning true");
13558
+ return true;
13559
+ default:
13560
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is not supported, returning false");
13561
+ return false;
13562
+ }
13563
+ }
13564
+ return true;
13684
13565
  }
13685
- /**
13686
- * creates an homeAccountIdentifier for the account
13687
- * @param response
13688
- * @param idTokenObj
13689
- * @returns
13690
- */
13691
- createHomeAccountIdentifier(response, idTokenClaims) {
13692
- // Save account in browser storage
13693
- const homeAccountIdentifier = AccountEntity.generateHomeAccountId(response.client_info || Constants.EMPTY_STRING, AuthorityType.Default, this.logger, this.browserCrypto, idTokenClaims);
13694
- return homeAccountIdentifier;
13566
+ }
13567
+
13568
+ /*
13569
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13570
+ * Licensed under the MIT License.
13571
+ */
13572
+ /**
13573
+ * Extracts the BrowserStateObject from the state string.
13574
+ * @param browserCrypto
13575
+ * @param state
13576
+ */
13577
+ function extractBrowserRequestState(browserCrypto, state) {
13578
+ if (!state) {
13579
+ return null;
13695
13580
  }
13696
- /**
13697
- * Helper to generate scopes
13698
- * @param response
13699
- * @param request
13700
- * @returns
13701
- */
13702
- generateScopes(response, request) {
13703
- return response.scope
13704
- ? ScopeSet.fromString(response.scope)
13705
- : ScopeSet.fromString(request.scope);
13581
+ try {
13582
+ const requestStateObj = ProtocolUtils.parseRequestState(browserCrypto, state);
13583
+ return requestStateObj.libraryState.meta;
13584
+ }
13585
+ catch (e) {
13586
+ throw createClientAuthError(invalidState);
13587
+ }
13588
+ }
13589
+
13590
+ /*
13591
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13592
+ * Licensed under the MIT License.
13593
+ */
13594
+ function deserializeResponse(responseString, responseLocation, logger) {
13595
+ // Deserialize hash fragment response parameters.
13596
+ const serverParams = getDeserializedResponse(responseString);
13597
+ if (!serverParams) {
13598
+ if (!stripLeadingHashOrQuery(responseString)) {
13599
+ // Hash or Query string is empty
13600
+ logger.error(`The request has returned to the redirectUri but a ${responseLocation} is not present. It's likely that the ${responseLocation} has been removed or the page has been redirected by code running on the redirectUri page.`);
13601
+ throw createBrowserAuthError(hashEmptyError);
13602
+ }
13603
+ else {
13604
+ logger.error(`A ${responseLocation} is present in the iframe but it does not contain known properties. It's likely that the ${responseLocation} has been replaced by code running on the redirectUri page.`);
13605
+ logger.errorPii(`The ${responseLocation} detected is: ${responseString}`);
13606
+ throw createBrowserAuthError(hashDoesNotContainKnownProperties);
13607
+ }
13608
+ }
13609
+ return serverParams;
13610
+ }
13611
+ /**
13612
+ * Returns the interaction type that the response object belongs to
13613
+ */
13614
+ function validateInteractionType(response, browserCrypto, interactionType) {
13615
+ if (!response.state) {
13616
+ throw createBrowserAuthError(noStateInHash);
13617
+ }
13618
+ const platformStateObj = extractBrowserRequestState(browserCrypto, response.state);
13619
+ if (!platformStateObj) {
13620
+ throw createBrowserAuthError(unableToParseState);
13621
+ }
13622
+ if (platformStateObj.interactionType !== interactionType) {
13623
+ throw createBrowserAuthError(stateInteractionTypeMismatch);
13624
+ }
13625
+ }
13626
+
13627
+ /*
13628
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13629
+ * Licensed under the MIT License.
13630
+ */
13631
+ /**
13632
+ * Abstract class which defines operations for a browser interaction handling class.
13633
+ */
13634
+ class InteractionHandler {
13635
+ constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
13636
+ this.authModule = authCodeModule;
13637
+ this.browserStorage = storageImpl;
13638
+ this.authCodeRequest = authCodeRequest;
13639
+ this.logger = logger;
13640
+ this.performanceClient = performanceClient;
13706
13641
  }
13707
13642
  /**
13708
- * If PoP token is requesred, records the PoP token if returned from the WAM, else generates one in the browser
13709
- * @param request
13710
- * @param response
13643
+ * Function to handle response parameters from hash.
13644
+ * @param locationHash
13711
13645
  */
13712
- async generatePopAccessToken(response, request) {
13713
- if (request.tokenType === AuthenticationScheme.POP &&
13714
- request.signPopToken) {
13715
- /**
13716
- * This code prioritizes SHR returned from the native layer. In case of error/SHR not calculated from WAM and the AT
13717
- * is still received, SHR is calculated locally
13718
- */
13719
- // Check if native layer returned an SHR token
13720
- if (response.shr) {
13721
- this.logger.trace("handleNativeServerResponse: SHR is enabled in native layer");
13722
- return response.shr;
13646
+ async handleCodeResponse(response, request) {
13647
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponse, request.correlationId);
13648
+ let authCodeResponse;
13649
+ try {
13650
+ authCodeResponse = getAuthorizationCodePayload(response, request.state);
13651
+ }
13652
+ catch (e) {
13653
+ if (e instanceof ServerError &&
13654
+ e.subError === userCancelled) {
13655
+ // Translate server error caused by user closing native prompt to corresponding first class MSAL error
13656
+ throw createBrowserAuthError(userCancelled);
13723
13657
  }
13724
- // Generate SHR in msal js if WAM does not compute it when POP is enabled
13725
- const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
13726
- const shrParameters = {
13727
- resourceRequestMethod: request.resourceRequestMethod,
13728
- resourceRequestUri: request.resourceRequestUri,
13729
- shrClaims: request.shrClaims,
13730
- shrNonce: request.shrNonce,
13731
- };
13732
- /**
13733
- * KeyID must be present in the native request from when the PoP key was generated in order for
13734
- * PopTokenGenerator to query the full key for signing
13735
- */
13736
- if (!request.keyId) {
13737
- throw createClientAuthError(keyIdMissing);
13658
+ else {
13659
+ throw e;
13738
13660
  }
13739
- return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
13661
+ }
13662
+ return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
13663
+ }
13664
+ /**
13665
+ * Process auth code response from AAD
13666
+ * @param authCodeResponse
13667
+ * @param state
13668
+ * @param authority
13669
+ * @param networkModule
13670
+ * @returns
13671
+ */
13672
+ async handleCodeResponseFromServer(authCodeResponse, request, validateNonce = true) {
13673
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponseFromServer, request.correlationId);
13674
+ this.logger.trace("InteractionHandler.handleCodeResponseFromServer called");
13675
+ // Assign code to request
13676
+ this.authCodeRequest.code = authCodeResponse.code;
13677
+ // Check for new cloud instance
13678
+ if (authCodeResponse.cloud_instance_host_name) {
13679
+ await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, request.correlationId)(authCodeResponse.cloud_instance_host_name, request.correlationId);
13680
+ }
13681
+ // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
13682
+ if (validateNonce) {
13683
+ // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
13684
+ authCodeResponse.nonce = request.nonce || undefined;
13685
+ }
13686
+ authCodeResponse.state = request.state;
13687
+ // Add CCS parameters if available
13688
+ if (authCodeResponse.client_info) {
13689
+ this.authCodeRequest.clientInfo = authCodeResponse.client_info;
13740
13690
  }
13741
13691
  else {
13742
- return response.access_token;
13692
+ const ccsCred = this.createCcsCredentials(request);
13693
+ if (ccsCred) {
13694
+ this.authCodeRequest.ccsCredential = ccsCred;
13695
+ }
13743
13696
  }
13697
+ // Acquire token with retrieved code.
13698
+ const tokenResponse = (await invokeAsync(this.authModule.acquireToken.bind(this.authModule), PerformanceEvents.AuthClientAcquireToken, this.logger, this.performanceClient, request.correlationId)(this.authCodeRequest, authCodeResponse));
13699
+ return tokenResponse;
13744
13700
  }
13745
13701
  /**
13746
- * Generates authentication result
13747
- * @param response
13748
- * @param request
13749
- * @param idTokenObj
13750
- * @param accountEntity
13751
- * @param authority
13752
- * @param reqTimestamp
13753
- * @returns
13702
+ * Build ccs creds if available
13754
13703
  */
13755
- async generateAuthenticationResult(response, request, idTokenClaims, accountEntity, authority, reqTimestamp) {
13756
- // Add Native Broker fields to Telemetry
13757
- const mats = this.addTelemetryFromNativeResponse(response);
13758
- // If scopes not returned in server response, use request scopes
13759
- const responseScopes = response.scope
13760
- ? ScopeSet.fromString(response.scope)
13761
- : ScopeSet.fromString(request.scope);
13762
- const accountProperties = response.account.properties || {};
13763
- const uid = accountProperties["UID"] ||
13764
- idTokenClaims.oid ||
13765
- idTokenClaims.sub ||
13766
- Constants.EMPTY_STRING;
13767
- const tid = accountProperties["TenantId"] ||
13768
- idTokenClaims.tid ||
13769
- Constants.EMPTY_STRING;
13770
- const accountInfo = updateAccountTenantProfileData(accountEntity.getAccountInfo(), undefined, // tenantProfile optional
13771
- idTokenClaims, response.id_token);
13772
- /**
13773
- * In pairwise broker flows, this check prevents the broker's native account id
13774
- * from being returned over the embedded app's account id.
13775
- */
13776
- if (accountInfo.nativeAccountId !== response.account.id) {
13777
- accountInfo.nativeAccountId = response.account.id;
13704
+ createCcsCredentials(request) {
13705
+ if (request.account) {
13706
+ return {
13707
+ credential: request.account.homeAccountId,
13708
+ type: CcsCredentialType.HOME_ACCOUNT_ID,
13709
+ };
13778
13710
  }
13779
- // generate PoP token as needed
13780
- const responseAccessToken = await this.generatePopAccessToken(response, request);
13781
- const tokenType = request.tokenType === AuthenticationScheme.POP
13782
- ? AuthenticationScheme.POP
13783
- : AuthenticationScheme.BEARER;
13784
- const result = {
13785
- authority: authority,
13786
- uniqueId: uid,
13787
- tenantId: tid,
13788
- scopes: responseScopes.asArray(),
13789
- account: accountInfo,
13790
- idToken: response.id_token,
13791
- idTokenClaims: idTokenClaims,
13792
- accessToken: responseAccessToken,
13793
- fromCache: mats ? this.isResponseFromCache(mats) : false,
13794
- // Request timestamp and NativeResponse expires_in are in seconds, converting to Date for AuthenticationResult
13795
- expiresOn: toDateFromSeconds(reqTimestamp + response.expires_in),
13796
- tokenType: tokenType,
13797
- correlationId: this.correlationId,
13798
- state: response.state,
13799
- fromNativeBroker: true,
13800
- };
13801
- return result;
13711
+ else if (request.loginHint) {
13712
+ return {
13713
+ credential: request.loginHint,
13714
+ type: CcsCredentialType.UPN,
13715
+ };
13716
+ }
13717
+ return null;
13802
13718
  }
13719
+ }
13720
+
13721
+ /*
13722
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13723
+ * Licensed under the MIT License.
13724
+ */
13725
+ class SilentCacheClient extends StandardInteractionClient {
13803
13726
  /**
13804
- * cache the account entity in browser storage
13805
- * @param accountEntity
13727
+ * Returns unexpired tokens from the cache, if available
13728
+ * @param silentRequest
13806
13729
  */
13807
- async cacheAccount(accountEntity) {
13808
- // Store the account info and hence `nativeAccountId` in browser cache
13809
- await this.browserStorage.setAccount(accountEntity, this.correlationId);
13810
- // Remove any existing cached tokens for this account in browser storage
13811
- this.browserStorage.removeAccountContext(accountEntity).catch((e) => {
13812
- this.logger.error(`Error occurred while removing account context from browser storage. ${e}`);
13730
+ async acquireToken(silentRequest) {
13731
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentCacheClientAcquireToken, silentRequest.correlationId);
13732
+ // Telemetry manager only used to increment cacheHits here
13733
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent_silentFlow);
13734
+ const clientConfig = await invokeAsync(this.getClientConfiguration.bind(this), PerformanceEvents.StandardInteractionClientGetClientConfiguration, this.logger, this.performanceClient, this.correlationId)({
13735
+ serverTelemetryManager,
13736
+ requestAuthority: silentRequest.authority,
13737
+ requestAzureCloudOptions: silentRequest.azureCloudOptions,
13738
+ account: silentRequest.account,
13813
13739
  });
13740
+ const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13741
+ this.logger.verbose("Silent auth client created");
13742
+ try {
13743
+ const response = await invokeAsync(silentAuthClient.acquireCachedToken.bind(silentAuthClient), PerformanceEvents.SilentFlowClientAcquireCachedToken, this.logger, this.performanceClient, silentRequest.correlationId)(silentRequest);
13744
+ const authResponse = response[0];
13745
+ this.performanceClient.addFields({
13746
+ fromCache: true,
13747
+ }, silentRequest.correlationId);
13748
+ return authResponse;
13749
+ }
13750
+ catch (error) {
13751
+ if (error instanceof BrowserAuthError &&
13752
+ error.errorCode === cryptoKeyNotFound) {
13753
+ this.logger.verbose("Signing keypair for bound access token not found. Refreshing bound access token and generating a new crypto keypair.");
13754
+ }
13755
+ throw error;
13756
+ }
13814
13757
  }
13815
13758
  /**
13816
- * Stores the access_token and id_token in inmemory storage
13817
- * @param response
13818
- * @param request
13819
- * @param homeAccountIdentifier
13820
- * @param idTokenObj
13821
- * @param responseAccessToken
13822
- * @param tenantId
13823
- * @param reqTimestamp
13759
+ * API to silenty clear the browser cache.
13760
+ * @param logoutRequest
13824
13761
  */
13825
- cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, responseAccessToken, tenantId, reqTimestamp) {
13826
- const cachedIdToken = createIdTokenEntity(homeAccountIdentifier, request.authority, response.id_token || "", request.clientId, idTokenClaims.tid || "");
13827
- // cache accessToken in inmemory storage
13828
- const expiresIn = request.tokenType === AuthenticationScheme.POP
13829
- ? Constants.SHR_NONCE_VALIDITY
13830
- : (typeof response.expires_in === "string"
13831
- ? parseInt(response.expires_in, 10)
13832
- : response.expires_in) || 0;
13833
- const tokenExpirationSeconds = reqTimestamp + expiresIn;
13834
- const responseScopes = this.generateScopes(response, request);
13835
- const cachedAccessToken = createAccessTokenEntity(homeAccountIdentifier, request.authority, responseAccessToken, request.clientId, idTokenClaims.tid || tenantId, responseScopes.printScopes(), tokenExpirationSeconds, 0, base64Decode, undefined, request.tokenType, undefined, request.keyId);
13836
- const nativeCacheRecord = {
13837
- idToken: cachedIdToken,
13838
- accessToken: cachedAccessToken,
13839
- };
13840
- return this.nativeStorageManager.saveCacheRecord(nativeCacheRecord, this.correlationId, request.storeInCache);
13762
+ logout(logoutRequest) {
13763
+ this.logger.verbose("logoutRedirect called");
13764
+ const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
13765
+ return this.clearCacheOnLogout(validLogoutRequest?.account);
13841
13766
  }
13842
- addTelemetryFromNativeResponse(response) {
13843
- const mats = this.getMATSFromResponse(response);
13844
- if (!mats) {
13845
- return null;
13846
- }
13847
- this.performanceClient.addFields({
13848
- extensionId: this.nativeMessageHandler.getExtensionId(),
13767
+ }
13768
+
13769
+ /*
13770
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13771
+ * Licensed under the MIT License.
13772
+ */
13773
+ class NativeInteractionClient extends BaseInteractionClient {
13774
+ constructor(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, provider, accountId, nativeStorageImpl, correlationId) {
13775
+ super(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13776
+ this.apiId = apiId;
13777
+ this.accountId = accountId;
13778
+ this.nativeMessageHandler = provider;
13779
+ this.nativeStorageManager = nativeStorageImpl;
13780
+ this.silentCacheClient = new SilentCacheClient(config, this.nativeStorageManager, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13781
+ const extensionName = this.nativeMessageHandler.getExtensionId() ===
13782
+ NativeConstants.PREFERRED_EXTENSION_ID
13783
+ ? "chrome"
13784
+ : this.nativeMessageHandler.getExtensionId()?.length
13785
+ ? "unknown"
13786
+ : undefined;
13787
+ this.skus = ServerTelemetryManager.makeExtraSkuString({
13788
+ libraryName: BrowserConstants.MSAL_SKU,
13789
+ libraryVersion: version,
13790
+ extensionName: extensionName,
13849
13791
  extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
13850
- matsBrokerVersion: mats.broker_version,
13851
- matsAccountJoinOnStart: mats.account_join_on_start,
13852
- matsAccountJoinOnEnd: mats.account_join_on_end,
13853
- matsDeviceJoin: mats.device_join,
13854
- matsPromptBehavior: mats.prompt_behavior,
13855
- matsApiErrorCode: mats.api_error_code,
13856
- matsUiVisible: mats.ui_visible,
13857
- matsSilentCode: mats.silent_code,
13858
- matsSilentBiSubCode: mats.silent_bi_sub_code,
13859
- matsSilentMessage: mats.silent_message,
13860
- matsSilentStatus: mats.silent_status,
13861
- matsHttpStatus: mats.http_status,
13862
- matsHttpEventCount: mats.http_event_count,
13863
- }, this.correlationId);
13864
- return mats;
13792
+ });
13865
13793
  }
13866
13794
  /**
13867
- * Validates native platform response before processing
13868
- * @param response
13795
+ * Adds SKUs to request extra query parameters
13796
+ * @param request {NativeTokenRequest}
13797
+ * @private
13869
13798
  */
13870
- validateNativeResponse(response) {
13871
- if (response.hasOwnProperty("access_token") &&
13872
- response.hasOwnProperty("id_token") &&
13873
- response.hasOwnProperty("client_info") &&
13874
- response.hasOwnProperty("account") &&
13875
- response.hasOwnProperty("scope") &&
13876
- response.hasOwnProperty("expires_in")) {
13877
- return response;
13878
- }
13879
- else {
13880
- throw createAuthError(unexpectedError, "Response missing expected properties.");
13881
- }
13799
+ addRequestSKUs(request) {
13800
+ request.extraParameters = {
13801
+ ...request.extraParameters,
13802
+ [X_CLIENT_EXTRA_SKU]: this.skus,
13803
+ };
13882
13804
  }
13883
13805
  /**
13884
- * Gets MATS telemetry from native response
13885
- * @param response
13886
- * @returns
13806
+ * Acquire token from native platform via browser extension
13807
+ * @param request
13887
13808
  */
13888
- getMATSFromResponse(response) {
13889
- if (response.properties.MATS) {
13809
+ async acquireToken(request, cacheLookupPolicy) {
13810
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13811
+ this.logger.trace("NativeInteractionClient - acquireToken called.");
13812
+ // start the perf measurement
13813
+ const nativeATMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13814
+ const reqTimestamp = nowSeconds();
13815
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13816
+ try {
13817
+ // initialize native request
13818
+ const nativeRequest = await this.initializeNativeRequest(request);
13819
+ // check if the tokens can be retrieved from internal cache
13890
13820
  try {
13891
- return JSON.parse(response.properties.MATS);
13821
+ const result = await this.acquireTokensFromCache(this.accountId, nativeRequest);
13822
+ nativeATMeasurement.end({
13823
+ success: true,
13824
+ isNativeBroker: false,
13825
+ fromCache: true,
13826
+ });
13827
+ return result;
13892
13828
  }
13893
13829
  catch (e) {
13894
- this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
13830
+ if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
13831
+ this.logger.info("MSAL internal Cache does not contain tokens, return error as per cache policy");
13832
+ throw e;
13833
+ }
13834
+ // continue with a native call for any and all errors
13835
+ this.logger.info("MSAL internal Cache does not contain tokens, proceed to make a native call");
13895
13836
  }
13837
+ const { ...nativeTokenRequest } = nativeRequest;
13838
+ // fall back to native calls
13839
+ const messageBody = {
13840
+ method: NativeExtensionMethod.GetToken,
13841
+ request: nativeTokenRequest,
13842
+ };
13843
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13844
+ const validatedResponse = this.validateNativeResponse(response);
13845
+ return await this.handleNativeResponse(validatedResponse, nativeRequest, reqTimestamp)
13846
+ .then((result) => {
13847
+ nativeATMeasurement.end({
13848
+ success: true,
13849
+ isNativeBroker: true,
13850
+ requestId: result.requestId,
13851
+ });
13852
+ serverTelemetryManager.clearNativeBrokerErrorCode();
13853
+ return result;
13854
+ })
13855
+ .catch((error) => {
13856
+ nativeATMeasurement.end({
13857
+ success: false,
13858
+ errorCode: error.errorCode,
13859
+ subErrorCode: error.subError,
13860
+ isNativeBroker: true,
13861
+ });
13862
+ throw error;
13863
+ });
13896
13864
  }
13897
- return null;
13898
- }
13899
- /**
13900
- * Returns whether or not response came from native cache
13901
- * @param response
13902
- * @returns
13903
- */
13904
- isResponseFromCache(mats) {
13905
- if (typeof mats.is_cached === "undefined") {
13906
- this.logger.verbose("NativeInteractionClient - MATS telemetry does not contain field indicating if response was served from cache. Returning false.");
13907
- return false;
13865
+ catch (e) {
13866
+ if (e instanceof NativeAuthError) {
13867
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13868
+ }
13869
+ throw e;
13908
13870
  }
13909
- return !!mats.is_cached;
13910
13871
  }
13911
13872
  /**
13912
- * Translates developer provided request object into NativeRequest object
13873
+ * Creates silent flow request
13913
13874
  * @param request
13875
+ * @param cachedAccount
13876
+ * @returns CommonSilentFlowRequest
13914
13877
  */
13915
- async initializeNativeRequest(request) {
13916
- this.logger.trace("NativeInteractionClient - initializeNativeRequest called");
13917
- const requestAuthority = request.authority || this.config.auth.authority;
13918
- if (request.account) {
13919
- // validate authority
13920
- await this.getDiscoveredAuthority({
13921
- requestAuthority,
13922
- requestAzureCloudOptions: request.azureCloudOptions,
13923
- account: request.account,
13924
- });
13925
- }
13926
- const canonicalAuthority = new UrlString(requestAuthority);
13927
- canonicalAuthority.validateAsUri();
13928
- // scopes are expected to be received by the native broker as "scope" and will be added to the request below. Other properties that should be dropped from the request to the native broker can be included in the object destructuring here.
13929
- const { scopes, ...remainingProperties } = request;
13930
- const scopeSet = new ScopeSet(scopes || []);
13931
- scopeSet.appendScopes(OIDC_DEFAULT_SCOPES);
13932
- const getPrompt = () => {
13933
- // If request is silent, prompt is always none
13934
- switch (this.apiId) {
13935
- case ApiId.ssoSilent:
13936
- case ApiId.acquireTokenSilent_silentFlow:
13937
- this.logger.trace("initializeNativeRequest: silent request sets prompt to none");
13938
- return PromptValue.NONE;
13939
- }
13940
- // Prompt not provided, request may proceed and native broker decides if it needs to prompt
13941
- if (!request.prompt) {
13942
- this.logger.trace("initializeNativeRequest: prompt was not provided");
13943
- return undefined;
13944
- }
13945
- // If request is interactive, check if prompt provided is allowed to go directly to native broker
13946
- switch (request.prompt) {
13947
- case PromptValue.NONE:
13948
- case PromptValue.CONSENT:
13949
- case PromptValue.LOGIN:
13950
- this.logger.trace("initializeNativeRequest: prompt is compatible with native flow");
13951
- return request.prompt;
13952
- default:
13953
- this.logger.trace(`initializeNativeRequest: prompt = ${request.prompt} is not compatible with native flow`);
13954
- throw createBrowserAuthError(nativePromptNotSupported);
13955
- }
13956
- };
13957
- const validatedRequest = {
13958
- ...remainingProperties,
13959
- accountId: this.accountId,
13960
- clientId: this.config.auth.clientId,
13961
- authority: canonicalAuthority.urlString,
13962
- scope: scopeSet.printScopes(),
13963
- redirectUri: this.getRedirectUri(request.redirectUri),
13964
- prompt: getPrompt(),
13878
+ createSilentCacheRequest(request, cachedAccount) {
13879
+ return {
13880
+ authority: request.authority,
13965
13881
  correlationId: this.correlationId,
13966
- tokenType: request.authenticationScheme,
13967
- windowTitleSubstring: document.title,
13968
- extraParameters: {
13969
- ...request.extraQueryParameters,
13970
- ...request.tokenQueryParameters,
13971
- },
13972
- extendedExpiryToken: false,
13973
- keyId: request.popKid,
13882
+ scopes: ScopeSet.fromString(request.scope).asArray(),
13883
+ account: cachedAccount,
13884
+ forceRefresh: false,
13974
13885
  };
13975
- // Check for PoP token requests: signPopToken should only be set to true if popKid is not set
13976
- if (validatedRequest.signPopToken && !!request.popKid) {
13977
- throw createBrowserAuthError(invalidPopTokenRequest);
13978
- }
13979
- this.handleExtraBrokerParams(validatedRequest);
13980
- validatedRequest.extraParameters =
13981
- validatedRequest.extraParameters || {};
13982
- validatedRequest.extraParameters.telemetry =
13983
- NativeConstants.MATS_TELEMETRY;
13984
- if (request.authenticationScheme === AuthenticationScheme.POP) {
13985
- // add POP request type
13986
- const shrParameters = {
13987
- resourceRequestUri: request.resourceRequestUri,
13988
- resourceRequestMethod: request.resourceRequestMethod,
13989
- shrClaims: request.shrClaims,
13990
- shrNonce: request.shrNonce,
13991
- };
13992
- const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
13993
- // generate reqCnf if not provided in the request
13994
- let reqCnfData;
13995
- if (!validatedRequest.keyId) {
13996
- const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, this.logger, this.performanceClient, request.correlationId)(shrParameters, this.logger);
13997
- reqCnfData = generatedReqCnfData.reqCnfString;
13998
- validatedRequest.keyId = generatedReqCnfData.kid;
13999
- validatedRequest.signPopToken = true;
14000
- }
14001
- else {
14002
- reqCnfData = this.browserCrypto.base64UrlEncode(JSON.stringify({ kid: validatedRequest.keyId }));
14003
- validatedRequest.signPopToken = false;
14004
- }
14005
- // SPAs require whole string to be passed to broker
14006
- validatedRequest.reqCnf = reqCnfData;
14007
- }
14008
- this.addRequestSKUs(validatedRequest);
14009
- return validatedRequest;
14010
13886
  }
14011
13887
  /**
14012
- * Handles extra broker request parameters
14013
- * @param request {NativeTokenRequest}
14014
- * @private
13888
+ * Fetches the tokens from the cache if un-expired
13889
+ * @param nativeAccountId
13890
+ * @param request
13891
+ * @returns authenticationResult
14015
13892
  */
14016
- handleExtraBrokerParams(request) {
14017
- const hasExtraBrokerParams = request.extraParameters &&
14018
- request.extraParameters.hasOwnProperty(BROKER_CLIENT_ID) &&
14019
- request.extraParameters.hasOwnProperty(BROKER_REDIRECT_URI) &&
14020
- request.extraParameters.hasOwnProperty(CLIENT_ID);
14021
- if (!request.embeddedClientId && !hasExtraBrokerParams) {
14022
- return;
13893
+ async acquireTokensFromCache(nativeAccountId, request) {
13894
+ if (!nativeAccountId) {
13895
+ this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13896
+ throw createClientAuthError(noAccountFound);
14023
13897
  }
14024
- let child_client_id = "";
14025
- const child_redirect_uri = request.redirectUri;
14026
- if (request.embeddedClientId) {
14027
- request.redirectUri = this.config.auth.redirectUri;
14028
- child_client_id = request.embeddedClientId;
13898
+ // fetch the account from browser cache
13899
+ const account = this.browserStorage.getBaseAccountInfo({
13900
+ nativeAccountId,
13901
+ });
13902
+ if (!account) {
13903
+ throw createClientAuthError(noAccountFound);
14029
13904
  }
14030
- else if (request.extraParameters) {
14031
- request.redirectUri =
14032
- request.extraParameters[BROKER_REDIRECT_URI];
14033
- child_client_id =
14034
- request.extraParameters[CLIENT_ID];
13905
+ // leverage silent flow for cached tokens retrieval
13906
+ try {
13907
+ const silentRequest = this.createSilentCacheRequest(request, account);
13908
+ const result = await this.silentCacheClient.acquireToken(silentRequest);
13909
+ const fullAccount = {
13910
+ ...account,
13911
+ idTokenClaims: result?.idTokenClaims,
13912
+ idToken: result?.idToken,
13913
+ };
13914
+ return {
13915
+ ...result,
13916
+ account: fullAccount,
13917
+ };
13918
+ }
13919
+ catch (e) {
13920
+ throw e;
14035
13921
  }
14036
- request.extraParameters = {
14037
- child_client_id,
14038
- child_redirect_uri,
14039
- };
14040
- this.performanceClient?.addFields({
14041
- embeddedClientId: child_client_id,
14042
- embeddedRedirectUri: child_redirect_uri,
14043
- }, request.correlationId);
14044
- }
14045
- }
14046
-
14047
- /*
14048
- * Copyright (c) Microsoft Corporation. All rights reserved.
14049
- * Licensed under the MIT License.
14050
- */
14051
- class NativeMessageHandler {
14052
- constructor(logger, handshakeTimeoutMs, performanceClient, extensionId) {
14053
- this.logger = logger;
14054
- this.handshakeTimeoutMs = handshakeTimeoutMs;
14055
- this.extensionId = extensionId;
14056
- this.resolvers = new Map(); // Used for non-handshake messages
14057
- this.handshakeResolvers = new Map(); // Used for handshake messages
14058
- this.messageChannel = new MessageChannel();
14059
- this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
14060
- this.performanceClient = performanceClient;
14061
- this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
14062
13922
  }
14063
13923
  /**
14064
- * Sends a given message to the extension and resolves with the extension response
14065
- * @param body
13924
+ * Acquires a token from native platform then redirects to the redirectUri instead of returning the response
13925
+ * @param {RedirectRequest} request
13926
+ * @param {InProgressPerformanceEvent} rootMeasurement
14066
13927
  */
14067
- async sendMessage(body) {
14068
- this.logger.trace("NativeMessageHandler - sendMessage called.");
14069
- const req = {
14070
- channel: NativeConstants.CHANNEL_ID,
14071
- extensionId: this.extensionId,
14072
- responseId: createNewGuid(),
14073
- body: body,
13928
+ async acquireTokenRedirect(request, rootMeasurement) {
13929
+ this.logger.trace("NativeInteractionClient - acquireTokenRedirect called.");
13930
+ const { ...remainingParameters } = request;
13931
+ delete remainingParameters.onRedirectNavigate;
13932
+ const nativeRequest = await this.initializeNativeRequest(remainingParameters);
13933
+ const messageBody = {
13934
+ method: NativeExtensionMethod.GetToken,
13935
+ request: nativeRequest,
14074
13936
  };
14075
- this.logger.trace("NativeMessageHandler - Sending request to browser extension");
14076
- this.logger.tracePii(`NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(req)}`);
14077
- this.messageChannel.port1.postMessage(req);
14078
- return new Promise((resolve, reject) => {
14079
- this.resolvers.set(req.responseId, { resolve, reject });
14080
- });
13937
+ try {
13938
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13939
+ this.validateNativeResponse(response);
13940
+ }
13941
+ catch (e) {
13942
+ // Only throw fatal errors here to allow application to fallback to regular redirect. Otherwise proceed and the error will be thrown in handleRedirectPromise
13943
+ if (e instanceof NativeAuthError) {
13944
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13945
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13946
+ if (isFatalNativeAuthError(e)) {
13947
+ throw e;
13948
+ }
13949
+ }
13950
+ }
13951
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, JSON.stringify(nativeRequest), true);
13952
+ const navigationOptions = {
13953
+ apiId: ApiId.acquireTokenRedirect,
13954
+ timeout: this.config.system.redirectNavigationTimeout,
13955
+ noHistory: false,
13956
+ };
13957
+ const redirectUri = this.config.auth.navigateToLoginRequestUrl
13958
+ ? window.location.href
13959
+ : this.getRedirectUri(request.redirectUri);
13960
+ rootMeasurement.end({ success: true });
13961
+ await this.navigationClient.navigateExternal(redirectUri, navigationOptions); // Need to treat this as external to ensure handleRedirectPromise is run again
14081
13962
  }
14082
13963
  /**
14083
- * Returns an instance of the MessageHandler that has successfully established a connection with an extension
14084
- * @param {Logger} logger
14085
- * @param {number} handshakeTimeoutMs
14086
- * @param {IPerformanceClient} performanceClient
14087
- * @param {ICrypto} crypto
13964
+ * If the previous page called native platform for a token using redirect APIs, send the same request again and return the response
13965
+ * @param performanceClient {IPerformanceClient?}
13966
+ * @param correlationId {string?} correlation identifier
14088
13967
  */
14089
- static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
14090
- logger.trace("NativeMessageHandler - createProvider called.");
13968
+ async handleRedirectPromise(performanceClient, correlationId) {
13969
+ this.logger.trace("NativeInteractionClient - handleRedirectPromise called.");
13970
+ if (!this.browserStorage.isInteractionInProgress(true)) {
13971
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
13972
+ return null;
13973
+ }
13974
+ // remove prompt from the request to prevent WAM from prompting twice
13975
+ const cachedRequest = this.browserStorage.getCachedNativeRequest();
13976
+ if (!cachedRequest) {
13977
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise called but there is no cached request, returning null.");
13978
+ if (performanceClient && correlationId) {
13979
+ performanceClient?.addFields({ errorCode: "no_cached_request" }, correlationId);
13980
+ }
13981
+ return null;
13982
+ }
13983
+ const { prompt, ...request } = cachedRequest;
13984
+ if (prompt) {
13985
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise called and prompt was included in the original request, removing prompt from cached request to prevent second interaction with native broker window.");
13986
+ }
13987
+ this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
13988
+ const messageBody = {
13989
+ method: NativeExtensionMethod.GetToken,
13990
+ request: request,
13991
+ };
13992
+ const reqTimestamp = nowSeconds();
14091
13993
  try {
14092
- const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
14093
- await preferredProvider.sendHandshakeRequest();
14094
- return preferredProvider;
13994
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise sending message to native broker.");
13995
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13996
+ this.validateNativeResponse(response);
13997
+ const result = this.handleNativeResponse(response, request, reqTimestamp);
13998
+ const res = await result;
13999
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
14000
+ serverTelemetryManager.clearNativeBrokerErrorCode();
14001
+ return res;
14095
14002
  }
14096
14003
  catch (e) {
14097
- // If preferred extension fails for whatever reason, fallback to using any installed extension
14098
- const backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
14099
- await backupProvider.sendHandshakeRequest();
14100
- return backupProvider;
14004
+ throw e;
14101
14005
  }
14102
14006
  }
14103
14007
  /**
14104
- * Send handshake request helper.
14008
+ * Logout from native platform via browser extension
14009
+ * @param request
14105
14010
  */
14106
- async sendHandshakeRequest() {
14107
- this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
14108
- // Register this event listener before sending handshake
14109
- window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
14110
- const req = {
14111
- channel: NativeConstants.CHANNEL_ID,
14112
- extensionId: this.extensionId,
14113
- responseId: createNewGuid(),
14114
- body: {
14115
- method: NativeExtensionMethod.HandshakeRequest,
14116
- },
14117
- };
14118
- this.handshakeEvent.add({
14119
- extensionId: this.extensionId,
14120
- extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
14121
- });
14122
- this.messageChannel.port1.onmessage = (event) => {
14123
- this.onChannelMessage(event);
14124
- };
14125
- window.postMessage(req, window.origin, [this.messageChannel.port2]);
14126
- return new Promise((resolve, reject) => {
14127
- this.handshakeResolvers.set(req.responseId, { resolve, reject });
14128
- this.timeoutId = window.setTimeout(() => {
14129
- /*
14130
- * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
14131
- * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
14132
- */
14133
- window.removeEventListener("message", this.windowListener, false);
14134
- this.messageChannel.port1.close();
14135
- this.messageChannel.port2.close();
14136
- this.handshakeEvent.end({
14137
- extensionHandshakeTimedOut: true,
14138
- success: false,
14139
- });
14140
- reject(createBrowserAuthError(nativeHandshakeTimeout));
14141
- this.handshakeResolvers.delete(req.responseId);
14142
- }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
14143
- });
14011
+ logout() {
14012
+ this.logger.trace("NativeInteractionClient - logout called.");
14013
+ return Promise.reject("Logout not implemented yet");
14144
14014
  }
14145
14015
  /**
14146
- * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
14147
- * @param event
14016
+ * Transform response from native platform into AuthenticationResult object which will be returned to the end user
14017
+ * @param response
14018
+ * @param request
14019
+ * @param reqTimestamp
14148
14020
  */
14149
- onWindowMessage(event) {
14150
- this.logger.trace("NativeMessageHandler - onWindowMessage called");
14151
- // We only accept messages from ourselves
14152
- if (event.source !== window) {
14153
- return;
14154
- }
14155
- const request = event.data;
14156
- if (!request.channel ||
14157
- request.channel !== NativeConstants.CHANNEL_ID) {
14158
- return;
14159
- }
14160
- if (request.extensionId && request.extensionId !== this.extensionId) {
14161
- return;
14021
+ async handleNativeResponse(response, request, reqTimestamp) {
14022
+ this.logger.trace("NativeInteractionClient - handleNativeResponse called.");
14023
+ // generate identifiers
14024
+ const idTokenClaims = extractTokenClaims(response.id_token, base64Decode);
14025
+ const homeAccountIdentifier = this.createHomeAccountIdentifier(response, idTokenClaims);
14026
+ const cachedhomeAccountId = this.browserStorage.getAccountInfoFilteredBy({
14027
+ nativeAccountId: request.accountId,
14028
+ })?.homeAccountId;
14029
+ // add exception for double brokering, please note this is temporary and will be fortified in future
14030
+ if (request.extraParameters?.child_client_id &&
14031
+ response.account.id !== request.accountId) {
14032
+ this.logger.info("handleNativeServerResponse: Double broker flow detected, ignoring accountId mismatch");
14162
14033
  }
14163
- if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
14164
- const handshakeResolver = this.handshakeResolvers.get(request.responseId);
14165
- /*
14166
- * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
14167
- * the proper response.
14168
- */
14169
- if (!handshakeResolver) {
14170
- this.logger.trace(`NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`);
14171
- return;
14172
- }
14173
- // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
14174
- this.logger.verbose(request.extensionId
14175
- ? `Extension with id: ${request.extensionId} not installed`
14176
- : "No extension installed");
14177
- clearTimeout(this.timeoutId);
14178
- this.messageChannel.port1.close();
14179
- this.messageChannel.port2.close();
14180
- window.removeEventListener("message", this.windowListener, false);
14181
- this.handshakeEvent.end({
14182
- success: false,
14183
- extensionInstalled: false,
14184
- });
14185
- handshakeResolver.reject(createBrowserAuthError(nativeExtensionNotInstalled));
14034
+ else if (homeAccountIdentifier !== cachedhomeAccountId &&
14035
+ response.account.id !== request.accountId) {
14036
+ // User switch in native broker prompt is not supported. All users must first sign in through web flow to ensure server state is in sync
14037
+ throw createNativeAuthError(userSwitch);
14186
14038
  }
14039
+ // Get the preferred_cache domain for the given authority
14040
+ const authority = await this.getDiscoveredAuthority({
14041
+ requestAuthority: request.authority,
14042
+ });
14043
+ const baseAccount = buildAccountToCache(this.browserStorage, authority, homeAccountIdentifier, base64Decode, idTokenClaims, response.client_info, undefined, // environment
14044
+ idTokenClaims.tid, undefined, // auth code payload
14045
+ response.account.id, this.logger);
14046
+ // Ensure expires_in is in number format
14047
+ response.expires_in = Number(response.expires_in);
14048
+ // generate authenticationResult
14049
+ const result = await this.generateAuthenticationResult(response, request, idTokenClaims, baseAccount, authority.canonicalAuthority, reqTimestamp);
14050
+ // cache accounts and tokens in the appropriate storage
14051
+ await this.cacheAccount(baseAccount);
14052
+ await this.cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, response.access_token, result.tenantId, reqTimestamp);
14053
+ return result;
14187
14054
  }
14188
14055
  /**
14189
- * Invoked when a message is received from the extension on the MessageChannel port
14190
- * @param event
14056
+ * creates an homeAccountIdentifier for the account
14057
+ * @param response
14058
+ * @param idTokenObj
14059
+ * @returns
14191
14060
  */
14192
- onChannelMessage(event) {
14193
- this.logger.trace("NativeMessageHandler - onChannelMessage called.");
14194
- const request = event.data;
14195
- const resolver = this.resolvers.get(request.responseId);
14196
- const handshakeResolver = this.handshakeResolvers.get(request.responseId);
14197
- try {
14198
- const method = request.body.method;
14199
- if (method === NativeExtensionMethod.Response) {
14200
- if (!resolver) {
14201
- return;
14202
- }
14203
- const response = request.body.response;
14204
- this.logger.trace("NativeMessageHandler - Received response from browser extension");
14205
- this.logger.tracePii(`NativeMessageHandler - Received response from browser extension: ${JSON.stringify(response)}`);
14206
- if (response.status !== "Success") {
14207
- resolver.reject(createNativeAuthError(response.code, response.description, response.ext));
14208
- }
14209
- else if (response.result) {
14210
- if (response.result["code"] &&
14211
- response.result["description"]) {
14212
- resolver.reject(createNativeAuthError(response.result["code"], response.result["description"], response.result["ext"]));
14213
- }
14214
- else {
14215
- resolver.resolve(response.result);
14216
- }
14217
- }
14218
- else {
14219
- throw createAuthError(unexpectedError, "Event does not contain result.");
14220
- }
14221
- this.resolvers.delete(request.responseId);
14061
+ createHomeAccountIdentifier(response, idTokenClaims) {
14062
+ // Save account in browser storage
14063
+ const homeAccountIdentifier = AccountEntity.generateHomeAccountId(response.client_info || Constants.EMPTY_STRING, AuthorityType.Default, this.logger, this.browserCrypto, idTokenClaims);
14064
+ return homeAccountIdentifier;
14065
+ }
14066
+ /**
14067
+ * Helper to generate scopes
14068
+ * @param response
14069
+ * @param request
14070
+ * @returns
14071
+ */
14072
+ generateScopes(response, request) {
14073
+ return response.scope
14074
+ ? ScopeSet.fromString(response.scope)
14075
+ : ScopeSet.fromString(request.scope);
14076
+ }
14077
+ /**
14078
+ * If PoP token is requesred, records the PoP token if returned from the WAM, else generates one in the browser
14079
+ * @param request
14080
+ * @param response
14081
+ */
14082
+ async generatePopAccessToken(response, request) {
14083
+ if (request.tokenType === AuthenticationScheme.POP &&
14084
+ request.signPopToken) {
14085
+ /**
14086
+ * This code prioritizes SHR returned from the native layer. In case of error/SHR not calculated from WAM and the AT
14087
+ * is still received, SHR is calculated locally
14088
+ */
14089
+ // Check if native layer returned an SHR token
14090
+ if (response.shr) {
14091
+ this.logger.trace("handleNativeServerResponse: SHR is enabled in native layer");
14092
+ return response.shr;
14222
14093
  }
14223
- else if (method === NativeExtensionMethod.HandshakeResponse) {
14224
- if (!handshakeResolver) {
14225
- this.logger.trace(`NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`);
14226
- return;
14227
- }
14228
- clearTimeout(this.timeoutId); // Clear setTimeout
14229
- window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
14230
- this.extensionId = request.extensionId;
14231
- this.extensionVersion = request.body.version;
14232
- this.logger.verbose(`NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`);
14233
- this.handshakeEvent.end({
14234
- extensionInstalled: true,
14235
- success: true,
14236
- });
14237
- handshakeResolver.resolve();
14238
- this.handshakeResolvers.delete(request.responseId);
14094
+ // Generate SHR in msal js if WAM does not compute it when POP is enabled
14095
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14096
+ const shrParameters = {
14097
+ resourceRequestMethod: request.resourceRequestMethod,
14098
+ resourceRequestUri: request.resourceRequestUri,
14099
+ shrClaims: request.shrClaims,
14100
+ shrNonce: request.shrNonce,
14101
+ };
14102
+ /**
14103
+ * KeyID must be present in the native request from when the PoP key was generated in order for
14104
+ * PopTokenGenerator to query the full key for signing
14105
+ */
14106
+ if (!request.keyId) {
14107
+ throw createClientAuthError(keyIdMissing);
14239
14108
  }
14240
- // Do nothing if method is not Response or HandshakeResponse
14109
+ return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
14241
14110
  }
14242
- catch (err) {
14243
- this.logger.error("Error parsing response from WAM Extension");
14244
- this.logger.errorPii(`Error parsing response from WAM Extension: ${err}`);
14245
- this.logger.errorPii(`Unable to parse ${event}`);
14246
- if (resolver) {
14247
- resolver.reject(err);
14248
- }
14249
- else if (handshakeResolver) {
14250
- handshakeResolver.reject(err);
14251
- }
14111
+ else {
14112
+ return response.access_token;
14252
14113
  }
14253
14114
  }
14254
14115
  /**
14255
- * Returns the Id for the browser extension this handler is communicating with
14116
+ * Generates authentication result
14117
+ * @param response
14118
+ * @param request
14119
+ * @param idTokenObj
14120
+ * @param accountEntity
14121
+ * @param authority
14122
+ * @param reqTimestamp
14256
14123
  * @returns
14257
14124
  */
14258
- getExtensionId() {
14259
- return this.extensionId;
14125
+ async generateAuthenticationResult(response, request, idTokenClaims, accountEntity, authority, reqTimestamp) {
14126
+ // Add Native Broker fields to Telemetry
14127
+ const mats = this.addTelemetryFromNativeResponse(response);
14128
+ // If scopes not returned in server response, use request scopes
14129
+ const responseScopes = response.scope
14130
+ ? ScopeSet.fromString(response.scope)
14131
+ : ScopeSet.fromString(request.scope);
14132
+ const accountProperties = response.account.properties || {};
14133
+ const uid = accountProperties["UID"] ||
14134
+ idTokenClaims.oid ||
14135
+ idTokenClaims.sub ||
14136
+ Constants.EMPTY_STRING;
14137
+ const tid = accountProperties["TenantId"] ||
14138
+ idTokenClaims.tid ||
14139
+ Constants.EMPTY_STRING;
14140
+ const accountInfo = updateAccountTenantProfileData(accountEntity.getAccountInfo(), undefined, // tenantProfile optional
14141
+ idTokenClaims, response.id_token);
14142
+ /**
14143
+ * In pairwise broker flows, this check prevents the broker's native account id
14144
+ * from being returned over the embedded app's account id.
14145
+ */
14146
+ if (accountInfo.nativeAccountId !== response.account.id) {
14147
+ accountInfo.nativeAccountId = response.account.id;
14148
+ }
14149
+ // generate PoP token as needed
14150
+ const responseAccessToken = await this.generatePopAccessToken(response, request);
14151
+ const tokenType = request.tokenType === AuthenticationScheme.POP
14152
+ ? AuthenticationScheme.POP
14153
+ : AuthenticationScheme.BEARER;
14154
+ const result = {
14155
+ authority: authority,
14156
+ uniqueId: uid,
14157
+ tenantId: tid,
14158
+ scopes: responseScopes.asArray(),
14159
+ account: accountInfo,
14160
+ idToken: response.id_token,
14161
+ idTokenClaims: idTokenClaims,
14162
+ accessToken: responseAccessToken,
14163
+ fromCache: mats ? this.isResponseFromCache(mats) : false,
14164
+ // Request timestamp and NativeResponse expires_in are in seconds, converting to Date for AuthenticationResult
14165
+ expiresOn: toDateFromSeconds(reqTimestamp + response.expires_in),
14166
+ tokenType: tokenType,
14167
+ correlationId: this.correlationId,
14168
+ state: response.state,
14169
+ fromNativeBroker: true,
14170
+ };
14171
+ return result;
14260
14172
  }
14261
14173
  /**
14262
- * Returns the version for the browser extension this handler is communicating with
14263
- * @returns
14174
+ * cache the account entity in browser storage
14175
+ * @param accountEntity
14264
14176
  */
14265
- getExtensionVersion() {
14266
- return this.extensionVersion;
14177
+ async cacheAccount(accountEntity) {
14178
+ // Store the account info and hence `nativeAccountId` in browser cache
14179
+ await this.browserStorage.setAccount(accountEntity, this.correlationId);
14180
+ // Remove any existing cached tokens for this account in browser storage
14181
+ this.browserStorage.removeAccountContext(accountEntity).catch((e) => {
14182
+ this.logger.error(`Error occurred while removing account context from browser storage. ${e}`);
14183
+ });
14267
14184
  }
14268
14185
  /**
14269
- * Returns boolean indicating whether or not the request should attempt to use native broker
14270
- * @param logger
14271
- * @param config
14272
- * @param nativeExtensionProvider
14273
- * @param authenticationScheme
14186
+ * Stores the access_token and id_token in inmemory storage
14187
+ * @param response
14188
+ * @param request
14189
+ * @param homeAccountIdentifier
14190
+ * @param idTokenObj
14191
+ * @param responseAccessToken
14192
+ * @param tenantId
14193
+ * @param reqTimestamp
14274
14194
  */
14275
- static isPlatformBrokerAvailable(config, logger, nativeExtensionProvider, authenticationScheme) {
14276
- logger.trace("isPlatformBrokerAvailable called");
14277
- if (!config.system.allowPlatformBroker) {
14278
- logger.trace("isPlatformBrokerAvailable: allowPlatformBroker is not enabled, returning false");
14279
- // Developer disabled WAM
14280
- return false;
14195
+ cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, responseAccessToken, tenantId, reqTimestamp) {
14196
+ const cachedIdToken = createIdTokenEntity(homeAccountIdentifier, request.authority, response.id_token || "", request.clientId, idTokenClaims.tid || "");
14197
+ // cache accessToken in inmemory storage
14198
+ const expiresIn = request.tokenType === AuthenticationScheme.POP
14199
+ ? Constants.SHR_NONCE_VALIDITY
14200
+ : (typeof response.expires_in === "string"
14201
+ ? parseInt(response.expires_in, 10)
14202
+ : response.expires_in) || 0;
14203
+ const tokenExpirationSeconds = reqTimestamp + expiresIn;
14204
+ const responseScopes = this.generateScopes(response, request);
14205
+ const cachedAccessToken = createAccessTokenEntity(homeAccountIdentifier, request.authority, responseAccessToken, request.clientId, idTokenClaims.tid || tenantId, responseScopes.printScopes(), tokenExpirationSeconds, 0, base64Decode, undefined, request.tokenType, undefined, request.keyId);
14206
+ const nativeCacheRecord = {
14207
+ idToken: cachedIdToken,
14208
+ accessToken: cachedAccessToken,
14209
+ };
14210
+ return this.nativeStorageManager.saveCacheRecord(nativeCacheRecord, this.correlationId, request.storeInCache);
14211
+ }
14212
+ addTelemetryFromNativeResponse(response) {
14213
+ const mats = this.getMATSFromResponse(response);
14214
+ if (!mats) {
14215
+ return null;
14281
14216
  }
14282
- if (!nativeExtensionProvider) {
14283
- logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
14284
- // Extension is not available
14285
- return false;
14217
+ this.performanceClient.addFields({
14218
+ extensionId: this.nativeMessageHandler.getExtensionId(),
14219
+ extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
14220
+ matsBrokerVersion: mats.broker_version,
14221
+ matsAccountJoinOnStart: mats.account_join_on_start,
14222
+ matsAccountJoinOnEnd: mats.account_join_on_end,
14223
+ matsDeviceJoin: mats.device_join,
14224
+ matsPromptBehavior: mats.prompt_behavior,
14225
+ matsApiErrorCode: mats.api_error_code,
14226
+ matsUiVisible: mats.ui_visible,
14227
+ matsSilentCode: mats.silent_code,
14228
+ matsSilentBiSubCode: mats.silent_bi_sub_code,
14229
+ matsSilentMessage: mats.silent_message,
14230
+ matsSilentStatus: mats.silent_status,
14231
+ matsHttpStatus: mats.http_status,
14232
+ matsHttpEventCount: mats.http_event_count,
14233
+ }, this.correlationId);
14234
+ return mats;
14235
+ }
14236
+ /**
14237
+ * Validates native platform response before processing
14238
+ * @param response
14239
+ */
14240
+ validateNativeResponse(response) {
14241
+ if (response.hasOwnProperty("access_token") &&
14242
+ response.hasOwnProperty("id_token") &&
14243
+ response.hasOwnProperty("client_info") &&
14244
+ response.hasOwnProperty("account") &&
14245
+ response.hasOwnProperty("scope") &&
14246
+ response.hasOwnProperty("expires_in")) {
14247
+ return response;
14286
14248
  }
14287
- if (authenticationScheme) {
14288
- switch (authenticationScheme) {
14289
- case AuthenticationScheme.BEARER:
14290
- case AuthenticationScheme.POP:
14291
- logger.trace("isPlatformBrokerAvailable: authenticationScheme is supported, returning true");
14292
- return true;
14293
- default:
14294
- logger.trace("isPlatformBrokerAvailable: authenticationScheme is not supported, returning false");
14295
- return false;
14296
- }
14249
+ else {
14250
+ throw createAuthError(unexpectedError, "Response missing expected properties.");
14297
14251
  }
14298
- return true;
14299
- }
14300
- }
14301
-
14302
- /*
14303
- * Copyright (c) Microsoft Corporation. All rights reserved.
14304
- * Licensed under the MIT License.
14305
- */
14306
- /**
14307
- * Abstract class which defines operations for a browser interaction handling class.
14308
- */
14309
- class InteractionHandler {
14310
- constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
14311
- this.authModule = authCodeModule;
14312
- this.browserStorage = storageImpl;
14313
- this.authCodeRequest = authCodeRequest;
14314
- this.logger = logger;
14315
- this.performanceClient = performanceClient;
14316
14252
  }
14317
14253
  /**
14318
- * Function to handle response parameters from hash.
14319
- * @param locationHash
14254
+ * Gets MATS telemetry from native response
14255
+ * @param response
14256
+ * @returns
14320
14257
  */
14321
- async handleCodeResponse(response, request) {
14322
- this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponse, request.correlationId);
14323
- let authCodeResponse;
14324
- try {
14325
- authCodeResponse = getAuthorizationCodePayload(response, request.state);
14326
- }
14327
- catch (e) {
14328
- if (e instanceof ServerError &&
14329
- e.subError === userCancelled) {
14330
- // Translate server error caused by user closing native prompt to corresponding first class MSAL error
14331
- throw createBrowserAuthError(userCancelled);
14258
+ getMATSFromResponse(response) {
14259
+ if (response.properties.MATS) {
14260
+ try {
14261
+ return JSON.parse(response.properties.MATS);
14332
14262
  }
14333
- else {
14334
- throw e;
14263
+ catch (e) {
14264
+ this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
14335
14265
  }
14336
14266
  }
14337
- return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
14267
+ return null;
14338
14268
  }
14339
14269
  /**
14340
- * Process auth code response from AAD
14341
- * @param authCodeResponse
14342
- * @param state
14343
- * @param authority
14344
- * @param networkModule
14270
+ * Returns whether or not response came from native cache
14271
+ * @param response
14345
14272
  * @returns
14346
14273
  */
14347
- async handleCodeResponseFromServer(authCodeResponse, request, validateNonce = true) {
14348
- this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponseFromServer, request.correlationId);
14349
- this.logger.trace("InteractionHandler.handleCodeResponseFromServer called");
14350
- // Assign code to request
14351
- this.authCodeRequest.code = authCodeResponse.code;
14352
- // Check for new cloud instance
14353
- if (authCodeResponse.cloud_instance_host_name) {
14354
- await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, request.correlationId)(authCodeResponse.cloud_instance_host_name, request.correlationId);
14355
- }
14356
- // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
14357
- if (validateNonce) {
14358
- // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
14359
- authCodeResponse.nonce = request.nonce || undefined;
14360
- }
14361
- authCodeResponse.state = request.state;
14362
- // Add CCS parameters if available
14363
- if (authCodeResponse.client_info) {
14364
- this.authCodeRequest.clientInfo = authCodeResponse.client_info;
14365
- }
14366
- else {
14367
- const ccsCred = this.createCcsCredentials(request);
14368
- if (ccsCred) {
14369
- this.authCodeRequest.ccsCredential = ccsCred;
14370
- }
14274
+ isResponseFromCache(mats) {
14275
+ if (typeof mats.is_cached === "undefined") {
14276
+ this.logger.verbose("NativeInteractionClient - MATS telemetry does not contain field indicating if response was served from cache. Returning false.");
14277
+ return false;
14371
14278
  }
14372
- // Acquire token with retrieved code.
14373
- const tokenResponse = (await invokeAsync(this.authModule.acquireToken.bind(this.authModule), PerformanceEvents.AuthClientAcquireToken, this.logger, this.performanceClient, request.correlationId)(this.authCodeRequest, authCodeResponse));
14374
- return tokenResponse;
14279
+ return !!mats.is_cached;
14375
14280
  }
14376
14281
  /**
14377
- * Build ccs creds if available
14282
+ * Translates developer provided request object into NativeRequest object
14283
+ * @param request
14378
14284
  */
14379
- createCcsCredentials(request) {
14285
+ async initializeNativeRequest(request) {
14286
+ this.logger.trace("NativeInteractionClient - initializeNativeRequest called");
14287
+ const requestAuthority = request.authority || this.config.auth.authority;
14380
14288
  if (request.account) {
14381
- return {
14382
- credential: request.account.homeAccountId,
14383
- type: CcsCredentialType.HOME_ACCOUNT_ID,
14384
- };
14289
+ // validate authority
14290
+ await this.getDiscoveredAuthority({
14291
+ requestAuthority,
14292
+ requestAzureCloudOptions: request.azureCloudOptions,
14293
+ account: request.account,
14294
+ });
14385
14295
  }
14386
- else if (request.loginHint) {
14387
- return {
14388
- credential: request.loginHint,
14389
- type: CcsCredentialType.UPN,
14296
+ const canonicalAuthority = new UrlString(requestAuthority);
14297
+ canonicalAuthority.validateAsUri();
14298
+ // scopes are expected to be received by the native broker as "scope" and will be added to the request below. Other properties that should be dropped from the request to the native broker can be included in the object destructuring here.
14299
+ const { scopes, ...remainingProperties } = request;
14300
+ const scopeSet = new ScopeSet(scopes || []);
14301
+ scopeSet.appendScopes(OIDC_DEFAULT_SCOPES);
14302
+ const getPrompt = () => {
14303
+ // If request is silent, prompt is always none
14304
+ switch (this.apiId) {
14305
+ case ApiId.ssoSilent:
14306
+ case ApiId.acquireTokenSilent_silentFlow:
14307
+ this.logger.trace("initializeNativeRequest: silent request sets prompt to none");
14308
+ return PromptValue.NONE;
14309
+ }
14310
+ // Prompt not provided, request may proceed and native broker decides if it needs to prompt
14311
+ if (!request.prompt) {
14312
+ this.logger.trace("initializeNativeRequest: prompt was not provided");
14313
+ return undefined;
14314
+ }
14315
+ // If request is interactive, check if prompt provided is allowed to go directly to native broker
14316
+ switch (request.prompt) {
14317
+ case PromptValue.NONE:
14318
+ case PromptValue.CONSENT:
14319
+ case PromptValue.LOGIN:
14320
+ this.logger.trace("initializeNativeRequest: prompt is compatible with native flow");
14321
+ return request.prompt;
14322
+ default:
14323
+ this.logger.trace(`initializeNativeRequest: prompt = ${request.prompt} is not compatible with native flow`);
14324
+ throw createBrowserAuthError(nativePromptNotSupported);
14325
+ }
14326
+ };
14327
+ const validatedRequest = {
14328
+ ...remainingProperties,
14329
+ accountId: this.accountId,
14330
+ clientId: this.config.auth.clientId,
14331
+ authority: canonicalAuthority.urlString,
14332
+ scope: scopeSet.printScopes(),
14333
+ redirectUri: this.getRedirectUri(request.redirectUri),
14334
+ prompt: getPrompt(),
14335
+ correlationId: this.correlationId,
14336
+ tokenType: request.authenticationScheme,
14337
+ windowTitleSubstring: document.title,
14338
+ extraParameters: {
14339
+ ...request.extraQueryParameters,
14340
+ ...request.tokenQueryParameters,
14341
+ },
14342
+ extendedExpiryToken: false,
14343
+ keyId: request.popKid,
14344
+ };
14345
+ // Check for PoP token requests: signPopToken should only be set to true if popKid is not set
14346
+ if (validatedRequest.signPopToken && !!request.popKid) {
14347
+ throw createBrowserAuthError(invalidPopTokenRequest);
14348
+ }
14349
+ this.handleExtraBrokerParams(validatedRequest);
14350
+ validatedRequest.extraParameters =
14351
+ validatedRequest.extraParameters || {};
14352
+ validatedRequest.extraParameters.telemetry =
14353
+ NativeConstants.MATS_TELEMETRY;
14354
+ if (request.authenticationScheme === AuthenticationScheme.POP) {
14355
+ // add POP request type
14356
+ const shrParameters = {
14357
+ resourceRequestUri: request.resourceRequestUri,
14358
+ resourceRequestMethod: request.resourceRequestMethod,
14359
+ shrClaims: request.shrClaims,
14360
+ shrNonce: request.shrNonce,
14390
14361
  };
14362
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14363
+ // generate reqCnf if not provided in the request
14364
+ let reqCnfData;
14365
+ if (!validatedRequest.keyId) {
14366
+ const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, this.logger, this.performanceClient, request.correlationId)(shrParameters, this.logger);
14367
+ reqCnfData = generatedReqCnfData.reqCnfString;
14368
+ validatedRequest.keyId = generatedReqCnfData.kid;
14369
+ validatedRequest.signPopToken = true;
14370
+ }
14371
+ else {
14372
+ reqCnfData = this.browserCrypto.base64UrlEncode(JSON.stringify({ kid: validatedRequest.keyId }));
14373
+ validatedRequest.signPopToken = false;
14374
+ }
14375
+ // SPAs require whole string to be passed to broker
14376
+ validatedRequest.reqCnf = reqCnfData;
14391
14377
  }
14392
- return null;
14378
+ this.addRequestSKUs(validatedRequest);
14379
+ return validatedRequest;
14393
14380
  }
14394
- }
14395
-
14396
- /*
14397
- * Copyright (c) Microsoft Corporation. All rights reserved.
14398
- * Licensed under the MIT License.
14399
- */
14400
- function deserializeResponse(responseString, responseLocation, logger) {
14401
- // Deserialize hash fragment response parameters.
14402
- const serverParams = getDeserializedResponse(responseString);
14403
- if (!serverParams) {
14404
- if (!stripLeadingHashOrQuery(responseString)) {
14405
- // Hash or Query string is empty
14406
- logger.error(`The request has returned to the redirectUri but a ${responseLocation} is not present. It's likely that the ${responseLocation} has been removed or the page has been redirected by code running on the redirectUri page.`);
14407
- throw createBrowserAuthError(hashEmptyError);
14381
+ /**
14382
+ * Handles extra broker request parameters
14383
+ * @param request {NativeTokenRequest}
14384
+ * @private
14385
+ */
14386
+ handleExtraBrokerParams(request) {
14387
+ const hasExtraBrokerParams = request.extraParameters &&
14388
+ request.extraParameters.hasOwnProperty(BROKER_CLIENT_ID) &&
14389
+ request.extraParameters.hasOwnProperty(BROKER_REDIRECT_URI) &&
14390
+ request.extraParameters.hasOwnProperty(CLIENT_ID);
14391
+ if (!request.embeddedClientId && !hasExtraBrokerParams) {
14392
+ return;
14408
14393
  }
14409
- else {
14410
- logger.error(`A ${responseLocation} is present in the iframe but it does not contain known properties. It's likely that the ${responseLocation} has been replaced by code running on the redirectUri page.`);
14411
- logger.errorPii(`The ${responseLocation} detected is: ${responseString}`);
14412
- throw createBrowserAuthError(hashDoesNotContainKnownProperties);
14394
+ let child_client_id = "";
14395
+ const child_redirect_uri = request.redirectUri;
14396
+ if (request.embeddedClientId) {
14397
+ request.redirectUri = this.config.auth.redirectUri;
14398
+ child_client_id = request.embeddedClientId;
14413
14399
  }
14414
- }
14415
- return serverParams;
14416
- }
14417
- /**
14418
- * Returns the interaction type that the response object belongs to
14419
- */
14420
- function validateInteractionType(response, browserCrypto, interactionType) {
14421
- if (!response.state) {
14422
- throw createBrowserAuthError(noStateInHash);
14423
- }
14424
- const platformStateObj = extractBrowserRequestState(browserCrypto, response.state);
14425
- if (!platformStateObj) {
14426
- throw createBrowserAuthError(unableToParseState);
14427
- }
14428
- if (platformStateObj.interactionType !== interactionType) {
14429
- throw createBrowserAuthError(stateInteractionTypeMismatch);
14400
+ else if (request.extraParameters) {
14401
+ request.redirectUri =
14402
+ request.extraParameters[BROKER_REDIRECT_URI];
14403
+ child_client_id =
14404
+ request.extraParameters[CLIENT_ID];
14405
+ }
14406
+ request.extraParameters = {
14407
+ child_client_id,
14408
+ child_redirect_uri,
14409
+ };
14410
+ this.performanceClient?.addFields({
14411
+ embeddedClientId: child_client_id,
14412
+ embeddedRedirectUri: child_redirect_uri,
14413
+ }, request.correlationId);
14430
14414
  }
14431
14415
  }
14432
14416
 
@@ -14490,9 +14474,147 @@ async function getAuthCodeRequestUrl(config, authority, request, logger, perform
14490
14474
  throw createClientConfigurationError(pkceParamsMissing);
14491
14475
  }
14492
14476
  const parameters = await invokeAsync(getStandardParameters, PerformanceEvents.GetStandardParams, logger, performanceClient, request.correlationId)(config, authority, request, logger, performanceClient);
14493
- addResponseTypeCode(parameters);
14477
+ addResponseType(parameters, OAuthResponseType.CODE);
14494
14478
  addCodeChallengeParams(parameters, request.codeChallenge, Constants.S256_CODE_CHALLENGE_METHOD);
14479
+ addExtraQueryParameters(parameters, request.extraQueryParameters || {});
14495
14480
  return getAuthorizeUrl(authority, parameters);
14481
+ }
14482
+ /**
14483
+ * Gets the form that will be posted to /authorize with request parameters when using EAR
14484
+ */
14485
+ async function getEARForm(frame, config, authority, request, logger, performanceClient) {
14486
+ if (!request.earJwk) {
14487
+ throw createBrowserAuthError(earJwkEmpty);
14488
+ }
14489
+ const parameters = await getStandardParameters(config, authority, request, logger, performanceClient);
14490
+ addResponseType(parameters, OAuthResponseType.IDTOKEN_TOKEN_REFRESHTOKEN);
14491
+ addEARParameters(parameters, request.earJwk);
14492
+ const queryParams = new Map();
14493
+ addExtraQueryParameters(queryParams, request.extraQueryParameters || {});
14494
+ const url = getAuthorizeUrl(authority, queryParams);
14495
+ return createForm(frame, url, parameters);
14496
+ }
14497
+ /**
14498
+ * Creates form element in the provided document with auth parameters in the post body
14499
+ * @param frame
14500
+ * @param authorizeUrl
14501
+ * @param parameters
14502
+ * @returns
14503
+ */
14504
+ function createForm(frame, authorizeUrl, parameters) {
14505
+ const form = frame.createElement("form");
14506
+ form.method = "post";
14507
+ form.action = authorizeUrl;
14508
+ parameters.forEach((value, key) => {
14509
+ const param = frame.createElement("input");
14510
+ param.hidden = true;
14511
+ param.name = key;
14512
+ param.value = value;
14513
+ form.appendChild(param);
14514
+ });
14515
+ frame.body.appendChild(form);
14516
+ return form;
14517
+ }
14518
+ /**
14519
+ * Response handler when server returns accountId on the /authorize request
14520
+ * @param request
14521
+ * @param accountId
14522
+ * @param apiId
14523
+ * @param config
14524
+ * @param browserStorage
14525
+ * @param nativeStorage
14526
+ * @param eventHandler
14527
+ * @param logger
14528
+ * @param performanceClient
14529
+ * @param nativeMessageHandler
14530
+ * @returns
14531
+ */
14532
+ async function handleResponsePlatformBroker(request, accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14533
+ if (!nativeMessageHandler) {
14534
+ throw createBrowserAuthError(nativeConnectionNotEstablished);
14535
+ }
14536
+ const browserCrypto = new CryptoOps(logger, performanceClient);
14537
+ const nativeInteractionClient = new NativeInteractionClient(config, browserStorage, browserCrypto, logger, eventHandler, config.system.navigationClient, apiId, performanceClient, nativeMessageHandler, accountId, nativeStorage, request.correlationId);
14538
+ const { userRequestState } = ProtocolUtils.parseRequestState(browserCrypto, request.state);
14539
+ return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), PerformanceEvents.NativeInteractionClientAcquireToken, logger, performanceClient, request.correlationId)({
14540
+ ...request,
14541
+ state: userRequestState,
14542
+ prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
14543
+ });
14544
+ }
14545
+ /**
14546
+ * Response handler when server returns code on the /authorize request
14547
+ * @param request
14548
+ * @param response
14549
+ * @param codeVerifier
14550
+ * @param authClient
14551
+ * @param browserStorage
14552
+ * @param logger
14553
+ * @param performanceClient
14554
+ * @returns
14555
+ */
14556
+ async function handleResponseCode(request, response, codeVerifier, apiId, config, authClient, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14557
+ // Remove throttle if it exists
14558
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14559
+ if (response.accountId) {
14560
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, response.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14561
+ }
14562
+ const authCodeRequest = {
14563
+ ...request,
14564
+ code: response.code || "",
14565
+ codeVerifier: codeVerifier,
14566
+ };
14567
+ // Create popup interaction handler.
14568
+ const interactionHandler = new InteractionHandler(authClient, browserStorage, authCodeRequest, logger, performanceClient);
14569
+ // Handle response from hash string.
14570
+ const result = await invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), PerformanceEvents.HandleCodeResponse, logger, performanceClient, request.correlationId)(response, request);
14571
+ return result;
14572
+ }
14573
+ /**
14574
+ * Response handler when server returns ear_jwe on the /authorize request
14575
+ * @param request
14576
+ * @param response
14577
+ * @param apiId
14578
+ * @param config
14579
+ * @param authority
14580
+ * @param browserStorage
14581
+ * @param nativeStorage
14582
+ * @param eventHandler
14583
+ * @param logger
14584
+ * @param performanceClient
14585
+ * @param nativeMessageHandler
14586
+ * @returns
14587
+ */
14588
+ async function handleResponseEAR(request, response, apiId, config, authority, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14589
+ // Remove throttle if it exists
14590
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14591
+ // Validate state & check response for errors
14592
+ validateAuthorizationResponse(response, request.state);
14593
+ if (!response.ear_jwe) {
14594
+ throw createBrowserAuthError(earJweEmpty);
14595
+ }
14596
+ if (!request.earJwk) {
14597
+ throw createBrowserAuthError(earJwkEmpty);
14598
+ }
14599
+ const decryptedData = JSON.parse(await invokeAsync(decryptEarResponse, PerformanceEvents.DecryptEarResponse, logger, performanceClient, request.correlationId)(request.earJwk, response.ear_jwe));
14600
+ if (decryptedData.accountId) {
14601
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, decryptedData.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14602
+ }
14603
+ const responseHandler = new ResponseHandler(config.auth.clientId, browserStorage, new CryptoOps(logger, performanceClient), logger, null, null, performanceClient);
14604
+ // Validate response. This function throws a server error if an error is returned by the server.
14605
+ responseHandler.validateTokenResponse(decryptedData);
14606
+ // Temporary until response handler is refactored to be more flow agnostic.
14607
+ const additionalData = {
14608
+ code: "",
14609
+ state: request.state,
14610
+ nonce: request.nonce,
14611
+ client_info: decryptedData.client_info,
14612
+ cloud_graph_host_name: decryptedData.cloud_graph_host_name,
14613
+ cloud_instance_host_name: decryptedData.cloud_instance_host_name,
14614
+ cloud_instance_name: decryptedData.cloud_instance_name,
14615
+ msgraph_host: decryptedData.msgraph_host,
14616
+ };
14617
+ return (await invokeAsync(responseHandler.handleServerTokenResponse.bind(responseHandler), PerformanceEvents.HandleServerTokenResponse, logger, performanceClient, request.correlationId)(decryptedData, authority, nowSeconds(), request, additionalData, undefined, undefined, undefined, undefined));
14496
14618
  }
14497
14619
 
14498
14620
  /*
@@ -14560,6 +14682,7 @@ class PopupClient extends StandardInteractionClient {
14560
14682
  // Properly sets this reference for the unload event.
14561
14683
  this.unloadWindow = this.unloadWindow.bind(this);
14562
14684
  this.nativeStorage = nativeStorageImpl;
14685
+ this.eventHandler = eventHandler;
14563
14686
  }
14564
14687
  /**
14565
14688
  * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
@@ -14635,11 +14758,7 @@ class PopupClient extends StandardInteractionClient {
14635
14758
  */
14636
14759
  async acquireTokenPopupAsync(request, popupParams, pkceCodes) {
14637
14760
  this.logger.verbose("acquireTokenPopupAsync called");
14638
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14639
14761
  const validRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(request, exports.InteractionType.Popup);
14640
- const pkce = pkceCodes ||
14641
- (await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId));
14642
- validRequest.codeChallenge = pkce.challenge;
14643
14762
  /*
14644
14763
  * Skip pre-connect for async popups to reduce time between user interaction and popup window creation to avoid
14645
14764
  * popup from being blocked by browsers with shorter popup timers
@@ -14647,65 +14766,49 @@ class PopupClient extends StandardInteractionClient {
14647
14766
  if (popupParams.popup) {
14648
14767
  preconnect(validRequest.authority);
14649
14768
  }
14769
+ const isPlatformBroker = NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
14770
+ validRequest.platformBroker = isPlatformBroker;
14771
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
14772
+ return this.executeEarFlow(validRequest, popupParams);
14773
+ }
14774
+ else {
14775
+ return this.executeCodeFlow(validRequest, popupParams, pkceCodes);
14776
+ }
14777
+ }
14778
+ /**
14779
+ * Executes auth code + PKCE flow
14780
+ * @param request
14781
+ * @param popupParams
14782
+ * @param pkceCodes
14783
+ * @returns
14784
+ */
14785
+ async executeCodeFlow(request, popupParams, pkceCodes) {
14786
+ const correlationId = request.correlationId;
14787
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14788
+ const pkce = pkceCodes ||
14789
+ (await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId));
14790
+ const popupRequest = {
14791
+ ...request,
14792
+ codeChallenge: pkce.challenge,
14793
+ };
14650
14794
  try {
14651
14795
  // Initialize the client
14652
- const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
14796
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, correlationId)({
14653
14797
  serverTelemetryManager,
14654
- requestAuthority: validRequest.authority,
14655
- requestAzureCloudOptions: validRequest.azureCloudOptions,
14656
- requestExtraQueryParameters: validRequest.extraQueryParameters,
14657
- account: validRequest.account,
14798
+ requestAuthority: popupRequest.authority,
14799
+ requestAzureCloudOptions: popupRequest.azureCloudOptions,
14800
+ requestExtraQueryParameters: popupRequest.extraQueryParameters,
14801
+ account: popupRequest.account,
14658
14802
  });
14659
- const isPlatformBroker = NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
14660
- // Start measurement for server calls with native brokering enabled
14661
- let fetchNativeAccountIdMeasurement;
14662
- if (isPlatformBroker) {
14663
- fetchNativeAccountIdMeasurement =
14664
- this.performanceClient.startMeasurement(PerformanceEvents.FetchAccountIdWithNativeBroker, request.correlationId);
14665
- }
14666
14803
  // Create acquire token url.
14667
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, validRequest.correlationId)(this.config, authClient.authority, {
14668
- ...validRequest,
14669
- platformBroker: isPlatformBroker,
14670
- }, this.logger, this.performanceClient);
14804
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, popupRequest, this.logger, this.performanceClient);
14671
14805
  // Show the UI once the url has been created. Get the window handle for the popup.
14672
14806
  const popupWindow = this.initiateAuthRequest(navigateUrl, popupParams);
14673
14807
  this.eventHandler.emitEvent(EventType.POPUP_OPENED, exports.InteractionType.Popup, { popupWindow }, null);
14674
14808
  // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
14675
14809
  const responseString = await this.monitorPopupForHash(popupWindow, popupParams.popupWindowParent);
14676
14810
  const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, this.config.auth.OIDCOptions.serverResponseType, this.logger);
14677
- // Remove throttle if it exists
14678
- ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, validRequest);
14679
- if (serverParams.accountId) {
14680
- this.logger.verbose("Account id found in hash, calling WAM for token");
14681
- // end measurement for server call with native brokering enabled
14682
- if (fetchNativeAccountIdMeasurement) {
14683
- fetchNativeAccountIdMeasurement.end({
14684
- success: true,
14685
- isNativeBroker: true,
14686
- });
14687
- }
14688
- if (!this.nativeMessageHandler) {
14689
- throw createBrowserAuthError(nativeConnectionNotEstablished);
14690
- }
14691
- const nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.acquireTokenPopup, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.nativeStorage, validRequest.correlationId);
14692
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, validRequest.state);
14693
- return await nativeInteractionClient.acquireToken({
14694
- ...validRequest,
14695
- state: userRequestState,
14696
- prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
14697
- });
14698
- }
14699
- const authCodeRequest = {
14700
- ...validRequest,
14701
- code: serverParams.code || "",
14702
- codeVerifier: pkce.verifier,
14703
- };
14704
- // Create popup interaction handler.
14705
- const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
14706
- // Handle response from hash string.
14707
- const result = await interactionHandler.handleCodeResponse(serverParams, validRequest);
14708
- return result;
14811
+ return await invokeAsync(handleResponseCode, PerformanceEvents.HandleResponseCode, this.logger, this.performanceClient, correlationId)(request, serverParams, pkce.verifier, ApiId.acquireTokenPopup, this.config, authClient, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
14709
14812
  }
14710
14813
  catch (e) {
14711
14814
  // Close the synchronous popup if an error is thrown before the window unload event is registered
@@ -14717,6 +14820,32 @@ class PopupClient extends StandardInteractionClient {
14717
14820
  throw e;
14718
14821
  }
14719
14822
  }
14823
+ /**
14824
+ * Executes EAR flow
14825
+ * @param request
14826
+ */
14827
+ async executeEarFlow(request, popupParams) {
14828
+ const correlationId = request.correlationId;
14829
+ // Get the frame handle for the silent request
14830
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
14831
+ requestAuthority: request.authority,
14832
+ requestAzureCloudOptions: request.azureCloudOptions,
14833
+ requestExtraQueryParameters: request.extraQueryParameters,
14834
+ account: request.account,
14835
+ });
14836
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
14837
+ const popupRequest = {
14838
+ ...request,
14839
+ earJwk: earJwk,
14840
+ };
14841
+ const popupWindow = popupParams.popup || this.openPopup("about:blank", popupParams);
14842
+ const form = await getEARForm(popupWindow.document, this.config, discoveredAuthority, popupRequest, this.logger, this.performanceClient);
14843
+ form.submit();
14844
+ // Monitor the popup for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
14845
+ const responseString = await invokeAsync(this.monitorPopupForHash.bind(this), PerformanceEvents.SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(popupWindow, popupParams.popupWindowParent);
14846
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, this.config.auth.OIDCOptions.serverResponseType, this.logger);
14847
+ return invokeAsync(handleResponseEAR, PerformanceEvents.HandleResponseEar, this.logger, this.performanceClient, correlationId)(popupRequest, serverParams, ApiId.acquireTokenPopup, this.config, discoveredAuthority, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
14848
+ }
14720
14849
  /**
14721
14850
  *
14722
14851
  * @param validRequest
@@ -14792,7 +14921,6 @@ class PopupClient extends StandardInteractionClient {
14792
14921
  e.setCorrelationId(this.correlationId);
14793
14922
  serverTelemetryManager.cacheFailedRequest(e);
14794
14923
  }
14795
- this.browserStorage.setInteractionInProgress(false);
14796
14924
  this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, exports.InteractionType.Popup, null, e);
14797
14925
  this.eventHandler.emitEvent(EventType.LOGOUT_END, exports.InteractionType.Popup);
14798
14926
  throw e;
@@ -14904,7 +15032,6 @@ class PopupClient extends StandardInteractionClient {
14904
15032
  }
14905
15033
  catch (e) {
14906
15034
  this.logger.error("error opening popup " + e.message);
14907
- this.browserStorage.setInteractionInProgress(false);
14908
15035
  throw createBrowserAuthError(popupWindowError);
14909
15036
  }
14910
15037
  }
@@ -14962,7 +15089,6 @@ class PopupClient extends StandardInteractionClient {
14962
15089
  * Event callback to unload main window.
14963
15090
  */
14964
15091
  unloadWindow(e) {
14965
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Popup);
14966
15092
  if (this.currentWindow) {
14967
15093
  this.currentWindow.close();
14968
15094
  }
@@ -14978,8 +15104,6 @@ class PopupClient extends StandardInteractionClient {
14978
15104
  popupWindow.close();
14979
15105
  // Remove window unload function
14980
15106
  popupWindowParent.removeEventListener("beforeunload", this.unloadWindow);
14981
- // Interaction is completed - remove interaction status.
14982
- this.browserStorage.setInteractionInProgress(false);
14983
15107
  }
14984
15108
  /**
14985
15109
  * Generates the name for the popup based on the client id and request
@@ -15000,141 +15124,6 @@ class PopupClient extends StandardInteractionClient {
15000
15124
  }
15001
15125
  }
15002
15126
 
15003
- /*
15004
- * Copyright (c) Microsoft Corporation. All rights reserved.
15005
- * Licensed under the MIT License.
15006
- */
15007
- class RedirectHandler {
15008
- constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
15009
- this.authModule = authCodeModule;
15010
- this.browserStorage = storageImpl;
15011
- this.authCodeRequest = authCodeRequest;
15012
- this.logger = logger;
15013
- this.performanceClient = performanceClient;
15014
- }
15015
- /**
15016
- * Redirects window to given URL.
15017
- * @param urlNavigate
15018
- */
15019
- async initiateAuthRequest(requestUrl, params) {
15020
- this.logger.verbose("RedirectHandler.initiateAuthRequest called");
15021
- // Navigate if valid URL
15022
- if (requestUrl) {
15023
- // Cache start page, returns to this page after redirectUri if navigateToLoginRequestUrl is true
15024
- if (params.redirectStartPage) {
15025
- this.logger.verbose("RedirectHandler.initiateAuthRequest: redirectStartPage set, caching start page");
15026
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.ORIGIN_URI, params.redirectStartPage, true);
15027
- }
15028
- // Set interaction status in the library.
15029
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.CORRELATION_ID, this.authCodeRequest.correlationId, true);
15030
- this.browserStorage.cacheCodeRequest(this.authCodeRequest);
15031
- this.logger.infoPii(`RedirectHandler.initiateAuthRequest: Navigate to: ${requestUrl}`);
15032
- const navigationOptions = {
15033
- apiId: ApiId.acquireTokenRedirect,
15034
- timeout: params.redirectTimeout,
15035
- noHistory: false,
15036
- };
15037
- // If onRedirectNavigate is implemented, invoke it and provide requestUrl
15038
- if (typeof params.onRedirectNavigate === "function") {
15039
- this.logger.verbose("RedirectHandler.initiateAuthRequest: Invoking onRedirectNavigate callback");
15040
- const navigate = params.onRedirectNavigate(requestUrl);
15041
- // Returning false from onRedirectNavigate will stop navigation
15042
- if (navigate !== false) {
15043
- this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate did not return false, navigating");
15044
- await params.navigationClient.navigateExternal(requestUrl, navigationOptions);
15045
- return;
15046
- }
15047
- else {
15048
- this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate returned false, stopping navigation");
15049
- return;
15050
- }
15051
- }
15052
- else {
15053
- // Navigate window to request URL
15054
- this.logger.verbose("RedirectHandler.initiateAuthRequest: Navigating window to navigate url");
15055
- await params.navigationClient.navigateExternal(requestUrl, navigationOptions);
15056
- return;
15057
- }
15058
- }
15059
- else {
15060
- // Throw error if request URL is empty.
15061
- this.logger.info("RedirectHandler.initiateAuthRequest: Navigate url is empty");
15062
- throw createBrowserAuthError(emptyNavigateUri);
15063
- }
15064
- }
15065
- /**
15066
- * Handle authorization code response in the window.
15067
- * @param hash
15068
- */
15069
- async handleCodeResponse(response, state) {
15070
- this.logger.verbose("RedirectHandler.handleCodeResponse called");
15071
- // Interaction is completed - remove interaction status.
15072
- this.browserStorage.setInteractionInProgress(false);
15073
- // Handle code response.
15074
- const stateKey = this.browserStorage.generateStateKey(state);
15075
- const requestState = this.browserStorage.getTemporaryCache(stateKey);
15076
- if (!requestState) {
15077
- throw createClientAuthError(stateNotFound, "Cached State");
15078
- }
15079
- let authCodeResponse;
15080
- try {
15081
- authCodeResponse = getAuthorizationCodePayload(response, requestState);
15082
- }
15083
- catch (e) {
15084
- if (e instanceof ServerError &&
15085
- e.subError === userCancelled) {
15086
- // Translate server error caused by user closing native prompt to corresponding first class MSAL error
15087
- throw createBrowserAuthError(userCancelled);
15088
- }
15089
- else {
15090
- throw e;
15091
- }
15092
- }
15093
- // Get cached items
15094
- const nonceKey = this.browserStorage.generateNonceKey(requestState);
15095
- const cachedNonce = this.browserStorage.getTemporaryCache(nonceKey);
15096
- // Assign code to request
15097
- this.authCodeRequest.code = authCodeResponse.code;
15098
- // Check for new cloud instance
15099
- if (authCodeResponse.cloud_instance_host_name) {
15100
- await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, this.authCodeRequest.correlationId)(authCodeResponse.cloud_instance_host_name, this.authCodeRequest.correlationId);
15101
- }
15102
- authCodeResponse.nonce = cachedNonce || undefined;
15103
- authCodeResponse.state = requestState;
15104
- // Add CCS parameters if available
15105
- if (authCodeResponse.client_info) {
15106
- this.authCodeRequest.clientInfo = authCodeResponse.client_info;
15107
- }
15108
- else {
15109
- const cachedCcsCred = this.checkCcsCredentials();
15110
- if (cachedCcsCred) {
15111
- this.authCodeRequest.ccsCredential = cachedCcsCred;
15112
- }
15113
- }
15114
- // Acquire token with retrieved code.
15115
- const tokenResponse = (await this.authModule.acquireToken(this.authCodeRequest, authCodeResponse));
15116
- this.browserStorage.cleanRequestByState(state);
15117
- return tokenResponse;
15118
- }
15119
- /**
15120
- * Looks up ccs creds in the cache
15121
- */
15122
- checkCcsCredentials() {
15123
- // Look up ccs credential in temp cache
15124
- const cachedCcsCred = this.browserStorage.getTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, true);
15125
- if (cachedCcsCred) {
15126
- try {
15127
- return JSON.parse(cachedCcsCred);
15128
- }
15129
- catch (e) {
15130
- this.authModule.logger.error("Cache credential could not be parsed");
15131
- this.authModule.logger.errorPii(`Cache credential could not be parsed: ${cachedCcsCred}`);
15132
- }
15133
- }
15134
- return null;
15135
- }
15136
- }
15137
-
15138
15127
  /*
15139
15128
  * Copyright (c) Microsoft Corporation. All rights reserved.
15140
15129
  * Licensed under the MIT License.
@@ -15162,62 +15151,96 @@ class RedirectClient extends StandardInteractionClient {
15162
15151
  */
15163
15152
  async acquireToken(request) {
15164
15153
  const validRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(request, exports.InteractionType.Redirect);
15165
- const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId);
15166
- validRequest.codeChallenge = pkceCodes.challenge;
15167
- this.browserStorage.updateCacheEntries(validRequest.state, validRequest.nonce, validRequest.authority, validRequest.loginHint || "", validRequest.account || null);
15168
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenRedirect);
15154
+ validRequest.platformBroker =
15155
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
15169
15156
  const handleBackButton = (event) => {
15170
15157
  // Clear temporary cache if the back button is clicked during the redirect flow.
15171
15158
  if (event.persisted) {
15172
15159
  this.logger.verbose("Page was restored from back/forward cache. Clearing temporary cache.");
15173
- this.browserStorage.cleanRequestByState(validRequest.state);
15160
+ this.browserStorage.resetRequestCache();
15174
15161
  this.eventHandler.emitEvent(EventType.RESTORE_FROM_BFCACHE, exports.InteractionType.Redirect);
15175
15162
  }
15176
15163
  };
15164
+ const redirectStartPage = this.getRedirectStartPage(request.redirectStartPage);
15165
+ this.logger.verbosePii(`Redirect start page: ${redirectStartPage}`);
15166
+ // Cache start page, returns to this page after redirectUri if navigateToLoginRequestUrl is true
15167
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.ORIGIN_URI, redirectStartPage, true);
15168
+ // Clear temporary cache if the back button is clicked during the redirect flow.
15169
+ window.addEventListener("pageshow", handleBackButton);
15170
+ try {
15171
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15172
+ await this.executeEarFlow(validRequest);
15173
+ }
15174
+ else {
15175
+ await this.executeCodeFlow(validRequest, request.onRedirectNavigate);
15176
+ }
15177
+ }
15178
+ catch (e) {
15179
+ if (e instanceof AuthError) {
15180
+ e.setCorrelationId(this.correlationId);
15181
+ }
15182
+ window.removeEventListener("pageshow", handleBackButton);
15183
+ throw e;
15184
+ }
15185
+ }
15186
+ /**
15187
+ * Executes auth code + PKCE flow
15188
+ * @param request
15189
+ * @returns
15190
+ */
15191
+ async executeCodeFlow(request, onRedirectNavigate) {
15192
+ const correlationId = request.correlationId;
15193
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenRedirect);
15194
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15195
+ const redirectRequest = {
15196
+ ...request,
15197
+ codeChallenge: pkceCodes.challenge,
15198
+ };
15199
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest, pkceCodes.verifier);
15177
15200
  try {
15178
15201
  // Initialize the client
15179
15202
  const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
15180
15203
  serverTelemetryManager,
15181
- requestAuthority: validRequest.authority,
15182
- requestAzureCloudOptions: validRequest.azureCloudOptions,
15183
- requestExtraQueryParameters: validRequest.extraQueryParameters,
15184
- account: validRequest.account,
15204
+ requestAuthority: redirectRequest.authority,
15205
+ requestAzureCloudOptions: redirectRequest.azureCloudOptions,
15206
+ requestExtraQueryParameters: redirectRequest.extraQueryParameters,
15207
+ account: redirectRequest.account,
15185
15208
  });
15186
- const authCodeRequest = {
15187
- ...validRequest,
15188
- code: "",
15189
- codeVerifier: pkceCodes.verifier,
15190
- };
15191
- // Create redirect interaction handler.
15192
- const interactionHandler = new RedirectHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
15193
15209
  // Create acquire token url.
15194
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, validRequest.correlationId)(this.config, authClient.authority, {
15195
- ...validRequest,
15196
- platformBroker: NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme),
15197
- }, this.logger, this.performanceClient);
15198
- const redirectStartPage = this.getRedirectStartPage(request.redirectStartPage);
15199
- this.logger.verbosePii(`Redirect start page: ${redirectStartPage}`);
15200
- // Clear temporary cache if the back button is clicked during the redirect flow.
15201
- window.addEventListener("pageshow", handleBackButton);
15210
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, request.correlationId)(this.config, authClient.authority, redirectRequest, this.logger, this.performanceClient);
15202
15211
  // Show the UI once the url has been created. Response will come back in the hash, which will be handled in the handleRedirectCallback function.
15203
- return await interactionHandler.initiateAuthRequest(navigateUrl, {
15204
- navigationClient: this.navigationClient,
15205
- redirectTimeout: this.config.system.redirectNavigationTimeout,
15206
- redirectStartPage: redirectStartPage,
15207
- onRedirectNavigate: request.onRedirectNavigate ||
15208
- this.config.auth.onRedirectNavigate,
15209
- });
15212
+ return await this.initiateAuthRequest(navigateUrl, onRedirectNavigate);
15210
15213
  }
15211
15214
  catch (e) {
15212
15215
  if (e instanceof AuthError) {
15213
15216
  e.setCorrelationId(this.correlationId);
15214
15217
  serverTelemetryManager.cacheFailedRequest(e);
15215
15218
  }
15216
- window.removeEventListener("pageshow", handleBackButton);
15217
- this.browserStorage.cleanRequestByState(validRequest.state);
15218
15219
  throw e;
15219
15220
  }
15220
15221
  }
15222
+ /**
15223
+ * Executes EAR flow
15224
+ * @param request
15225
+ */
15226
+ async executeEarFlow(request) {
15227
+ const correlationId = request.correlationId;
15228
+ // Get the frame handle for the silent request
15229
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15230
+ requestAuthority: request.authority,
15231
+ requestAzureCloudOptions: request.azureCloudOptions,
15232
+ requestExtraQueryParameters: request.extraQueryParameters,
15233
+ account: request.account,
15234
+ });
15235
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15236
+ const redirectRequest = {
15237
+ ...request,
15238
+ earJwk: earJwk,
15239
+ };
15240
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest);
15241
+ const form = await getEARForm(document, this.config, discoveredAuthority, redirectRequest, this.logger, this.performanceClient);
15242
+ form.submit();
15243
+ }
15221
15244
  /**
15222
15245
  * Checks if navigateToLoginRequestUrl is set, and:
15223
15246
  * - if true, performs logic to cache and navigate
@@ -15225,18 +15248,14 @@ class RedirectClient extends StandardInteractionClient {
15225
15248
  * @param hash {string} url hash
15226
15249
  * @param parentMeasurement {InProgressPerformanceEvent} parent measurement
15227
15250
  */
15228
- async handleRedirectPromise(hash = "", parentMeasurement) {
15251
+ async handleRedirectPromise(hash = "", request, pkceVerifier, parentMeasurement) {
15229
15252
  const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.handleRedirectPromise);
15230
15253
  try {
15231
- if (!this.browserStorage.isInteractionInProgress(true)) {
15232
- this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
15233
- return null;
15234
- }
15235
15254
  const [serverParams, responseString] = this.getRedirectResponse(hash || "");
15236
15255
  if (!serverParams) {
15237
15256
  // Not a recognized server response hash or hash not associated with a redirect request
15238
15257
  this.logger.info("handleRedirectPromise did not detect a response as a result of a redirect. Cleaning temporary cache.");
15239
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Redirect);
15258
+ this.browserStorage.resetRequestCache();
15240
15259
  // Do not instrument "no_server_response" if user clicked back button
15241
15260
  if (getNavigationType() !== "back_forward") {
15242
15261
  parentMeasurement.event.errorCode = "no_server_response";
@@ -15258,12 +15277,12 @@ class RedirectClient extends StandardInteractionClient {
15258
15277
  // Replace current hash with non-msal hash, if present
15259
15278
  replaceHash(loginRequestUrl);
15260
15279
  }
15261
- const handleHashResult = await this.handleResponse(serverParams, serverTelemetryManager);
15280
+ const handleHashResult = await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15262
15281
  return handleHashResult;
15263
15282
  }
15264
15283
  else if (!this.config.auth.navigateToLoginRequestUrl) {
15265
15284
  this.logger.verbose("NavigateToLoginRequestUrl set to false, handling response");
15266
- return await this.handleResponse(serverParams, serverTelemetryManager);
15285
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15267
15286
  }
15268
15287
  else if (!isInIframe() ||
15269
15288
  this.config.system.allowRedirectInIframe) {
@@ -15299,7 +15318,7 @@ class RedirectClient extends StandardInteractionClient {
15299
15318
  }
15300
15319
  // If navigateInternal implementation returns false, handle the hash now
15301
15320
  if (!processHashOnRedirect) {
15302
- return await this.handleResponse(serverParams, serverTelemetryManager);
15321
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15303
15322
  }
15304
15323
  }
15305
15324
  return null;
@@ -15309,7 +15328,6 @@ class RedirectClient extends StandardInteractionClient {
15309
15328
  e.setCorrelationId(this.correlationId);
15310
15329
  serverTelemetryManager.cacheFailedRequest(e);
15311
15330
  }
15312
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Redirect);
15313
15331
  throw e;
15314
15332
  }
15315
15333
  }
@@ -15362,39 +15380,67 @@ class RedirectClient extends StandardInteractionClient {
15362
15380
  * @param hash
15363
15381
  * @param state
15364
15382
  */
15365
- async handleResponse(serverParams, serverTelemetryManager) {
15383
+ async handleResponse(serverParams, request, codeVerifier, serverTelemetryManager) {
15366
15384
  const state = serverParams.state;
15367
15385
  if (!state) {
15368
15386
  throw createBrowserAuthError(noStateInHash);
15369
15387
  }
15370
- const cachedRequest = this.browserStorage.getCachedRequest(state);
15371
- this.logger.verbose("handleResponse called, retrieved cached request");
15372
- if (serverParams.accountId) {
15373
- this.logger.verbose("Account id found in hash, calling WAM for token");
15374
- if (!this.nativeMessageHandler) {
15375
- throw createBrowserAuthError(nativeConnectionNotEstablished);
15376
- }
15377
- const nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.acquireTokenPopup, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.nativeStorage, cachedRequest.correlationId);
15378
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, state);
15379
- return nativeInteractionClient
15380
- .acquireToken({
15381
- ...cachedRequest,
15382
- state: userRequestState,
15383
- prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
15384
- })
15385
- .finally(() => {
15386
- this.browserStorage.cleanRequestByState(state);
15388
+ if (serverParams.ear_jwe) {
15389
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, request.correlationId)({
15390
+ requestAuthority: request.authority,
15391
+ requestAzureCloudOptions: request.azureCloudOptions,
15392
+ requestExtraQueryParameters: request.extraQueryParameters,
15393
+ account: request.account,
15387
15394
  });
15395
+ return invokeAsync(handleResponseEAR, PerformanceEvents.HandleResponseEar, this.logger, this.performanceClient, request.correlationId)(request, serverParams, ApiId.acquireTokenRedirect, this.config, discoveredAuthority, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
15396
+ }
15397
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({ serverTelemetryManager, requestAuthority: request.authority });
15398
+ return invokeAsync(handleResponseCode, PerformanceEvents.HandleResponseCode, this.logger, this.performanceClient, request.correlationId)(request, serverParams, codeVerifier, ApiId.acquireTokenRedirect, this.config, authClient, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
15399
+ }
15400
+ /**
15401
+ * Redirects window to given URL.
15402
+ * @param urlNavigate
15403
+ * @param onRedirectNavigateRequest - onRedirectNavigate callback provided on the request
15404
+ */
15405
+ async initiateAuthRequest(requestUrl, onRedirectNavigateRequest) {
15406
+ this.logger.verbose("RedirectHandler.initiateAuthRequest called");
15407
+ // Navigate if valid URL
15408
+ if (requestUrl) {
15409
+ this.logger.infoPii(`RedirectHandler.initiateAuthRequest: Navigate to: ${requestUrl}`);
15410
+ const navigationOptions = {
15411
+ apiId: ApiId.acquireTokenRedirect,
15412
+ timeout: this.config.system.redirectNavigationTimeout,
15413
+ noHistory: false,
15414
+ };
15415
+ const onRedirectNavigate = onRedirectNavigateRequest ||
15416
+ this.config.auth.onRedirectNavigate;
15417
+ // If onRedirectNavigate is implemented, invoke it and provide requestUrl
15418
+ if (typeof onRedirectNavigate === "function") {
15419
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Invoking onRedirectNavigate callback");
15420
+ const navigate = onRedirectNavigate(requestUrl);
15421
+ // Returning false from onRedirectNavigate will stop navigation
15422
+ if (navigate !== false) {
15423
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate did not return false, navigating");
15424
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15425
+ return;
15426
+ }
15427
+ else {
15428
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate returned false, stopping navigation");
15429
+ return;
15430
+ }
15431
+ }
15432
+ else {
15433
+ // Navigate window to request URL
15434
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Navigating window to navigate url");
15435
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15436
+ return;
15437
+ }
15388
15438
  }
15389
- // Hash contains known properties - handle and return in callback
15390
- const currentAuthority = this.browserStorage.getCachedAuthority(state);
15391
- if (!currentAuthority) {
15392
- throw createBrowserAuthError(noCachedAuthorityError);
15439
+ else {
15440
+ // Throw error if request URL is empty.
15441
+ this.logger.info("RedirectHandler.initiateAuthRequest: Navigate url is empty");
15442
+ throw createBrowserAuthError(emptyNavigateUri);
15393
15443
  }
15394
- const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({ serverTelemetryManager, requestAuthority: currentAuthority });
15395
- ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, cachedRequest);
15396
- const interactionHandler = new RedirectHandler(authClient, this.browserStorage, cachedRequest, this.logger, this.performanceClient);
15397
- return interactionHandler.handleCodeResponse(serverParams, state);
15398
15444
  }
15399
15445
  /**
15400
15446
  * Use to log out the current user, and redirect the user to the postLogoutRedirectUri.
@@ -15493,7 +15539,7 @@ class RedirectClient extends StandardInteractionClient {
15493
15539
  * @param urlNavigate
15494
15540
  * @param userRequestScopes
15495
15541
  */
15496
- async function initiateAuthRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15542
+ async function initiateCodeRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15497
15543
  performanceClient.addQueueMeasurement(PerformanceEvents.SilentHandlerInitiateAuthRequest, correlationId);
15498
15544
  if (!requestUrl) {
15499
15545
  // Throw error if request URL is empty.
@@ -15505,6 +15551,15 @@ async function initiateAuthRequest(requestUrl, performanceClient, logger, correl
15505
15551
  }
15506
15552
  return invoke(loadFrameSync, PerformanceEvents.SilentHandlerLoadFrameSync, logger, performanceClient, correlationId)(requestUrl);
15507
15553
  }
15554
+ async function initiateEarRequest(config, authority, request, logger, performanceClient) {
15555
+ const frame = createHiddenIframe();
15556
+ if (!frame.contentDocument) {
15557
+ throw "No document associated with iframe!";
15558
+ }
15559
+ const form = await getEARForm(frame.contentDocument, config, authority, request, logger, performanceClient);
15560
+ form.submit();
15561
+ return frame;
15562
+ }
15508
15563
  /**
15509
15564
  * Monitors an iframe content window until it loads a url with a known hash, or hits a specified timeout.
15510
15565
  * @param iframe
@@ -15655,19 +15710,34 @@ class SilentIframeClient extends StandardInteractionClient {
15655
15710
  }
15656
15711
  // Create silent request
15657
15712
  const silentRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, request.correlationId)(inputRequest, exports.InteractionType.Silent);
15713
+ silentRequest.platformBroker =
15714
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, silentRequest.authenticationScheme);
15658
15715
  preconnect(silentRequest.authority);
15659
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15716
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15717
+ return this.executeEarFlow(silentRequest);
15718
+ }
15719
+ else {
15720
+ return this.executeCodeFlow(silentRequest);
15721
+ }
15722
+ }
15723
+ /**
15724
+ * Executes auth code + PKCE flow
15725
+ * @param request
15726
+ * @returns
15727
+ */
15728
+ async executeCodeFlow(request) {
15660
15729
  let authClient;
15730
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15661
15731
  try {
15662
15732
  // Initialize the client
15663
15733
  authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, request.correlationId)({
15664
15734
  serverTelemetryManager,
15665
- requestAuthority: silentRequest.authority,
15666
- requestAzureCloudOptions: silentRequest.azureCloudOptions,
15667
- requestExtraQueryParameters: silentRequest.extraQueryParameters,
15668
- account: silentRequest.account,
15735
+ requestAuthority: request.authority,
15736
+ requestAzureCloudOptions: request.azureCloudOptions,
15737
+ requestExtraQueryParameters: request.extraQueryParameters,
15738
+ account: request.account,
15669
15739
  });
15670
- return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, silentRequest);
15740
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, request);
15671
15741
  }
15672
15742
  catch (e) {
15673
15743
  if (e instanceof AuthError) {
@@ -15682,10 +15752,33 @@ class SilentIframeClient extends StandardInteractionClient {
15682
15752
  this.performanceClient.addFields({
15683
15753
  retryError: e.errorCode,
15684
15754
  }, this.correlationId);
15685
- const retrySilentRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, request.correlationId)(inputRequest, exports.InteractionType.Silent);
15686
- return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, retrySilentRequest);
15755
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, request);
15687
15756
  }
15688
15757
  }
15758
+ /**
15759
+ * Executes EAR flow
15760
+ * @param request
15761
+ */
15762
+ async executeEarFlow(request) {
15763
+ const correlationId = request.correlationId;
15764
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15765
+ requestAuthority: request.authority,
15766
+ requestAzureCloudOptions: request.azureCloudOptions,
15767
+ requestExtraQueryParameters: request.extraQueryParameters,
15768
+ account: request.account,
15769
+ });
15770
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15771
+ const silentRequest = {
15772
+ ...request,
15773
+ earJwk: earJwk,
15774
+ };
15775
+ const msalFrame = await invokeAsync(initiateEarRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(this.config, discoveredAuthority, silentRequest, this.logger, this.performanceClient);
15776
+ const responseType = this.config.auth.OIDCOptions.serverResponseType;
15777
+ // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
15778
+ const responseString = await invokeAsync(monitorIframeForHash, PerformanceEvents.SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(msalFrame, this.config.system.iframeHashTimeout, this.config.system.pollIntervalMilliseconds, this.performanceClient, this.logger, correlationId, responseType);
15779
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15780
+ return invokeAsync(handleResponseEAR, PerformanceEvents.HandleResponseEar, this.logger, this.performanceClient, correlationId)(silentRequest, serverParams, this.apiId, this.config, discoveredAuthority, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
15781
+ }
15689
15782
  /**
15690
15783
  * Currently Unsupported
15691
15784
  */
@@ -15702,44 +15795,20 @@ class SilentIframeClient extends StandardInteractionClient {
15702
15795
  async silentTokenHelper(authClient, request) {
15703
15796
  const correlationId = request.correlationId;
15704
15797
  this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentIframeClientTokenHelper, correlationId);
15705
- const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId);
15798
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15706
15799
  const silentRequest = {
15707
15800
  ...request,
15708
15801
  codeChallenge: pkceCodes.challenge,
15709
15802
  };
15710
15803
  // Create authorize request url
15711
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, {
15712
- ...silentRequest,
15713
- platformBroker: NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, silentRequest.authenticationScheme),
15714
- }, this.logger, this.performanceClient);
15804
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, silentRequest, this.logger, this.performanceClient);
15715
15805
  // Get the frame handle for the silent request
15716
- const msalFrame = await invokeAsync(initiateAuthRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId, this.config.system.navigateFrameWait);
15806
+ const msalFrame = await invokeAsync(initiateCodeRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId, this.config.system.navigateFrameWait);
15717
15807
  const responseType = this.config.auth.OIDCOptions.serverResponseType;
15718
15808
  // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
15719
15809
  const responseString = await invokeAsync(monitorIframeForHash, PerformanceEvents.SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(msalFrame, this.config.system.iframeHashTimeout, this.config.system.pollIntervalMilliseconds, this.performanceClient, this.logger, correlationId, responseType);
15720
- const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, responseType, this.logger);
15721
- if (serverParams.accountId) {
15722
- this.logger.verbose("Account id found in hash, calling WAM for token");
15723
- if (!this.nativeMessageHandler) {
15724
- throw createBrowserAuthError(nativeConnectionNotEstablished);
15725
- }
15726
- const nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, this.apiId, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.browserStorage, correlationId);
15727
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, silentRequest.state);
15728
- return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), PerformanceEvents.NativeInteractionClientAcquireToken, this.logger, this.performanceClient, correlationId)({
15729
- ...silentRequest,
15730
- state: userRequestState,
15731
- prompt: silentRequest.prompt || PromptValue.NONE,
15732
- });
15733
- }
15734
- const authCodeRequest = {
15735
- ...silentRequest,
15736
- code: serverParams.code || "",
15737
- codeVerifier: pkceCodes.verifier,
15738
- };
15739
- // Create silent handler
15740
- const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
15741
- // Handle response from hash string
15742
- return invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), PerformanceEvents.HandleCodeResponse, this.logger, this.performanceClient, correlationId)(serverParams, silentRequest);
15810
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15811
+ return invokeAsync(handleResponseCode, PerformanceEvents.HandleResponseCode, this.logger, this.performanceClient, correlationId)(request, serverParams, pkceCodes.verifier, this.apiId, this.config, authClient, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.nativeMessageHandler);
15743
15812
  }
15744
15813
  }
15745
15814
 
@@ -16268,31 +16337,38 @@ class StandardController {
16268
16337
  * @returns
16269
16338
  */
16270
16339
  async handleRedirectPromiseInternal(hash) {
16340
+ if (!this.browserStorage.isInteractionInProgress(true)) {
16341
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
16342
+ return null;
16343
+ }
16271
16344
  const loggedInAccounts = this.getAllAccounts();
16272
- const request = this.browserStorage.getCachedNativeRequest();
16273
- const useNative = request &&
16345
+ const platformBrokerRequest = this.browserStorage.getCachedNativeRequest();
16346
+ const useNative = platformBrokerRequest &&
16274
16347
  NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeExtensionProvider) &&
16275
16348
  this.nativeExtensionProvider &&
16276
16349
  !hash;
16277
- const correlationId = useNative
16278
- ? request?.correlationId
16279
- : this.browserStorage.getTemporaryCache(TemporaryCacheKeys.CORRELATION_ID, true) || "";
16280
- const rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, correlationId);
16350
+ let rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, platformBrokerRequest?.correlationId || "");
16281
16351
  this.eventHandler.emitEvent(EventType.HANDLE_REDIRECT_START, exports.InteractionType.Redirect);
16282
16352
  let redirectResponse;
16283
16353
  if (useNative && this.nativeExtensionProvider) {
16284
16354
  this.logger.trace("handleRedirectPromise - acquiring token from native platform");
16285
- const nativeClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.handleRedirectPromise, this.performanceClient, this.nativeExtensionProvider, request.accountId, this.nativeInternalStorage, request.correlationId);
16355
+ const nativeClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.handleRedirectPromise, this.performanceClient, this.nativeExtensionProvider, platformBrokerRequest.accountId, this.nativeInternalStorage, platformBrokerRequest.correlationId);
16286
16356
  redirectResponse = invokeAsync(nativeClient.handleRedirectPromise.bind(nativeClient), PerformanceEvents.HandleNativeRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(this.performanceClient, rootMeasurement.event.correlationId);
16287
16357
  }
16288
16358
  else {
16359
+ const [standardRequest, codeVerifier] = this.browserStorage.getCachedRequest();
16360
+ const correlationId = standardRequest.correlationId;
16361
+ // Reset rootMeasurement now that we have correlationId
16362
+ rootMeasurement.discard();
16363
+ rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, correlationId);
16289
16364
  this.logger.trace("handleRedirectPromise - acquiring token from web flow");
16290
16365
  const redirectClient = this.createRedirectClient(correlationId);
16291
- redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, rootMeasurement);
16366
+ redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, standardRequest, codeVerifier, rootMeasurement);
16292
16367
  }
16293
16368
  return redirectResponse
16294
16369
  .then((result) => {
16295
16370
  if (result) {
16371
+ this.browserStorage.resetRequestCache();
16296
16372
  // Emit login event if number of accounts change
16297
16373
  const isLoggingIn = loggedInAccounts.length < this.getAllAccounts().length;
16298
16374
  if (isLoggingIn) {
@@ -16324,6 +16400,7 @@ class StandardController {
16324
16400
  return result;
16325
16401
  })
16326
16402
  .catch((e) => {
16403
+ this.browserStorage.resetRequestCache();
16327
16404
  const eventError = e;
16328
16405
  // Emit login event if there is an account
16329
16406
  if (loggedInAccounts.length > 0) {
@@ -16417,7 +16494,6 @@ class StandardController {
16417
16494
  const redirectClient = this.createRedirectClient(correlationId);
16418
16495
  return redirectClient.acquireToken(request);
16419
16496
  }
16420
- this.browserStorage.setInteractionInProgress(false);
16421
16497
  throw e;
16422
16498
  });
16423
16499
  }
@@ -16428,6 +16504,7 @@ class StandardController {
16428
16504
  return await result;
16429
16505
  }
16430
16506
  catch (e) {
16507
+ this.browserStorage.resetRequestCache();
16431
16508
  atrMeasurement.end({ success: false }, e);
16432
16509
  if (isLoggedIn) {
16433
16510
  this.eventHandler.emitEvent(EventType.ACQUIRE_TOKEN_FAILURE, exports.InteractionType.Redirect, null, e);
@@ -16479,7 +16556,6 @@ class StandardController {
16479
16556
  correlationId,
16480
16557
  }, ApiId.acquireTokenPopup)
16481
16558
  .then((response) => {
16482
- this.browserStorage.setInteractionInProgress(false);
16483
16559
  atPopupMeasurement.end({
16484
16560
  success: true,
16485
16561
  isNativeBroker: true,
@@ -16499,7 +16575,6 @@ class StandardController {
16499
16575
  const popupClient = this.createPopupClient(correlationId);
16500
16576
  return popupClient.acquireToken(request, pkce);
16501
16577
  }
16502
- this.browserStorage.setInteractionInProgress(false);
16503
16578
  throw e;
16504
16579
  });
16505
16580
  }
@@ -16540,8 +16615,12 @@ class StandardController {
16540
16615
  // Since this function is syncronous we need to reject
16541
16616
  return Promise.reject(e);
16542
16617
  })
16543
- .finally(() => this.config.system.asyncPopups &&
16544
- this.preGeneratePkceCodes(correlationId));
16618
+ .finally(async () => {
16619
+ this.browserStorage.setInteractionInProgress(false);
16620
+ if (this.config.system.asyncPopups) {
16621
+ await this.preGeneratePkceCodes(correlationId);
16622
+ }
16623
+ });
16545
16624
  }
16546
16625
  trackPageVisibilityWithMeasurement() {
16547
16626
  const measurement = this.ssoSilentMeasurement ||
@@ -16844,7 +16923,9 @@ class StandardController {
16844
16923
  preflightCheck$1(this.initialized);
16845
16924
  this.browserStorage.setInteractionInProgress(true);
16846
16925
  const popupClient = this.createPopupClient(correlationId);
16847
- return popupClient.logout(logoutRequest);
16926
+ return popupClient.logout(logoutRequest).finally(() => {
16927
+ this.browserStorage.setInteractionInProgress(false);
16928
+ });
16848
16929
  }
16849
16930
  catch (e) {
16850
16931
  // Since this function is syncronous we need to reject