@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,4 +1,4 @@
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
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
@@ -6,7 +6,7 @@
6
6
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.msal = {}));
7
7
  })(this, (function (exports) { 'use strict';
8
8
 
9
- /*! @azure/msal-common v15.3.0 2025-03-20 */
9
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
10
10
  /*
11
11
  * Copyright (c) Microsoft Corporation. All rights reserved.
12
12
  * Licensed under the MIT License.
@@ -41,11 +41,8 @@
41
41
  PROFILE_SCOPE: "profile",
42
42
  OFFLINE_ACCESS_SCOPE: "offline_access",
43
43
  EMAIL_SCOPE: "email",
44
- // Default response type for authorization code flow
45
- CODE_RESPONSE_TYPE: "code",
46
44
  CODE_GRANT_TYPE: "authorization_code",
47
45
  RT_GRANT_TYPE: "refresh_token",
48
- FRAGMENT_RESPONSE_MODE: "fragment",
49
46
  S256_CODE_CHALLENGE_METHOD: "S256",
50
47
  URL_FORM_CONTENT_TYPE: "application/x-www-form-urlencoded;charset=utf-8",
51
48
  AUTHORIZATION_PENDING: "authorization_pending",
@@ -65,8 +62,6 @@
65
62
  "login.microsoft.com",
66
63
  "sts.windows.net",
67
64
  ],
68
- TOKEN_RESPONSE_TYPE: "token",
69
- ID_TOKEN_RESPONSE_TYPE: "id_token",
70
65
  SHR_NONCE_VALIDITY: 240,
71
66
  INVALID_INSTANCE: "invalid_instance",
72
67
  };
@@ -150,8 +145,17 @@
150
145
  PLAIN: "plain",
151
146
  S256: "S256",
152
147
  };
148
+ /**
149
+ * Allowed values for response_type
150
+ */
151
+ const OAuthResponseType = {
152
+ CODE: "code",
153
+ IDTOKEN_TOKEN: "id_token token",
154
+ IDTOKEN_TOKEN_REFRESHTOKEN: "id_token token refresh_token",
155
+ };
153
156
  /**
154
157
  * allowed values for server response type
158
+ * @deprecated Use ResponseMode instead
155
159
  */
156
160
  const ServerResponseType = {
157
161
  QUERY: "query",
@@ -161,7 +165,8 @@
161
165
  * allowed values for response_mode
162
166
  */
163
167
  const ResponseMode = {
164
- ...ServerResponseType,
168
+ QUERY: "query",
169
+ FRAGMENT: "fragment",
165
170
  FORM_POST: "form_post",
166
171
  };
167
172
  /**
@@ -303,7 +308,7 @@
303
308
  // Token renewal offset default in seconds
304
309
  const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
305
310
 
306
- /*! @azure/msal-common v15.3.0 2025-03-20 */
311
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
307
312
  /*
308
313
  * Copyright (c) Microsoft Corporation. All rights reserved.
309
314
  * Licensed under the MIT License.
@@ -320,7 +325,7 @@
320
325
  unexpectedError: unexpectedError
321
326
  });
322
327
 
323
- /*! @azure/msal-common v15.3.0 2025-03-20 */
328
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
324
329
 
325
330
  /*
326
331
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -369,7 +374,7 @@
369
374
  : AuthErrorMessages[code]);
370
375
  }
371
376
 
372
- /*! @azure/msal-common v15.3.0 2025-03-20 */
377
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
373
378
  /*
374
379
  * Copyright (c) Microsoft Corporation. All rights reserved.
375
380
  * Licensed under the MIT License.
@@ -467,7 +472,7 @@
467
472
  userTimeoutReached: userTimeoutReached
468
473
  });
469
474
 
470
- /*! @azure/msal-common v15.3.0 2025-03-20 */
475
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
471
476
 
472
477
  /*
473
478
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -719,7 +724,7 @@
719
724
  return new ClientAuthError(errorCode, additionalMessage);
720
725
  }
721
726
 
722
- /*! @azure/msal-common v15.3.0 2025-03-20 */
727
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
723
728
 
724
729
  /*
725
730
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -758,7 +763,7 @@
758
763
  },
759
764
  };
760
765
 
761
- /*! @azure/msal-common v15.3.0 2025-03-20 */
766
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
762
767
 
763
768
  /*
764
769
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -949,12 +954,12 @@
949
954
  }
950
955
  }
951
956
 
952
- /*! @azure/msal-common v15.3.0 2025-03-20 */
957
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
953
958
  /* eslint-disable header/header */
954
959
  const name$1 = "@azure/msal-common";
955
- const version$1 = "15.3.0";
960
+ const version$1 = "15.4.0";
956
961
 
957
- /*! @azure/msal-common v15.3.0 2025-03-20 */
962
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
958
963
  /*
959
964
  * Copyright (c) Microsoft Corporation. All rights reserved.
960
965
  * Licensed under the MIT License.
@@ -974,7 +979,7 @@
974
979
  AzureUsGovernment: "https://login.microsoftonline.us",
975
980
  };
976
981
 
977
- /*! @azure/msal-common v15.3.0 2025-03-20 */
982
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
978
983
 
979
984
  /*
980
985
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1035,7 +1040,7 @@
1035
1040
  }
1036
1041
  }
1037
1042
 
1038
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1043
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1039
1044
  /*
1040
1045
  * Copyright (c) Microsoft Corporation. All rights reserved.
1041
1046
  * Licensed under the MIT License.
@@ -1090,7 +1095,7 @@
1090
1095
  return cachedAtSec > nowSeconds();
1091
1096
  }
1092
1097
 
1093
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1098
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1094
1099
 
1095
1100
  /*
1096
1101
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1417,7 +1422,7 @@
1417
1422
  return metadata.expiresAt <= nowSeconds();
1418
1423
  }
1419
1424
 
1420
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1425
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1421
1426
  /*
1422
1427
  * Copyright (c) Microsoft Corporation. All rights reserved.
1423
1428
  * Licensed under the MIT License.
@@ -1471,7 +1476,7 @@
1471
1476
  urlParseError: urlParseError
1472
1477
  });
1473
1478
 
1474
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1479
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1475
1480
 
1476
1481
  /*
1477
1482
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1609,7 +1614,7 @@
1609
1614
  return new ClientConfigurationError(errorCode);
1610
1615
  }
1611
1616
 
1612
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1617
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1613
1618
  /*
1614
1619
  * Copyright (c) Microsoft Corporation. All rights reserved.
1615
1620
  * Licensed under the MIT License.
@@ -1706,7 +1711,7 @@
1706
1711
  }
1707
1712
  }
1708
1713
 
1709
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1714
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1710
1715
 
1711
1716
  /*
1712
1717
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1897,7 +1902,7 @@
1897
1902
  }
1898
1903
  }
1899
1904
 
1900
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1905
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1901
1906
 
1902
1907
  /*
1903
1908
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1937,7 +1942,7 @@
1937
1942
  };
1938
1943
  }
1939
1944
 
1940
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1945
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
1941
1946
  /*
1942
1947
  * Copyright (c) Microsoft Corporation. All rights reserved.
1943
1948
  * Licensed under the MIT License.
@@ -2016,7 +2021,7 @@
2016
2021
  return updatedAccountInfo;
2017
2022
  }
2018
2023
 
2019
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2024
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2020
2025
  /*
2021
2026
  * Copyright (c) Microsoft Corporation. All rights reserved.
2022
2027
  * Licensed under the MIT License.
@@ -2031,7 +2036,7 @@
2031
2036
  Ciam: 3,
2032
2037
  };
2033
2038
 
2034
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2039
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2035
2040
  /*
2036
2041
  * Copyright (c) Microsoft Corporation. All rights reserved.
2037
2042
  * Licensed under the MIT License.
@@ -2053,7 +2058,7 @@
2053
2058
  return null;
2054
2059
  }
2055
2060
 
2056
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2061
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2057
2062
  /*
2058
2063
  * Copyright (c) Microsoft Corporation. All rights reserved.
2059
2064
  * Licensed under the MIT License.
@@ -2062,11 +2067,22 @@
2062
2067
  * Protocol modes supported by MSAL.
2063
2068
  */
2064
2069
  const ProtocolMode = {
2070
+ /**
2071
+ * Auth Code + PKCE with Entra ID (formerly AAD) specific optimizations and features
2072
+ */
2065
2073
  AAD: "AAD",
2074
+ /**
2075
+ * Auth Code + PKCE without Entra ID specific optimizations and features. For use only with non-Microsoft owned authorities.
2076
+ * Support is limited for this mode.
2077
+ */
2066
2078
  OIDC: "OIDC",
2079
+ /**
2080
+ * Encrypted Authorize Response (EAR) with Entra ID specific optimizations and features
2081
+ */
2082
+ EAR: "EAR",
2067
2083
  };
2068
2084
 
2069
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2085
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2070
2086
 
2071
2087
  /*
2072
2088
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2162,11 +2178,11 @@
2162
2178
  if (authority.authorityType === AuthorityType.Adfs) {
2163
2179
  account.authorityType = CacheAccountType.ADFS_ACCOUNT_TYPE;
2164
2180
  }
2165
- else if (authority.protocolMode === ProtocolMode.AAD) {
2166
- account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2181
+ else if (authority.protocolMode === ProtocolMode.OIDC) {
2182
+ account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2167
2183
  }
2168
2184
  else {
2169
- account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2185
+ account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2170
2186
  }
2171
2187
  let clientInfo;
2172
2188
  if (accountDetails.clientInfo && base64Decode) {
@@ -2309,7 +2325,7 @@
2309
2325
  }
2310
2326
  }
2311
2327
 
2312
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2328
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2313
2329
 
2314
2330
  /*
2315
2331
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2344,6 +2360,7 @@
2344
2360
  const deserializedHash = Object.fromEntries(new URLSearchParams(normalizedResponse));
2345
2361
  // Check for known response properties
2346
2362
  if (deserializedHash.code ||
2363
+ deserializedHash.ear_jwe ||
2347
2364
  deserializedHash.error ||
2348
2365
  deserializedHash.error_description ||
2349
2366
  deserializedHash.state) {
@@ -2366,7 +2383,7 @@
2366
2383
  return queryParameterArray.join("&");
2367
2384
  }
2368
2385
 
2369
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2386
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2370
2387
 
2371
2388
  /*
2372
2389
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2530,7 +2547,7 @@
2530
2547
  }
2531
2548
  }
2532
2549
 
2533
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2550
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2534
2551
 
2535
2552
  /*
2536
2553
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2670,7 +2687,7 @@
2670
2687
  return null;
2671
2688
  }
2672
2689
 
2673
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2690
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2674
2691
  /*
2675
2692
  * Copyright (c) Microsoft Corporation. All rights reserved.
2676
2693
  * Licensed under the MIT License.
@@ -2678,7 +2695,7 @@
2678
2695
  const cacheQuotaExceededErrorCode = "cache_quota_exceeded";
2679
2696
  const cacheUnknownErrorCode = "cache_error_unknown";
2680
2697
 
2681
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2698
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2682
2699
 
2683
2700
  /*
2684
2701
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2705,7 +2722,7 @@
2705
2722
  }
2706
2723
  }
2707
2724
 
2708
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2725
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
2709
2726
 
2710
2727
  /*
2711
2728
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3890,7 +3907,7 @@
3890
3907
  }
3891
3908
  }
3892
3909
 
3893
- /*! @azure/msal-common v15.3.0 2025-03-20 */
3910
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
3894
3911
 
3895
3912
  /*
3896
3913
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3989,7 +4006,7 @@
3989
4006
  return (config.authOptions.authority.options.protocolMode === ProtocolMode.OIDC);
3990
4007
  }
3991
4008
 
3992
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4009
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
3993
4010
  /*
3994
4011
  * Copyright (c) Microsoft Corporation. All rights reserved.
3995
4012
  * Licensed under the MIT License.
@@ -3999,7 +4016,7 @@
3999
4016
  UPN: "UPN",
4000
4017
  };
4001
4018
 
4002
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4019
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4003
4020
  /*
4004
4021
  * Copyright (c) Microsoft Corporation. All rights reserved.
4005
4022
  * Licensed under the MIT License.
@@ -4045,9 +4062,11 @@
4045
4062
  const X_CLIENT_EXTRA_SKU = "x-client-xtra-sku";
4046
4063
  const BROKER_CLIENT_ID = "brk_client_id";
4047
4064
  const BROKER_REDIRECT_URI = "brk_redirect_uri";
4048
- const INSTANCE_AWARE = "instance_aware";
4065
+ const INSTANCE_AWARE = "instance_aware";
4066
+ const EAR_JWK = "ear_jwk";
4067
+ const EAR_JWE_CRYPTO = "ear_jwe_crypto";
4049
4068
 
4050
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4069
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4051
4070
 
4052
4071
  /*
4053
4072
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -4066,10 +4085,12 @@
4066
4085
  }
4067
4086
  }
4068
4087
  /**
4069
- * add response_type = code
4088
+ * Add the given response_type
4089
+ * @param parameters
4090
+ * @param responseType
4070
4091
  */
4071
- function addResponseTypeCode(parameters) {
4072
- parameters.set(RESPONSE_TYPE, Constants.CODE_RESPONSE_TYPE);
4092
+ function addResponseType(parameters, responseType) {
4093
+ parameters.set(RESPONSE_TYPE, responseType);
4073
4094
  }
4074
4095
  /**
4075
4096
  * add response_mode. defaults to query.
@@ -4400,9 +4421,20 @@
4400
4421
  if (!parameters.has(BROKER_REDIRECT_URI)) {
4401
4422
  parameters.set(BROKER_REDIRECT_URI, brokerRedirectUri);
4402
4423
  }
4424
+ }
4425
+ /**
4426
+ * Add EAR (Encrypted Authorize Response) request parameters
4427
+ * @param parameters
4428
+ * @param jwk
4429
+ */
4430
+ function addEARParameters(parameters, jwk) {
4431
+ parameters.set(EAR_JWK, encodeURIComponent(jwk));
4432
+ // ear_jwe_crypto will always have value: {"alg":"dir","enc":"A256GCM"} so we can hardcode this
4433
+ const jweCryptoB64Encoded = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0";
4434
+ parameters.set(EAR_JWE_CRYPTO, jweCryptoB64Encoded);
4403
4435
  }
4404
4436
 
4405
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4437
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4406
4438
  /*
4407
4439
  * Copyright (c) Microsoft Corporation. All rights reserved.
4408
4440
  * Licensed under the MIT License.
@@ -4414,7 +4446,7 @@
4414
4446
  response.hasOwnProperty("jwks_uri"));
4415
4447
  }
4416
4448
 
4417
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4449
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4418
4450
  /*
4419
4451
  * Copyright (c) Microsoft Corporation. All rights reserved.
4420
4452
  * Licensed under the MIT License.
@@ -4424,7 +4456,7 @@
4424
4456
  response.hasOwnProperty("metadata"));
4425
4457
  }
4426
4458
 
4427
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4459
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4428
4460
  /*
4429
4461
  * Copyright (c) Microsoft Corporation. All rights reserved.
4430
4462
  * Licensed under the MIT License.
@@ -4434,7 +4466,7 @@
4434
4466
  response.hasOwnProperty("error_description"));
4435
4467
  }
4436
4468
 
4437
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4469
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4438
4470
  /*
4439
4471
  * Copyright (c) Microsoft Corporation. All rights reserved.
4440
4472
  * Licensed under the MIT License.
@@ -4620,6 +4652,9 @@
4620
4652
  */
4621
4653
  HandleCodeResponseFromServer: "handleCodeResponseFromServer",
4622
4654
  HandleCodeResponse: "handleCodeResponse",
4655
+ HandleResponseEar: "handleResponseEar",
4656
+ HandleResponsePlatformBroker: "handleResponsePlatformBroker",
4657
+ HandleResponseCode: "handleResponseCode",
4623
4658
  UpdateTokenEndpointAuthority: "updateTokenEndpointAuthority",
4624
4659
  /**
4625
4660
  * APIs in Authorization Code Client (msal-common)
@@ -4686,6 +4721,8 @@
4686
4721
  UrlEncodeArr: "urlEncodeArr",
4687
4722
  Encrypt: "encrypt",
4688
4723
  Decrypt: "decrypt",
4724
+ GenerateEarKey: "generateEarKey",
4725
+ DecryptEarResponse: "decryptEarResponse",
4689
4726
  };
4690
4727
  const PerformanceEventAbbreviations = new Map([
4691
4728
  [PerformanceEvents.AcquireTokenByCode, "ATByCode"],
@@ -4803,6 +4840,12 @@
4803
4840
  "HandleCodeResFromServer",
4804
4841
  ],
4805
4842
  [PerformanceEvents.HandleCodeResponse, "HandleCodeResp"],
4843
+ [PerformanceEvents.HandleResponseEar, "HandleRespEar"],
4844
+ [PerformanceEvents.HandleResponseCode, "HandleRespCode"],
4845
+ [
4846
+ PerformanceEvents.HandleResponsePlatformBroker,
4847
+ "HandleRespPlatBroker",
4848
+ ],
4806
4849
  [PerformanceEvents.UpdateTokenEndpointAuthority, "UpdTEndpointAuth"],
4807
4850
  [PerformanceEvents.AuthClientAcquireToken, "AuthClientAT"],
4808
4851
  [PerformanceEvents.AuthClientExecuteTokenRequest, "AuthClientExecTReq"],
@@ -4905,6 +4948,8 @@
4905
4948
  [PerformanceEvents.UrlEncodeArr, "urlEncArr"],
4906
4949
  [PerformanceEvents.Encrypt, "encrypt"],
4907
4950
  [PerformanceEvents.Decrypt, "decrypt"],
4951
+ [PerformanceEvents.GenerateEarKey, "genEarKey"],
4952
+ [PerformanceEvents.DecryptEarResponse, "decryptEarResp"],
4908
4953
  ]);
4909
4954
  /**
4910
4955
  * State of the performance event.
@@ -4934,7 +4979,7 @@
4934
4979
  "encryptedCacheExpiredCount",
4935
4980
  ]);
4936
4981
 
4937
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4982
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
4938
4983
  /*
4939
4984
  * Copyright (c) Microsoft Corporation. All rights reserved.
4940
4985
  * Licensed under the MIT License.
@@ -5030,7 +5075,7 @@
5030
5075
  };
5031
5076
  };
5032
5077
 
5033
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5078
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5034
5079
 
5035
5080
  /*
5036
5081
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5139,7 +5184,7 @@
5139
5184
  },
5140
5185
  };
5141
5186
 
5142
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5187
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5143
5188
 
5144
5189
  /*
5145
5190
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5317,7 +5362,7 @@
5317
5362
  return (authorityUri.PathSegments.length === 1 &&
5318
5363
  !Authority.reservedTenantDomains.has(authorityUri.PathSegments[0]) &&
5319
5364
  this.getAuthorityType(authorityUri) === AuthorityType.Default &&
5320
- this.protocolMode === ProtocolMode.AAD);
5365
+ this.protocolMode !== ProtocolMode.OIDC);
5321
5366
  }
5322
5367
  /**
5323
5368
  * Replaces tenant in url path with current tenant. Defaults to common.
@@ -5364,7 +5409,7 @@
5364
5409
  const canonicalAuthorityHost = this.hostnameAndPort;
5365
5410
  if (this.canonicalAuthority.endsWith("v2.0/") ||
5366
5411
  this.authorityType === AuthorityType.Adfs ||
5367
- (this.protocolMode !== ProtocolMode.AAD &&
5412
+ (this.protocolMode === ProtocolMode.OIDC &&
5368
5413
  !this.isAliasOfKnownMicrosoftAuthority(canonicalAuthorityHost))) {
5369
5414
  return `${this.canonicalAuthority}.well-known/openid-configuration`;
5370
5415
  }
@@ -5978,7 +6023,7 @@
5978
6023
  };
5979
6024
  }
5980
6025
 
5981
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6026
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
5982
6027
 
5983
6028
  /*
5984
6029
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6009,7 +6054,7 @@
6009
6054
  }
6010
6055
  }
6011
6056
 
6012
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6057
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6013
6058
 
6014
6059
  /*
6015
6060
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6028,7 +6073,7 @@
6028
6073
  }
6029
6074
  }
6030
6075
 
6031
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6076
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6032
6077
  /*
6033
6078
  * Copyright (c) Microsoft Corporation. All rights reserved.
6034
6079
  * Licensed under the MIT License.
@@ -6049,7 +6094,7 @@
6049
6094
  };
6050
6095
  }
6051
6096
 
6052
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6097
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6053
6098
 
6054
6099
  /*
6055
6100
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6136,7 +6181,7 @@
6136
6181
  }
6137
6182
  }
6138
6183
 
6139
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6184
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6140
6185
 
6141
6186
  /*
6142
6187
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6166,7 +6211,7 @@
6166
6211
  return new NetworkError(error, httpStatus, responseHeaders);
6167
6212
  }
6168
6213
 
6169
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6214
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6170
6215
 
6171
6216
  /*
6172
6217
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6314,7 +6359,7 @@
6314
6359
  }
6315
6360
  }
6316
6361
 
6317
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6362
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6318
6363
  /*
6319
6364
  * Copyright (c) Microsoft Corporation. All rights reserved.
6320
6365
  * Licensed under the MIT License.
@@ -6340,7 +6385,7 @@
6340
6385
  refreshTokenExpired: refreshTokenExpired
6341
6386
  });
6342
6387
 
6343
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6388
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6344
6389
 
6345
6390
  /*
6346
6391
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6428,7 +6473,7 @@
6428
6473
  return new InteractionRequiredAuthError(errorCode, InteractionRequiredAuthErrorMessages[errorCode]);
6429
6474
  }
6430
6475
 
6431
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6476
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6432
6477
 
6433
6478
  /*
6434
6479
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6500,7 +6545,7 @@
6500
6545
  }
6501
6546
  }
6502
6547
 
6503
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6548
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6504
6549
 
6505
6550
  /*
6506
6551
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6584,7 +6629,7 @@
6584
6629
  }
6585
6630
  }
6586
6631
 
6587
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6632
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6588
6633
  /*
6589
6634
  * Copyright (c) Microsoft Corporation. All rights reserved.
6590
6635
  * Licensed under the MIT License.
@@ -6611,7 +6656,7 @@
6611
6656
  }
6612
6657
  }
6613
6658
 
6614
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6659
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6615
6660
 
6616
6661
  /*
6617
6662
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6942,7 +6987,7 @@
6942
6987
  return baseAccount;
6943
6988
  }
6944
6989
 
6945
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6990
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
6946
6991
 
6947
6992
  /*
6948
6993
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7009,7 +7054,7 @@
7009
7054
  }
7010
7055
  }
7011
7056
 
7012
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7057
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7013
7058
  /*
7014
7059
  * Copyright (c) Microsoft Corporation. All rights reserved.
7015
7060
  * Licensed under the MIT License.
@@ -7027,7 +7072,7 @@
7027
7072
  }
7028
7073
  }
7029
7074
 
7030
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7075
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7031
7076
 
7032
7077
  /*
7033
7078
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7260,7 +7305,7 @@
7260
7305
  }
7261
7306
  }
7262
7307
 
7263
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7308
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7264
7309
 
7265
7310
  /*
7266
7311
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7469,7 +7514,7 @@
7469
7514
  }
7470
7515
  }
7471
7516
 
7472
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7517
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7473
7518
 
7474
7519
  /*
7475
7520
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7567,7 +7612,7 @@
7567
7612
  }
7568
7613
  }
7569
7614
 
7570
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7615
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7571
7616
 
7572
7617
  /*
7573
7618
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7582,7 +7627,7 @@
7582
7627
  },
7583
7628
  };
7584
7629
 
7585
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7630
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7586
7631
 
7587
7632
  /*
7588
7633
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7711,10 +7756,10 @@
7711
7756
  if (request.embeddedClientId) {
7712
7757
  addBrokerParameters(parameters, authOptions.clientId, authOptions.redirectUri);
7713
7758
  }
7714
- if (request.extraQueryParameters) {
7715
- addExtraQueryParameters(parameters, request.extraQueryParameters);
7716
- }
7717
- if (authOptions.instanceAware) {
7759
+ // If extraQueryParameters includes instance_aware its value will be added when extraQueryParameters are added
7760
+ if (authOptions.instanceAware &&
7761
+ (!request.extraQueryParameters ||
7762
+ !Object.keys(request.extraQueryParameters).includes(INSTANCE_AWARE))) {
7718
7763
  addInstanceAware(parameters);
7719
7764
  }
7720
7765
  return parameters;
@@ -7806,7 +7851,7 @@
7806
7851
  return account.idTokenClaims?.login_hint || null;
7807
7852
  }
7808
7853
 
7809
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7854
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7810
7855
 
7811
7856
  /*
7812
7857
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7864,7 +7909,7 @@
7864
7909
  }
7865
7910
  }
7866
7911
 
7867
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7912
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
7868
7913
 
7869
7914
  /*
7870
7915
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8127,7 +8172,7 @@
8127
8172
  }
8128
8173
  }
8129
8174
 
8130
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8175
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8131
8176
  /*
8132
8177
  * Copyright (c) Microsoft Corporation. All rights reserved.
8133
8178
  * Licensed under the MIT License.
@@ -8135,7 +8180,7 @@
8135
8180
  const missingKidError = "missing_kid_error";
8136
8181
  const missingAlgError = "missing_alg_error";
8137
8182
 
8138
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8183
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8139
8184
 
8140
8185
  /*
8141
8186
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8160,7 +8205,7 @@
8160
8205
  return new JoseHeaderError(code, JoseHeaderErrorMessages[code]);
8161
8206
  }
8162
8207
 
8163
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8208
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8164
8209
 
8165
8210
  /*
8166
8211
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8200,7 +8245,7 @@
8200
8245
  }
8201
8246
  }
8202
8247
 
8203
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8248
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8204
8249
 
8205
8250
  /*
8206
8251
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8279,7 +8324,7 @@
8279
8324
  }
8280
8325
  }
8281
8326
 
8282
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8327
+ /*! @azure/msal-common v15.4.0 2025-03-25 */
8283
8328
 
8284
8329
  /*
8285
8330
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8893,6 +8938,8 @@
8893
8938
  * Licensed under the MIT License.
8894
8939
  */
8895
8940
  const pkceNotCreated = "pkce_not_created";
8941
+ const earJwkEmpty = "ear_jwk_empty";
8942
+ const earJweEmpty = "ear_jwe_empty";
8896
8943
  const cryptoNonExistent = "crypto_nonexistent";
8897
8944
  const emptyNavigateUri = "empty_navigate_uri";
8898
8945
  const hashEmptyError = "hash_empty_error";
@@ -8915,7 +8962,6 @@
8915
8962
  const silentPromptValueError = "silent_prompt_value_error";
8916
8963
  const noTokenRequestCacheError = "no_token_request_cache_error";
8917
8964
  const unableToParseTokenRequestCacheError = "unable_to_parse_token_request_cache_error";
8918
- const noCachedAuthorityError = "no_cached_authority_error";
8919
8965
  const authRequestNotSetError = "auth_request_not_set_error";
8920
8966
  const invalidCacheType = "invalid_cache_type";
8921
8967
  const nonBrowserEnvironment = "non_browser_environment";
@@ -8939,7 +8985,8 @@
8939
8985
  const invalidBase64String = "invalid_base64_string";
8940
8986
  const invalidPopTokenRequest = "invalid_pop_token_request";
8941
8987
  const failedToBuildHeaders = "failed_to_build_headers";
8942
- const failedToParseHeaders = "failed_to_parse_headers";
8988
+ const failedToParseHeaders = "failed_to_parse_headers";
8989
+ const failedToDecryptEarResponse = "failed_to_decrypt_ear_response";
8943
8990
 
8944
8991
  var BrowserAuthErrorCodes = /*#__PURE__*/Object.freeze({
8945
8992
  __proto__: null,
@@ -8952,9 +8999,12 @@
8952
8999
  cryptoNonExistent: cryptoNonExistent,
8953
9000
  databaseNotOpen: databaseNotOpen,
8954
9001
  databaseUnavailable: databaseUnavailable,
9002
+ earJweEmpty: earJweEmpty,
9003
+ earJwkEmpty: earJwkEmpty,
8955
9004
  emptyNavigateUri: emptyNavigateUri,
8956
9005
  emptyWindowError: emptyWindowError,
8957
9006
  failedToBuildHeaders: failedToBuildHeaders,
9007
+ failedToDecryptEarResponse: failedToDecryptEarResponse,
8958
9008
  failedToParseHeaders: failedToParseHeaders,
8959
9009
  failedToParseResponse: failedToParseResponse,
8960
9010
  getRequestFailed: getRequestFailed,
@@ -8972,7 +9022,6 @@
8972
9022
  nativeHandshakeTimeout: nativeHandshakeTimeout,
8973
9023
  nativePromptNotSupported: nativePromptNotSupported,
8974
9024
  noAccountError: noAccountError,
8975
- noCachedAuthorityError: noCachedAuthorityError,
8976
9025
  noNetworkConnectivity: noNetworkConnectivity,
8977
9026
  noStateInHash: noStateInHash,
8978
9027
  noTokenRequestCacheError: noTokenRequestCacheError,
@@ -9003,6 +9052,8 @@
9003
9052
  */
9004
9053
  const BrowserAuthErrorMessages = {
9005
9054
  [pkceNotCreated]: "The PKCE code challenge and verifier could not be generated.",
9055
+ [earJwkEmpty]: "No EAR encryption key provided. This is unexpected.",
9056
+ [earJweEmpty]: "Server response does not contain ear_jwe property. This is unexpected.",
9006
9057
  [cryptoNonExistent]: "The crypto object or function is not available.",
9007
9058
  [emptyNavigateUri]: "Navigation URI is empty. Please check stack trace for more info.",
9008
9059
  [hashEmptyError]: `Hash value cannot be processed because it is empty. Please verify that your redirectUri is not clearing the hash. ${ErrorLink}`,
@@ -9025,7 +9076,6 @@
9025
9076
  [silentPromptValueError]: "The value given for the prompt value is not valid for silent requests - must be set to 'none' or 'no_session'.",
9026
9077
  [noTokenRequestCacheError]: "No token request found in cache.",
9027
9078
  [unableToParseTokenRequestCacheError]: "The cached token request could not be parsed.",
9028
- [noCachedAuthorityError]: "No cached authority found.",
9029
9079
  [authRequestNotSetError]: "Auth Request not set. Please ensure initiateAuthRequest was called from the InteractionHandler",
9030
9080
  [invalidCacheType]: "Invalid cache type",
9031
9081
  [nonBrowserEnvironment]: "Login and token requests are not supported in non-browser environments.",
@@ -9050,6 +9100,7 @@
9050
9100
  [invalidPopTokenRequest]: "Invalid PoP token request. The request should not have both a popKid value and signPopToken set to true.",
9051
9101
  [failedToBuildHeaders]: "Failed to build request headers object.",
9052
9102
  [failedToParseHeaders]: "Failed to parse response headers",
9103
+ [failedToDecryptEarResponse]: "Failed to decrypt ear response",
9053
9104
  };
9054
9105
  /**
9055
9106
  * BrowserAuthErrorMessage class containing string constants used by error codes and messages.
@@ -9152,10 +9203,6 @@
9152
9203
  code: unableToParseTokenRequestCacheError,
9153
9204
  desc: BrowserAuthErrorMessages[unableToParseTokenRequestCacheError],
9154
9205
  },
9155
- noCachedAuthorityError: {
9156
- code: noCachedAuthorityError,
9157
- desc: BrowserAuthErrorMessages[noCachedAuthorityError],
9158
- },
9159
9206
  authRequestNotSet: {
9160
9207
  code: authRequestNotSetError,
9161
9208
  desc: BrowserAuthErrorMessages[authRequestNotSetError],
@@ -9323,21 +9370,12 @@
9323
9370
  * Temporary cache keys for MSAL, deleted after any request.
9324
9371
  */
9325
9372
  const TemporaryCacheKeys = {
9326
- AUTHORITY: "authority",
9327
- ACQUIRE_TOKEN_ACCOUNT: "acquireToken.account",
9328
- SESSION_STATE: "session.state",
9329
- REQUEST_STATE: "request.state",
9330
- NONCE_IDTOKEN: "nonce.id_token",
9331
9373
  ORIGIN_URI: "request.origin",
9332
- RENEW_STATUS: "token.renew.status",
9333
9374
  URL_HASH: "urlHash",
9334
9375
  REQUEST_PARAMS: "request.params",
9335
- SCOPES: "scopes",
9376
+ VERIFIER: "code.verifier",
9336
9377
  INTERACTION_STATUS_KEY: "interaction.status",
9337
- CCS_CREDENTIAL: "ccs.credential",
9338
- CORRELATION_ID: "request.correlationId",
9339
9378
  NATIVE_REQUEST: "request.native",
9340
- REDIRECT_CONTEXT: "request.redirect.context",
9341
9379
  };
9342
9380
  const StaticCacheKeys = {
9343
9381
  ACCOUNT_KEYS: "msal.account.keys",
@@ -9707,6 +9745,69 @@
9707
9745
  async function sign(key, data) {
9708
9746
  return window.crypto.subtle.sign(keygenAlgorithmOptions, key, data);
9709
9747
  }
9748
+ /**
9749
+ * Generates Base64 encoded jwk used in the Encrypted Authorize Response (EAR) flow
9750
+ */
9751
+ async function generateEarKey() {
9752
+ const key = await generateBaseKey();
9753
+ const keyStr = urlEncodeArr(new Uint8Array(key));
9754
+ const jwk = {
9755
+ alg: "dir",
9756
+ kty: "oct",
9757
+ k: keyStr,
9758
+ };
9759
+ return base64Encode(JSON.stringify(jwk));
9760
+ }
9761
+ /**
9762
+ * Parses earJwk for encryption key and returns CryptoKey object
9763
+ * @param earJwk
9764
+ * @returns
9765
+ */
9766
+ async function importEarKey(earJwk) {
9767
+ const b64DecodedJwk = base64Decode(earJwk);
9768
+ const jwkJson = JSON.parse(b64DecodedJwk);
9769
+ const rawKey = jwkJson.k;
9770
+ const keyBuffer = base64DecToArr(rawKey);
9771
+ return window.crypto.subtle.importKey(RAW, keyBuffer, AES_GCM, false, [
9772
+ DECRYPT,
9773
+ ]);
9774
+ }
9775
+ /**
9776
+ * Decrypt ear_jwe response returned in the Encrypted Authorize Response (EAR) flow
9777
+ * @param earJwk
9778
+ * @param earJwe
9779
+ * @returns
9780
+ */
9781
+ async function decryptEarResponse(earJwk, earJwe) {
9782
+ const earJweParts = earJwe.split(".");
9783
+ if (earJweParts.length !== 5) {
9784
+ throw createBrowserAuthError(failedToDecryptEarResponse, "jwe_length");
9785
+ }
9786
+ const key = await importEarKey(earJwk).catch(() => {
9787
+ throw createBrowserAuthError(failedToDecryptEarResponse, "import_key");
9788
+ });
9789
+ try {
9790
+ const header = new TextEncoder().encode(earJweParts[0]);
9791
+ const iv = base64DecToArr(earJweParts[2]);
9792
+ const ciphertext = base64DecToArr(earJweParts[3]);
9793
+ const tag = base64DecToArr(earJweParts[4]);
9794
+ const tagLengthBits = tag.byteLength * 8;
9795
+ // Concat ciphertext and tag
9796
+ const encryptedData = new Uint8Array(ciphertext.length + tag.length);
9797
+ encryptedData.set(ciphertext);
9798
+ encryptedData.set(tag, ciphertext.length);
9799
+ const decryptedData = await window.crypto.subtle.decrypt({
9800
+ name: AES_GCM,
9801
+ iv: iv,
9802
+ tagLength: tagLengthBits,
9803
+ additionalData: header,
9804
+ }, key, encryptedData);
9805
+ return new TextDecoder().decode(decryptedData);
9806
+ }
9807
+ catch (e) {
9808
+ throw createBrowserAuthError(failedToDecryptEarResponse, "decrypt");
9809
+ }
9810
+ }
9710
9811
  /**
9711
9812
  * Generates symmetric base encryption key. This may be stored as all encryption/decryption keys will be derived from this one.
9712
9813
  */
@@ -10292,9 +10393,9 @@
10292
10393
  const logger = new Logger(providedSystemOptions.loggerOptions);
10293
10394
  logger.warning(JSON.stringify(createClientConfigurationError(cannotSetOIDCOptions)));
10294
10395
  }
10295
- // Throw an error if user has set allowPlatformBroker to true without being in AAD protocol mode
10396
+ // Throw an error if user has set allowPlatformBroker to true with OIDC protocol mode
10296
10397
  if (userInputAuth?.protocolMode &&
10297
- userInputAuth.protocolMode !== ProtocolMode.AAD &&
10398
+ userInputAuth.protocolMode === ProtocolMode.OIDC &&
10298
10399
  providedSystemOptions?.allowPlatformBroker) {
10299
10400
  throw createClientConfigurationError(cannotAllowPlatformBroker);
10300
10401
  }
@@ -10311,12 +10412,21 @@
10311
10412
  system: providedSystemOptions,
10312
10413
  telemetry: { ...DEFAULT_TELEMETRY_OPTIONS, ...userInputTelemetry },
10313
10414
  };
10415
+ /**
10416
+ * Temporarily disable EAR until implementation is complete
10417
+ * TODO: Remove this
10418
+ */
10419
+ if (overlayedConfig.auth.protocolMode === ProtocolMode.EAR) {
10420
+ const logger = new Logger(providedSystemOptions.loggerOptions);
10421
+ logger.warning("EAR Protocol Mode is not yet supported. Overriding to use PKCE auth");
10422
+ overlayedConfig.auth.protocolMode = ProtocolMode.AAD;
10423
+ }
10314
10424
  return overlayedConfig;
10315
10425
  }
10316
10426
 
10317
10427
  /* eslint-disable header/header */
10318
10428
  const name = "@azure/msal-browser";
10319
- const version = "4.8.0";
10429
+ const version = "4.9.0";
10320
10430
 
10321
10431
  /*
10322
10432
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -11637,28 +11747,6 @@
11637
11747
  }
11638
11748
  }
11639
11749
 
11640
- /*
11641
- * Copyright (c) Microsoft Corporation. All rights reserved.
11642
- * Licensed under the MIT License.
11643
- */
11644
- /**
11645
- * Extracts the BrowserStateObject from the state string.
11646
- * @param browserCrypto
11647
- * @param state
11648
- */
11649
- function extractBrowserRequestState(browserCrypto, state) {
11650
- if (!state) {
11651
- return null;
11652
- }
11653
- try {
11654
- const requestStateObj = ProtocolUtils.parseRequestState(browserCrypto, state);
11655
- return requestStateObj.libraryState.meta;
11656
- }
11657
- catch (e) {
11658
- throw createClientAuthError(invalidState);
11659
- }
11660
- }
11661
-
11662
11750
  /*
11663
11751
  * Copyright (c) Microsoft Corporation. All rights reserved.
11664
11752
  * Licensed under the MIT License.
@@ -12358,177 +12446,53 @@
12358
12446
  }
12359
12447
  return JSON.stringify(key);
12360
12448
  }
12361
- /**
12362
- * Create authorityKey to cache authority
12363
- * @param state
12364
- */
12365
- generateAuthorityKey(stateString) {
12366
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12367
- return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
12368
- }
12369
- /**
12370
- * Create Nonce key to cache nonce
12371
- * @param state
12372
- */
12373
- generateNonceKey(stateString) {
12374
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12375
- return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
12376
- }
12377
- /**
12378
- * Creates full cache key for the request state
12379
- * @param stateString State string for the request
12380
- */
12381
- generateStateKey(stateString) {
12382
- // Use the library state id to key temp storage for uniqueness for multiple concurrent requests
12383
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
12384
- return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
12385
- }
12386
- /**
12387
- * Gets the cached authority based on the cached state. Returns empty if no cached state found.
12388
- */
12389
- getCachedAuthority(cachedState) {
12390
- const stateCacheKey = this.generateStateKey(cachedState);
12391
- const state = this.getTemporaryCache(stateCacheKey);
12392
- if (!state) {
12393
- return null;
12394
- }
12395
- const authorityCacheKey = this.generateAuthorityKey(state);
12396
- return this.getTemporaryCache(authorityCacheKey);
12397
- }
12398
- /**
12399
- * Updates account, authority, and state in cache
12400
- * @param serverAuthenticationRequest
12401
- * @param account
12402
- */
12403
- updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
12404
- this.logger.trace("BrowserCacheManager.updateCacheEntries called");
12405
- // Cache the request state
12406
- const stateCacheKey = this.generateStateKey(state);
12407
- this.setTemporaryCache(stateCacheKey, state, false);
12408
- // Cache the nonce
12409
- const nonceCacheKey = this.generateNonceKey(state);
12410
- this.setTemporaryCache(nonceCacheKey, nonce, false);
12411
- // Cache authorityKey
12412
- const authorityCacheKey = this.generateAuthorityKey(state);
12413
- this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
12414
- if (account) {
12415
- const ccsCredential = {
12416
- credential: account.homeAccountId,
12417
- type: CcsCredentialType.HOME_ACCOUNT_ID,
12418
- };
12419
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
12420
- }
12421
- else if (loginHint) {
12422
- const ccsCredential = {
12423
- credential: loginHint,
12424
- type: CcsCredentialType.UPN,
12425
- };
12426
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
12427
- }
12428
- }
12429
12449
  /**
12430
12450
  * Reset all temporary cache items
12431
12451
  * @param state
12432
12452
  */
12433
- resetRequestCache(state) {
12453
+ resetRequestCache() {
12434
12454
  this.logger.trace("BrowserCacheManager.resetRequestCache called");
12435
- // check state and remove associated cache items
12436
- if (state) {
12437
- this.temporaryCacheStorage.getKeys().forEach((key) => {
12438
- if (key.indexOf(state) !== -1) {
12439
- this.removeTemporaryItem(key);
12440
- }
12441
- });
12442
- // delete generic interactive request parameters
12443
- this.removeTemporaryItem(this.generateStateKey(state));
12444
- this.removeTemporaryItem(this.generateNonceKey(state));
12445
- this.removeTemporaryItem(this.generateAuthorityKey(state));
12446
- }
12447
12455
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
12456
+ this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.VERIFIER));
12448
12457
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
12449
12458
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
12450
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
12451
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
12452
12459
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
12453
12460
  this.setInteractionInProgress(false);
12454
12461
  }
12455
- /**
12456
- * Removes temporary cache for the provided state
12457
- * @param stateString
12458
- */
12459
- cleanRequestByState(stateString) {
12460
- this.logger.trace("BrowserCacheManager.cleanRequestByState called");
12461
- // Interaction is completed - remove interaction status.
12462
- if (stateString) {
12463
- const stateKey = this.generateStateKey(stateString);
12464
- const cachedState = this.temporaryCacheStorage.getItem(stateKey);
12465
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
12466
- this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
12467
- }
12468
- }
12469
- /**
12470
- * Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
12471
- * Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
12472
- * @param interactionType
12473
- */
12474
- cleanRequestByInteractionType(interactionType) {
12475
- this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
12476
- // Loop through all keys to find state key
12477
- this.temporaryCacheStorage.getKeys().forEach((key) => {
12478
- // If this key is not the state key, move on
12479
- if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
12480
- return;
12481
- }
12482
- // Retrieve state value, return if not a valid value
12483
- const stateValue = this.temporaryCacheStorage.getItem(key);
12484
- if (!stateValue) {
12485
- return;
12486
- }
12487
- // Extract state and ensure it matches given InteractionType, then clean request cache
12488
- const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
12489
- if (parsedState &&
12490
- parsedState.interactionType === interactionType) {
12491
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
12492
- this.resetRequestCache(stateValue);
12493
- }
12494
- });
12495
- this.setInteractionInProgress(false);
12496
- }
12497
- cacheCodeRequest(authCodeRequest) {
12498
- this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
12462
+ cacheAuthorizeRequest(authCodeRequest, codeVerifier) {
12463
+ this.logger.trace("BrowserCacheManager.cacheAuthorizeRequest called");
12499
12464
  const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
12500
12465
  this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
12466
+ if (codeVerifier) {
12467
+ const encodedVerifier = base64Encode(codeVerifier);
12468
+ this.setTemporaryCache(TemporaryCacheKeys.VERIFIER, encodedVerifier, true);
12469
+ }
12501
12470
  }
12502
12471
  /**
12503
12472
  * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
12504
12473
  */
12505
- getCachedRequest(state) {
12474
+ getCachedRequest() {
12506
12475
  this.logger.trace("BrowserCacheManager.getCachedRequest called");
12507
12476
  // Get token request from cache and parse as TokenExchangeParameters.
12508
12477
  const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
12509
12478
  if (!encodedTokenRequest) {
12510
12479
  throw createBrowserAuthError(noTokenRequestCacheError);
12511
12480
  }
12481
+ const encodedVerifier = this.getTemporaryCache(TemporaryCacheKeys.VERIFIER, true);
12512
12482
  let parsedRequest;
12483
+ let verifier = "";
12513
12484
  try {
12514
12485
  parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
12486
+ if (encodedVerifier) {
12487
+ verifier = base64Decode(encodedVerifier);
12488
+ }
12515
12489
  }
12516
12490
  catch (e) {
12517
12491
  this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
12518
12492
  this.logger.error(`Parsing cached token request threw with error: ${e}`);
12519
12493
  throw createBrowserAuthError(unableToParseTokenRequestCacheError);
12520
12494
  }
12521
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
12522
- // Get cached authority and use if no authority is cached with request.
12523
- if (!parsedRequest.authority) {
12524
- const authorityCacheKey = this.generateAuthorityKey(state);
12525
- const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
12526
- if (!cachedAuthority) {
12527
- throw createBrowserAuthError(noCachedAuthorityError);
12528
- }
12529
- parsedRequest.authority = cachedAuthority;
12530
- }
12531
- return parsedRequest;
12495
+ return [parsedRequest, verifier];
12532
12496
  }
12533
12497
  /**
12534
12498
  * Gets cached native request for redirect flows
@@ -13354,1083 +13318,1103 @@
13354
13318
  * Copyright (c) Microsoft Corporation. All rights reserved.
13355
13319
  * Licensed under the MIT License.
13356
13320
  */
13357
- class SilentCacheClient extends StandardInteractionClient {
13321
+ class NativeMessageHandler {
13322
+ constructor(logger, handshakeTimeoutMs, performanceClient, extensionId) {
13323
+ this.logger = logger;
13324
+ this.handshakeTimeoutMs = handshakeTimeoutMs;
13325
+ this.extensionId = extensionId;
13326
+ this.resolvers = new Map(); // Used for non-handshake messages
13327
+ this.handshakeResolvers = new Map(); // Used for handshake messages
13328
+ this.messageChannel = new MessageChannel();
13329
+ this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
13330
+ this.performanceClient = performanceClient;
13331
+ this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
13332
+ }
13358
13333
  /**
13359
- * Returns unexpired tokens from the cache, if available
13360
- * @param silentRequest
13334
+ * Sends a given message to the extension and resolves with the extension response
13335
+ * @param body
13361
13336
  */
13362
- async acquireToken(silentRequest) {
13363
- this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentCacheClientAcquireToken, silentRequest.correlationId);
13364
- // Telemetry manager only used to increment cacheHits here
13365
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent_silentFlow);
13366
- const clientConfig = await invokeAsync(this.getClientConfiguration.bind(this), PerformanceEvents.StandardInteractionClientGetClientConfiguration, this.logger, this.performanceClient, this.correlationId)({
13367
- serverTelemetryManager,
13368
- requestAuthority: silentRequest.authority,
13369
- requestAzureCloudOptions: silentRequest.azureCloudOptions,
13370
- account: silentRequest.account,
13337
+ async sendMessage(body) {
13338
+ this.logger.trace("NativeMessageHandler - sendMessage called.");
13339
+ const req = {
13340
+ channel: NativeConstants.CHANNEL_ID,
13341
+ extensionId: this.extensionId,
13342
+ responseId: createNewGuid(),
13343
+ body: body,
13344
+ };
13345
+ this.logger.trace("NativeMessageHandler - Sending request to browser extension");
13346
+ this.logger.tracePii(`NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(req)}`);
13347
+ this.messageChannel.port1.postMessage(req);
13348
+ return new Promise((resolve, reject) => {
13349
+ this.resolvers.set(req.responseId, { resolve, reject });
13371
13350
  });
13372
- const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13373
- this.logger.verbose("Silent auth client created");
13351
+ }
13352
+ /**
13353
+ * Returns an instance of the MessageHandler that has successfully established a connection with an extension
13354
+ * @param {Logger} logger
13355
+ * @param {number} handshakeTimeoutMs
13356
+ * @param {IPerformanceClient} performanceClient
13357
+ * @param {ICrypto} crypto
13358
+ */
13359
+ static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
13360
+ logger.trace("NativeMessageHandler - createProvider called.");
13374
13361
  try {
13375
- const response = await invokeAsync(silentAuthClient.acquireCachedToken.bind(silentAuthClient), PerformanceEvents.SilentFlowClientAcquireCachedToken, this.logger, this.performanceClient, silentRequest.correlationId)(silentRequest);
13376
- const authResponse = response[0];
13377
- this.performanceClient.addFields({
13378
- fromCache: true,
13379
- }, silentRequest.correlationId);
13380
- return authResponse;
13362
+ const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
13363
+ await preferredProvider.sendHandshakeRequest();
13364
+ return preferredProvider;
13381
13365
  }
13382
- catch (error) {
13383
- if (error instanceof BrowserAuthError &&
13384
- error.errorCode === cryptoKeyNotFound) {
13385
- this.logger.verbose("Signing keypair for bound access token not found. Refreshing bound access token and generating a new crypto keypair.");
13386
- }
13387
- throw error;
13366
+ catch (e) {
13367
+ // If preferred extension fails for whatever reason, fallback to using any installed extension
13368
+ const backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
13369
+ await backupProvider.sendHandshakeRequest();
13370
+ return backupProvider;
13388
13371
  }
13389
13372
  }
13390
13373
  /**
13391
- * API to silenty clear the browser cache.
13392
- * @param logoutRequest
13374
+ * Send handshake request helper.
13393
13375
  */
13394
- logout(logoutRequest) {
13395
- this.logger.verbose("logoutRedirect called");
13396
- const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
13397
- return this.clearCacheOnLogout(validLogoutRequest?.account);
13398
- }
13399
- }
13400
-
13401
- /*
13402
- * Copyright (c) Microsoft Corporation. All rights reserved.
13403
- * Licensed under the MIT License.
13404
- */
13405
- class NativeInteractionClient extends BaseInteractionClient {
13406
- constructor(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, provider, accountId, nativeStorageImpl, correlationId) {
13407
- super(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13408
- this.apiId = apiId;
13409
- this.accountId = accountId;
13410
- this.nativeMessageHandler = provider;
13411
- this.nativeStorageManager = nativeStorageImpl;
13412
- this.silentCacheClient = new SilentCacheClient(config, this.nativeStorageManager, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13413
- const extensionName = this.nativeMessageHandler.getExtensionId() ===
13414
- NativeConstants.PREFERRED_EXTENSION_ID
13415
- ? "chrome"
13416
- : this.nativeMessageHandler.getExtensionId()?.length
13417
- ? "unknown"
13418
- : undefined;
13419
- this.skus = ServerTelemetryManager.makeExtraSkuString({
13420
- libraryName: BrowserConstants.MSAL_SKU,
13421
- libraryVersion: version,
13422
- extensionName: extensionName,
13423
- extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
13376
+ async sendHandshakeRequest() {
13377
+ this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
13378
+ // Register this event listener before sending handshake
13379
+ window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
13380
+ const req = {
13381
+ channel: NativeConstants.CHANNEL_ID,
13382
+ extensionId: this.extensionId,
13383
+ responseId: createNewGuid(),
13384
+ body: {
13385
+ method: NativeExtensionMethod.HandshakeRequest,
13386
+ },
13387
+ };
13388
+ this.handshakeEvent.add({
13389
+ extensionId: this.extensionId,
13390
+ extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
13424
13391
  });
13425
- }
13426
- /**
13427
- * Adds SKUs to request extra query parameters
13428
- * @param request {NativeTokenRequest}
13429
- * @private
13430
- */
13431
- addRequestSKUs(request) {
13432
- request.extraParameters = {
13433
- ...request.extraParameters,
13434
- [X_CLIENT_EXTRA_SKU]: this.skus,
13392
+ this.messageChannel.port1.onmessage = (event) => {
13393
+ this.onChannelMessage(event);
13435
13394
  };
13436
- }
13437
- /**
13438
- * Acquire token from native platform via browser extension
13439
- * @param request
13440
- */
13441
- async acquireToken(request, cacheLookupPolicy) {
13442
- this.performanceClient.addQueueMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13443
- this.logger.trace("NativeInteractionClient - acquireToken called.");
13444
- // start the perf measurement
13445
- const nativeATMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13446
- const reqTimestamp = nowSeconds();
13447
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13448
- try {
13449
- // initialize native request
13450
- const nativeRequest = await this.initializeNativeRequest(request);
13451
- // check if the tokens can be retrieved from internal cache
13452
- try {
13453
- const result = await this.acquireTokensFromCache(this.accountId, nativeRequest);
13454
- nativeATMeasurement.end({
13455
- success: true,
13456
- isNativeBroker: false,
13457
- fromCache: true,
13458
- });
13459
- return result;
13460
- }
13461
- catch (e) {
13462
- if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
13463
- this.logger.info("MSAL internal Cache does not contain tokens, return error as per cache policy");
13464
- throw e;
13465
- }
13466
- // continue with a native call for any and all errors
13467
- this.logger.info("MSAL internal Cache does not contain tokens, proceed to make a native call");
13468
- }
13469
- const { ...nativeTokenRequest } = nativeRequest;
13470
- // fall back to native calls
13471
- const messageBody = {
13472
- method: NativeExtensionMethod.GetToken,
13473
- request: nativeTokenRequest,
13474
- };
13475
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13476
- const validatedResponse = this.validateNativeResponse(response);
13477
- return await this.handleNativeResponse(validatedResponse, nativeRequest, reqTimestamp)
13478
- .then((result) => {
13479
- nativeATMeasurement.end({
13480
- success: true,
13481
- isNativeBroker: true,
13482
- requestId: result.requestId,
13483
- });
13484
- serverTelemetryManager.clearNativeBrokerErrorCode();
13485
- return result;
13486
- })
13487
- .catch((error) => {
13488
- nativeATMeasurement.end({
13395
+ window.postMessage(req, window.origin, [this.messageChannel.port2]);
13396
+ return new Promise((resolve, reject) => {
13397
+ this.handshakeResolvers.set(req.responseId, { resolve, reject });
13398
+ this.timeoutId = window.setTimeout(() => {
13399
+ /*
13400
+ * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
13401
+ * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
13402
+ */
13403
+ window.removeEventListener("message", this.windowListener, false);
13404
+ this.messageChannel.port1.close();
13405
+ this.messageChannel.port2.close();
13406
+ this.handshakeEvent.end({
13407
+ extensionHandshakeTimedOut: true,
13489
13408
  success: false,
13490
- errorCode: error.errorCode,
13491
- subErrorCode: error.subError,
13492
- isNativeBroker: true,
13493
13409
  });
13494
- throw error;
13495
- });
13496
- }
13497
- catch (e) {
13498
- if (e instanceof NativeAuthError) {
13499
- serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13500
- }
13501
- throw e;
13502
- }
13503
- }
13504
- /**
13505
- * Creates silent flow request
13506
- * @param request
13507
- * @param cachedAccount
13508
- * @returns CommonSilentFlowRequest
13509
- */
13510
- createSilentCacheRequest(request, cachedAccount) {
13511
- return {
13512
- authority: request.authority,
13513
- correlationId: this.correlationId,
13514
- scopes: ScopeSet.fromString(request.scope).asArray(),
13515
- account: cachedAccount,
13516
- forceRefresh: false,
13517
- };
13410
+ reject(createBrowserAuthError(nativeHandshakeTimeout));
13411
+ this.handshakeResolvers.delete(req.responseId);
13412
+ }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
13413
+ });
13518
13414
  }
13519
13415
  /**
13520
- * Fetches the tokens from the cache if un-expired
13521
- * @param nativeAccountId
13522
- * @param request
13523
- * @returns authenticationResult
13416
+ * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
13417
+ * @param event
13524
13418
  */
13525
- async acquireTokensFromCache(nativeAccountId, request) {
13526
- if (!nativeAccountId) {
13527
- this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13528
- throw createClientAuthError(noAccountFound);
13419
+ onWindowMessage(event) {
13420
+ this.logger.trace("NativeMessageHandler - onWindowMessage called");
13421
+ // We only accept messages from ourselves
13422
+ if (event.source !== window) {
13423
+ return;
13529
13424
  }
13530
- // fetch the account from browser cache
13531
- const account = this.browserStorage.getBaseAccountInfo({
13532
- nativeAccountId,
13533
- });
13534
- if (!account) {
13535
- throw createClientAuthError(noAccountFound);
13425
+ const request = event.data;
13426
+ if (!request.channel ||
13427
+ request.channel !== NativeConstants.CHANNEL_ID) {
13428
+ return;
13536
13429
  }
13537
- // leverage silent flow for cached tokens retrieval
13538
- try {
13539
- const silentRequest = this.createSilentCacheRequest(request, account);
13540
- const result = await this.silentCacheClient.acquireToken(silentRequest);
13541
- const fullAccount = {
13542
- ...account,
13543
- idTokenClaims: result?.idTokenClaims,
13544
- idToken: result?.idToken,
13545
- };
13546
- return {
13547
- ...result,
13548
- account: fullAccount,
13549
- };
13430
+ if (request.extensionId && request.extensionId !== this.extensionId) {
13431
+ return;
13550
13432
  }
13551
- catch (e) {
13552
- throw e;
13433
+ if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
13434
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13435
+ /*
13436
+ * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
13437
+ * the proper response.
13438
+ */
13439
+ if (!handshakeResolver) {
13440
+ this.logger.trace(`NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`);
13441
+ return;
13442
+ }
13443
+ // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
13444
+ this.logger.verbose(request.extensionId
13445
+ ? `Extension with id: ${request.extensionId} not installed`
13446
+ : "No extension installed");
13447
+ clearTimeout(this.timeoutId);
13448
+ this.messageChannel.port1.close();
13449
+ this.messageChannel.port2.close();
13450
+ window.removeEventListener("message", this.windowListener, false);
13451
+ this.handshakeEvent.end({
13452
+ success: false,
13453
+ extensionInstalled: false,
13454
+ });
13455
+ handshakeResolver.reject(createBrowserAuthError(nativeExtensionNotInstalled));
13553
13456
  }
13554
13457
  }
13555
13458
  /**
13556
- * Acquires a token from native platform then redirects to the redirectUri instead of returning the response
13557
- * @param {RedirectRequest} request
13558
- * @param {InProgressPerformanceEvent} rootMeasurement
13459
+ * Invoked when a message is received from the extension on the MessageChannel port
13460
+ * @param event
13559
13461
  */
13560
- async acquireTokenRedirect(request, rootMeasurement) {
13561
- this.logger.trace("NativeInteractionClient - acquireTokenRedirect called.");
13562
- const { ...remainingParameters } = request;
13563
- delete remainingParameters.onRedirectNavigate;
13564
- const nativeRequest = await this.initializeNativeRequest(remainingParameters);
13565
- const messageBody = {
13566
- method: NativeExtensionMethod.GetToken,
13567
- request: nativeRequest,
13568
- };
13462
+ onChannelMessage(event) {
13463
+ this.logger.trace("NativeMessageHandler - onChannelMessage called.");
13464
+ const request = event.data;
13465
+ const resolver = this.resolvers.get(request.responseId);
13466
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13569
13467
  try {
13570
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13571
- this.validateNativeResponse(response);
13572
- }
13573
- catch (e) {
13574
- // Only throw fatal errors here to allow application to fallback to regular redirect. Otherwise proceed and the error will be thrown in handleRedirectPromise
13575
- if (e instanceof NativeAuthError) {
13576
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13577
- serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13578
- if (isFatalNativeAuthError(e)) {
13579
- throw e;
13468
+ const method = request.body.method;
13469
+ if (method === NativeExtensionMethod.Response) {
13470
+ if (!resolver) {
13471
+ return;
13472
+ }
13473
+ const response = request.body.response;
13474
+ this.logger.trace("NativeMessageHandler - Received response from browser extension");
13475
+ this.logger.tracePii(`NativeMessageHandler - Received response from browser extension: ${JSON.stringify(response)}`);
13476
+ if (response.status !== "Success") {
13477
+ resolver.reject(createNativeAuthError(response.code, response.description, response.ext));
13478
+ }
13479
+ else if (response.result) {
13480
+ if (response.result["code"] &&
13481
+ response.result["description"]) {
13482
+ resolver.reject(createNativeAuthError(response.result["code"], response.result["description"], response.result["ext"]));
13483
+ }
13484
+ else {
13485
+ resolver.resolve(response.result);
13486
+ }
13487
+ }
13488
+ else {
13489
+ throw createAuthError(unexpectedError, "Event does not contain result.");
13580
13490
  }
13491
+ this.resolvers.delete(request.responseId);
13492
+ }
13493
+ else if (method === NativeExtensionMethod.HandshakeResponse) {
13494
+ if (!handshakeResolver) {
13495
+ this.logger.trace(`NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`);
13496
+ return;
13497
+ }
13498
+ clearTimeout(this.timeoutId); // Clear setTimeout
13499
+ window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
13500
+ this.extensionId = request.extensionId;
13501
+ this.extensionVersion = request.body.version;
13502
+ this.logger.verbose(`NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`);
13503
+ this.handshakeEvent.end({
13504
+ extensionInstalled: true,
13505
+ success: true,
13506
+ });
13507
+ handshakeResolver.resolve();
13508
+ this.handshakeResolvers.delete(request.responseId);
13509
+ }
13510
+ // Do nothing if method is not Response or HandshakeResponse
13511
+ }
13512
+ catch (err) {
13513
+ this.logger.error("Error parsing response from WAM Extension");
13514
+ this.logger.errorPii(`Error parsing response from WAM Extension: ${err}`);
13515
+ this.logger.errorPii(`Unable to parse ${event}`);
13516
+ if (resolver) {
13517
+ resolver.reject(err);
13518
+ }
13519
+ else if (handshakeResolver) {
13520
+ handshakeResolver.reject(err);
13581
13521
  }
13582
13522
  }
13583
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, JSON.stringify(nativeRequest), true);
13584
- const navigationOptions = {
13585
- apiId: ApiId.acquireTokenRedirect,
13586
- timeout: this.config.system.redirectNavigationTimeout,
13587
- noHistory: false,
13588
- };
13589
- const redirectUri = this.config.auth.navigateToLoginRequestUrl
13590
- ? window.location.href
13591
- : this.getRedirectUri(request.redirectUri);
13592
- rootMeasurement.end({ success: true });
13593
- await this.navigationClient.navigateExternal(redirectUri, navigationOptions); // Need to treat this as external to ensure handleRedirectPromise is run again
13594
13523
  }
13595
13524
  /**
13596
- * If the previous page called native platform for a token using redirect APIs, send the same request again and return the response
13597
- * @param performanceClient {IPerformanceClient?}
13598
- * @param correlationId {string?} correlation identifier
13525
+ * Returns the Id for the browser extension this handler is communicating with
13526
+ * @returns
13599
13527
  */
13600
- async handleRedirectPromise(performanceClient, correlationId) {
13601
- this.logger.trace("NativeInteractionClient - handleRedirectPromise called.");
13602
- if (!this.browserStorage.isInteractionInProgress(true)) {
13603
- this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
13604
- return null;
13605
- }
13606
- // remove prompt from the request to prevent WAM from prompting twice
13607
- const cachedRequest = this.browserStorage.getCachedNativeRequest();
13608
- if (!cachedRequest) {
13609
- this.logger.verbose("NativeInteractionClient - handleRedirectPromise called but there is no cached request, returning null.");
13610
- if (performanceClient && correlationId) {
13611
- performanceClient?.addFields({ errorCode: "no_cached_request" }, correlationId);
13612
- }
13613
- return null;
13614
- }
13615
- const { prompt, ...request } = cachedRequest;
13616
- if (prompt) {
13617
- 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.");
13618
- }
13619
- this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
13620
- const messageBody = {
13621
- method: NativeExtensionMethod.GetToken,
13622
- request: request,
13623
- };
13624
- const reqTimestamp = nowSeconds();
13625
- try {
13626
- this.logger.verbose("NativeInteractionClient - handleRedirectPromise sending message to native broker.");
13627
- const response = await this.nativeMessageHandler.sendMessage(messageBody);
13628
- this.validateNativeResponse(response);
13629
- const result = this.handleNativeResponse(response, request, reqTimestamp);
13630
- this.browserStorage.setInteractionInProgress(false);
13631
- const res = await result;
13632
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13633
- serverTelemetryManager.clearNativeBrokerErrorCode();
13634
- return res;
13635
- }
13636
- catch (e) {
13637
- this.browserStorage.setInteractionInProgress(false);
13638
- throw e;
13639
- }
13528
+ getExtensionId() {
13529
+ return this.extensionId;
13640
13530
  }
13641
13531
  /**
13642
- * Logout from native platform via browser extension
13643
- * @param request
13532
+ * Returns the version for the browser extension this handler is communicating with
13533
+ * @returns
13644
13534
  */
13645
- logout() {
13646
- this.logger.trace("NativeInteractionClient - logout called.");
13647
- return Promise.reject("Logout not implemented yet");
13535
+ getExtensionVersion() {
13536
+ return this.extensionVersion;
13648
13537
  }
13649
13538
  /**
13650
- * Transform response from native platform into AuthenticationResult object which will be returned to the end user
13651
- * @param response
13652
- * @param request
13653
- * @param reqTimestamp
13539
+ * Returns boolean indicating whether or not the request should attempt to use native broker
13540
+ * @param logger
13541
+ * @param config
13542
+ * @param nativeExtensionProvider
13543
+ * @param authenticationScheme
13654
13544
  */
13655
- async handleNativeResponse(response, request, reqTimestamp) {
13656
- this.logger.trace("NativeInteractionClient - handleNativeResponse called.");
13657
- // generate identifiers
13658
- const idTokenClaims = extractTokenClaims(response.id_token, base64Decode);
13659
- const homeAccountIdentifier = this.createHomeAccountIdentifier(response, idTokenClaims);
13660
- const cachedhomeAccountId = this.browserStorage.getAccountInfoFilteredBy({
13661
- nativeAccountId: request.accountId,
13662
- })?.homeAccountId;
13663
- // add exception for double brokering, please note this is temporary and will be fortified in future
13664
- if (request.extraParameters?.child_client_id &&
13665
- response.account.id !== request.accountId) {
13666
- this.logger.info("handleNativeServerResponse: Double broker flow detected, ignoring accountId mismatch");
13545
+ static isPlatformBrokerAvailable(config, logger, nativeExtensionProvider, authenticationScheme) {
13546
+ logger.trace("isPlatformBrokerAvailable called");
13547
+ if (!config.system.allowPlatformBroker) {
13548
+ logger.trace("isPlatformBrokerAvailable: allowPlatformBroker is not enabled, returning false");
13549
+ // Developer disabled WAM
13550
+ return false;
13667
13551
  }
13668
- else if (homeAccountIdentifier !== cachedhomeAccountId &&
13669
- response.account.id !== request.accountId) {
13670
- // 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
13671
- throw createNativeAuthError(userSwitch);
13552
+ if (!nativeExtensionProvider) {
13553
+ logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
13554
+ // Extension is not available
13555
+ return false;
13672
13556
  }
13673
- // Get the preferred_cache domain for the given authority
13674
- const authority = await this.getDiscoveredAuthority({
13675
- requestAuthority: request.authority,
13676
- });
13677
- const baseAccount = buildAccountToCache(this.browserStorage, authority, homeAccountIdentifier, base64Decode, idTokenClaims, response.client_info, undefined, // environment
13678
- idTokenClaims.tid, undefined, // auth code payload
13679
- response.account.id, this.logger);
13680
- // Ensure expires_in is in number format
13681
- response.expires_in = Number(response.expires_in);
13682
- // generate authenticationResult
13683
- const result = await this.generateAuthenticationResult(response, request, idTokenClaims, baseAccount, authority.canonicalAuthority, reqTimestamp);
13684
- // cache accounts and tokens in the appropriate storage
13685
- await this.cacheAccount(baseAccount);
13686
- await this.cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, response.access_token, result.tenantId, reqTimestamp);
13687
- return result;
13557
+ if (authenticationScheme) {
13558
+ switch (authenticationScheme) {
13559
+ case AuthenticationScheme.BEARER:
13560
+ case AuthenticationScheme.POP:
13561
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is supported, returning true");
13562
+ return true;
13563
+ default:
13564
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is not supported, returning false");
13565
+ return false;
13566
+ }
13567
+ }
13568
+ return true;
13688
13569
  }
13689
- /**
13690
- * creates an homeAccountIdentifier for the account
13691
- * @param response
13692
- * @param idTokenObj
13693
- * @returns
13694
- */
13695
- createHomeAccountIdentifier(response, idTokenClaims) {
13696
- // Save account in browser storage
13697
- const homeAccountIdentifier = AccountEntity.generateHomeAccountId(response.client_info || Constants.EMPTY_STRING, AuthorityType.Default, this.logger, this.browserCrypto, idTokenClaims);
13698
- return homeAccountIdentifier;
13570
+ }
13571
+
13572
+ /*
13573
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13574
+ * Licensed under the MIT License.
13575
+ */
13576
+ /**
13577
+ * Extracts the BrowserStateObject from the state string.
13578
+ * @param browserCrypto
13579
+ * @param state
13580
+ */
13581
+ function extractBrowserRequestState(browserCrypto, state) {
13582
+ if (!state) {
13583
+ return null;
13699
13584
  }
13700
- /**
13701
- * Helper to generate scopes
13702
- * @param response
13703
- * @param request
13704
- * @returns
13705
- */
13706
- generateScopes(response, request) {
13707
- return response.scope
13708
- ? ScopeSet.fromString(response.scope)
13709
- : ScopeSet.fromString(request.scope);
13585
+ try {
13586
+ const requestStateObj = ProtocolUtils.parseRequestState(browserCrypto, state);
13587
+ return requestStateObj.libraryState.meta;
13588
+ }
13589
+ catch (e) {
13590
+ throw createClientAuthError(invalidState);
13591
+ }
13592
+ }
13593
+
13594
+ /*
13595
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13596
+ * Licensed under the MIT License.
13597
+ */
13598
+ function deserializeResponse(responseString, responseLocation, logger) {
13599
+ // Deserialize hash fragment response parameters.
13600
+ const serverParams = getDeserializedResponse(responseString);
13601
+ if (!serverParams) {
13602
+ if (!stripLeadingHashOrQuery(responseString)) {
13603
+ // Hash or Query string is empty
13604
+ 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.`);
13605
+ throw createBrowserAuthError(hashEmptyError);
13606
+ }
13607
+ else {
13608
+ 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.`);
13609
+ logger.errorPii(`The ${responseLocation} detected is: ${responseString}`);
13610
+ throw createBrowserAuthError(hashDoesNotContainKnownProperties);
13611
+ }
13612
+ }
13613
+ return serverParams;
13614
+ }
13615
+ /**
13616
+ * Returns the interaction type that the response object belongs to
13617
+ */
13618
+ function validateInteractionType(response, browserCrypto, interactionType) {
13619
+ if (!response.state) {
13620
+ throw createBrowserAuthError(noStateInHash);
13621
+ }
13622
+ const platformStateObj = extractBrowserRequestState(browserCrypto, response.state);
13623
+ if (!platformStateObj) {
13624
+ throw createBrowserAuthError(unableToParseState);
13625
+ }
13626
+ if (platformStateObj.interactionType !== interactionType) {
13627
+ throw createBrowserAuthError(stateInteractionTypeMismatch);
13628
+ }
13629
+ }
13630
+
13631
+ /*
13632
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13633
+ * Licensed under the MIT License.
13634
+ */
13635
+ /**
13636
+ * Abstract class which defines operations for a browser interaction handling class.
13637
+ */
13638
+ class InteractionHandler {
13639
+ constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
13640
+ this.authModule = authCodeModule;
13641
+ this.browserStorage = storageImpl;
13642
+ this.authCodeRequest = authCodeRequest;
13643
+ this.logger = logger;
13644
+ this.performanceClient = performanceClient;
13710
13645
  }
13711
13646
  /**
13712
- * If PoP token is requesred, records the PoP token if returned from the WAM, else generates one in the browser
13713
- * @param request
13714
- * @param response
13647
+ * Function to handle response parameters from hash.
13648
+ * @param locationHash
13715
13649
  */
13716
- async generatePopAccessToken(response, request) {
13717
- if (request.tokenType === AuthenticationScheme.POP &&
13718
- request.signPopToken) {
13719
- /**
13720
- * This code prioritizes SHR returned from the native layer. In case of error/SHR not calculated from WAM and the AT
13721
- * is still received, SHR is calculated locally
13722
- */
13723
- // Check if native layer returned an SHR token
13724
- if (response.shr) {
13725
- this.logger.trace("handleNativeServerResponse: SHR is enabled in native layer");
13726
- return response.shr;
13650
+ async handleCodeResponse(response, request) {
13651
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponse, request.correlationId);
13652
+ let authCodeResponse;
13653
+ try {
13654
+ authCodeResponse = getAuthorizationCodePayload(response, request.state);
13655
+ }
13656
+ catch (e) {
13657
+ if (e instanceof ServerError &&
13658
+ e.subError === userCancelled) {
13659
+ // Translate server error caused by user closing native prompt to corresponding first class MSAL error
13660
+ throw createBrowserAuthError(userCancelled);
13727
13661
  }
13728
- // Generate SHR in msal js if WAM does not compute it when POP is enabled
13729
- const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
13730
- const shrParameters = {
13731
- resourceRequestMethod: request.resourceRequestMethod,
13732
- resourceRequestUri: request.resourceRequestUri,
13733
- shrClaims: request.shrClaims,
13734
- shrNonce: request.shrNonce,
13735
- };
13736
- /**
13737
- * KeyID must be present in the native request from when the PoP key was generated in order for
13738
- * PopTokenGenerator to query the full key for signing
13739
- */
13740
- if (!request.keyId) {
13741
- throw createClientAuthError(keyIdMissing);
13662
+ else {
13663
+ throw e;
13742
13664
  }
13743
- return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
13665
+ }
13666
+ return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
13667
+ }
13668
+ /**
13669
+ * Process auth code response from AAD
13670
+ * @param authCodeResponse
13671
+ * @param state
13672
+ * @param authority
13673
+ * @param networkModule
13674
+ * @returns
13675
+ */
13676
+ async handleCodeResponseFromServer(authCodeResponse, request, validateNonce = true) {
13677
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponseFromServer, request.correlationId);
13678
+ this.logger.trace("InteractionHandler.handleCodeResponseFromServer called");
13679
+ // Assign code to request
13680
+ this.authCodeRequest.code = authCodeResponse.code;
13681
+ // Check for new cloud instance
13682
+ if (authCodeResponse.cloud_instance_host_name) {
13683
+ await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, request.correlationId)(authCodeResponse.cloud_instance_host_name, request.correlationId);
13684
+ }
13685
+ // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
13686
+ if (validateNonce) {
13687
+ // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
13688
+ authCodeResponse.nonce = request.nonce || undefined;
13689
+ }
13690
+ authCodeResponse.state = request.state;
13691
+ // Add CCS parameters if available
13692
+ if (authCodeResponse.client_info) {
13693
+ this.authCodeRequest.clientInfo = authCodeResponse.client_info;
13744
13694
  }
13745
13695
  else {
13746
- return response.access_token;
13696
+ const ccsCred = this.createCcsCredentials(request);
13697
+ if (ccsCred) {
13698
+ this.authCodeRequest.ccsCredential = ccsCred;
13699
+ }
13747
13700
  }
13701
+ // Acquire token with retrieved code.
13702
+ const tokenResponse = (await invokeAsync(this.authModule.acquireToken.bind(this.authModule), PerformanceEvents.AuthClientAcquireToken, this.logger, this.performanceClient, request.correlationId)(this.authCodeRequest, authCodeResponse));
13703
+ return tokenResponse;
13748
13704
  }
13749
13705
  /**
13750
- * Generates authentication result
13751
- * @param response
13752
- * @param request
13753
- * @param idTokenObj
13754
- * @param accountEntity
13755
- * @param authority
13756
- * @param reqTimestamp
13757
- * @returns
13706
+ * Build ccs creds if available
13758
13707
  */
13759
- async generateAuthenticationResult(response, request, idTokenClaims, accountEntity, authority, reqTimestamp) {
13760
- // Add Native Broker fields to Telemetry
13761
- const mats = this.addTelemetryFromNativeResponse(response);
13762
- // If scopes not returned in server response, use request scopes
13763
- const responseScopes = response.scope
13764
- ? ScopeSet.fromString(response.scope)
13765
- : ScopeSet.fromString(request.scope);
13766
- const accountProperties = response.account.properties || {};
13767
- const uid = accountProperties["UID"] ||
13768
- idTokenClaims.oid ||
13769
- idTokenClaims.sub ||
13770
- Constants.EMPTY_STRING;
13771
- const tid = accountProperties["TenantId"] ||
13772
- idTokenClaims.tid ||
13773
- Constants.EMPTY_STRING;
13774
- const accountInfo = updateAccountTenantProfileData(accountEntity.getAccountInfo(), undefined, // tenantProfile optional
13775
- idTokenClaims, response.id_token);
13776
- /**
13777
- * In pairwise broker flows, this check prevents the broker's native account id
13778
- * from being returned over the embedded app's account id.
13779
- */
13780
- if (accountInfo.nativeAccountId !== response.account.id) {
13781
- accountInfo.nativeAccountId = response.account.id;
13708
+ createCcsCredentials(request) {
13709
+ if (request.account) {
13710
+ return {
13711
+ credential: request.account.homeAccountId,
13712
+ type: CcsCredentialType.HOME_ACCOUNT_ID,
13713
+ };
13782
13714
  }
13783
- // generate PoP token as needed
13784
- const responseAccessToken = await this.generatePopAccessToken(response, request);
13785
- const tokenType = request.tokenType === AuthenticationScheme.POP
13786
- ? AuthenticationScheme.POP
13787
- : AuthenticationScheme.BEARER;
13788
- const result = {
13789
- authority: authority,
13790
- uniqueId: uid,
13791
- tenantId: tid,
13792
- scopes: responseScopes.asArray(),
13793
- account: accountInfo,
13794
- idToken: response.id_token,
13795
- idTokenClaims: idTokenClaims,
13796
- accessToken: responseAccessToken,
13797
- fromCache: mats ? this.isResponseFromCache(mats) : false,
13798
- // Request timestamp and NativeResponse expires_in are in seconds, converting to Date for AuthenticationResult
13799
- expiresOn: toDateFromSeconds(reqTimestamp + response.expires_in),
13800
- tokenType: tokenType,
13801
- correlationId: this.correlationId,
13802
- state: response.state,
13803
- fromNativeBroker: true,
13804
- };
13805
- return result;
13715
+ else if (request.loginHint) {
13716
+ return {
13717
+ credential: request.loginHint,
13718
+ type: CcsCredentialType.UPN,
13719
+ };
13720
+ }
13721
+ return null;
13806
13722
  }
13723
+ }
13724
+
13725
+ /*
13726
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13727
+ * Licensed under the MIT License.
13728
+ */
13729
+ class SilentCacheClient extends StandardInteractionClient {
13807
13730
  /**
13808
- * cache the account entity in browser storage
13809
- * @param accountEntity
13731
+ * Returns unexpired tokens from the cache, if available
13732
+ * @param silentRequest
13810
13733
  */
13811
- async cacheAccount(accountEntity) {
13812
- // Store the account info and hence `nativeAccountId` in browser cache
13813
- await this.browserStorage.setAccount(accountEntity, this.correlationId);
13814
- // Remove any existing cached tokens for this account in browser storage
13815
- this.browserStorage.removeAccountContext(accountEntity).catch((e) => {
13816
- this.logger.error(`Error occurred while removing account context from browser storage. ${e}`);
13734
+ async acquireToken(silentRequest) {
13735
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentCacheClientAcquireToken, silentRequest.correlationId);
13736
+ // Telemetry manager only used to increment cacheHits here
13737
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent_silentFlow);
13738
+ const clientConfig = await invokeAsync(this.getClientConfiguration.bind(this), PerformanceEvents.StandardInteractionClientGetClientConfiguration, this.logger, this.performanceClient, this.correlationId)({
13739
+ serverTelemetryManager,
13740
+ requestAuthority: silentRequest.authority,
13741
+ requestAzureCloudOptions: silentRequest.azureCloudOptions,
13742
+ account: silentRequest.account,
13817
13743
  });
13744
+ const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13745
+ this.logger.verbose("Silent auth client created");
13746
+ try {
13747
+ const response = await invokeAsync(silentAuthClient.acquireCachedToken.bind(silentAuthClient), PerformanceEvents.SilentFlowClientAcquireCachedToken, this.logger, this.performanceClient, silentRequest.correlationId)(silentRequest);
13748
+ const authResponse = response[0];
13749
+ this.performanceClient.addFields({
13750
+ fromCache: true,
13751
+ }, silentRequest.correlationId);
13752
+ return authResponse;
13753
+ }
13754
+ catch (error) {
13755
+ if (error instanceof BrowserAuthError &&
13756
+ error.errorCode === cryptoKeyNotFound) {
13757
+ this.logger.verbose("Signing keypair for bound access token not found. Refreshing bound access token and generating a new crypto keypair.");
13758
+ }
13759
+ throw error;
13760
+ }
13818
13761
  }
13819
13762
  /**
13820
- * Stores the access_token and id_token in inmemory storage
13821
- * @param response
13822
- * @param request
13823
- * @param homeAccountIdentifier
13824
- * @param idTokenObj
13825
- * @param responseAccessToken
13826
- * @param tenantId
13827
- * @param reqTimestamp
13763
+ * API to silenty clear the browser cache.
13764
+ * @param logoutRequest
13828
13765
  */
13829
- cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, responseAccessToken, tenantId, reqTimestamp) {
13830
- const cachedIdToken = createIdTokenEntity(homeAccountIdentifier, request.authority, response.id_token || "", request.clientId, idTokenClaims.tid || "");
13831
- // cache accessToken in inmemory storage
13832
- const expiresIn = request.tokenType === AuthenticationScheme.POP
13833
- ? Constants.SHR_NONCE_VALIDITY
13834
- : (typeof response.expires_in === "string"
13835
- ? parseInt(response.expires_in, 10)
13836
- : response.expires_in) || 0;
13837
- const tokenExpirationSeconds = reqTimestamp + expiresIn;
13838
- const responseScopes = this.generateScopes(response, request);
13839
- const cachedAccessToken = createAccessTokenEntity(homeAccountIdentifier, request.authority, responseAccessToken, request.clientId, idTokenClaims.tid || tenantId, responseScopes.printScopes(), tokenExpirationSeconds, 0, base64Decode, undefined, request.tokenType, undefined, request.keyId);
13840
- const nativeCacheRecord = {
13841
- idToken: cachedIdToken,
13842
- accessToken: cachedAccessToken,
13843
- };
13844
- return this.nativeStorageManager.saveCacheRecord(nativeCacheRecord, this.correlationId, request.storeInCache);
13766
+ logout(logoutRequest) {
13767
+ this.logger.verbose("logoutRedirect called");
13768
+ const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
13769
+ return this.clearCacheOnLogout(validLogoutRequest?.account);
13845
13770
  }
13846
- addTelemetryFromNativeResponse(response) {
13847
- const mats = this.getMATSFromResponse(response);
13848
- if (!mats) {
13849
- return null;
13850
- }
13851
- this.performanceClient.addFields({
13852
- extensionId: this.nativeMessageHandler.getExtensionId(),
13771
+ }
13772
+
13773
+ /*
13774
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13775
+ * Licensed under the MIT License.
13776
+ */
13777
+ class NativeInteractionClient extends BaseInteractionClient {
13778
+ constructor(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, provider, accountId, nativeStorageImpl, correlationId) {
13779
+ super(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13780
+ this.apiId = apiId;
13781
+ this.accountId = accountId;
13782
+ this.nativeMessageHandler = provider;
13783
+ this.nativeStorageManager = nativeStorageImpl;
13784
+ this.silentCacheClient = new SilentCacheClient(config, this.nativeStorageManager, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13785
+ const extensionName = this.nativeMessageHandler.getExtensionId() ===
13786
+ NativeConstants.PREFERRED_EXTENSION_ID
13787
+ ? "chrome"
13788
+ : this.nativeMessageHandler.getExtensionId()?.length
13789
+ ? "unknown"
13790
+ : undefined;
13791
+ this.skus = ServerTelemetryManager.makeExtraSkuString({
13792
+ libraryName: BrowserConstants.MSAL_SKU,
13793
+ libraryVersion: version,
13794
+ extensionName: extensionName,
13853
13795
  extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
13854
- matsBrokerVersion: mats.broker_version,
13855
- matsAccountJoinOnStart: mats.account_join_on_start,
13856
- matsAccountJoinOnEnd: mats.account_join_on_end,
13857
- matsDeviceJoin: mats.device_join,
13858
- matsPromptBehavior: mats.prompt_behavior,
13859
- matsApiErrorCode: mats.api_error_code,
13860
- matsUiVisible: mats.ui_visible,
13861
- matsSilentCode: mats.silent_code,
13862
- matsSilentBiSubCode: mats.silent_bi_sub_code,
13863
- matsSilentMessage: mats.silent_message,
13864
- matsSilentStatus: mats.silent_status,
13865
- matsHttpStatus: mats.http_status,
13866
- matsHttpEventCount: mats.http_event_count,
13867
- }, this.correlationId);
13868
- return mats;
13796
+ });
13869
13797
  }
13870
13798
  /**
13871
- * Validates native platform response before processing
13872
- * @param response
13799
+ * Adds SKUs to request extra query parameters
13800
+ * @param request {NativeTokenRequest}
13801
+ * @private
13873
13802
  */
13874
- validateNativeResponse(response) {
13875
- if (response.hasOwnProperty("access_token") &&
13876
- response.hasOwnProperty("id_token") &&
13877
- response.hasOwnProperty("client_info") &&
13878
- response.hasOwnProperty("account") &&
13879
- response.hasOwnProperty("scope") &&
13880
- response.hasOwnProperty("expires_in")) {
13881
- return response;
13882
- }
13883
- else {
13884
- throw createAuthError(unexpectedError, "Response missing expected properties.");
13885
- }
13803
+ addRequestSKUs(request) {
13804
+ request.extraParameters = {
13805
+ ...request.extraParameters,
13806
+ [X_CLIENT_EXTRA_SKU]: this.skus,
13807
+ };
13886
13808
  }
13887
13809
  /**
13888
- * Gets MATS telemetry from native response
13889
- * @param response
13890
- * @returns
13810
+ * Acquire token from native platform via browser extension
13811
+ * @param request
13891
13812
  */
13892
- getMATSFromResponse(response) {
13893
- if (response.properties.MATS) {
13813
+ async acquireToken(request, cacheLookupPolicy) {
13814
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13815
+ this.logger.trace("NativeInteractionClient - acquireToken called.");
13816
+ // start the perf measurement
13817
+ const nativeATMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13818
+ const reqTimestamp = nowSeconds();
13819
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13820
+ try {
13821
+ // initialize native request
13822
+ const nativeRequest = await this.initializeNativeRequest(request);
13823
+ // check if the tokens can be retrieved from internal cache
13894
13824
  try {
13895
- return JSON.parse(response.properties.MATS);
13825
+ const result = await this.acquireTokensFromCache(this.accountId, nativeRequest);
13826
+ nativeATMeasurement.end({
13827
+ success: true,
13828
+ isNativeBroker: false,
13829
+ fromCache: true,
13830
+ });
13831
+ return result;
13896
13832
  }
13897
13833
  catch (e) {
13898
- this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
13834
+ if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
13835
+ this.logger.info("MSAL internal Cache does not contain tokens, return error as per cache policy");
13836
+ throw e;
13837
+ }
13838
+ // continue with a native call for any and all errors
13839
+ this.logger.info("MSAL internal Cache does not contain tokens, proceed to make a native call");
13899
13840
  }
13841
+ const { ...nativeTokenRequest } = nativeRequest;
13842
+ // fall back to native calls
13843
+ const messageBody = {
13844
+ method: NativeExtensionMethod.GetToken,
13845
+ request: nativeTokenRequest,
13846
+ };
13847
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13848
+ const validatedResponse = this.validateNativeResponse(response);
13849
+ return await this.handleNativeResponse(validatedResponse, nativeRequest, reqTimestamp)
13850
+ .then((result) => {
13851
+ nativeATMeasurement.end({
13852
+ success: true,
13853
+ isNativeBroker: true,
13854
+ requestId: result.requestId,
13855
+ });
13856
+ serverTelemetryManager.clearNativeBrokerErrorCode();
13857
+ return result;
13858
+ })
13859
+ .catch((error) => {
13860
+ nativeATMeasurement.end({
13861
+ success: false,
13862
+ errorCode: error.errorCode,
13863
+ subErrorCode: error.subError,
13864
+ isNativeBroker: true,
13865
+ });
13866
+ throw error;
13867
+ });
13900
13868
  }
13901
- return null;
13902
- }
13903
- /**
13904
- * Returns whether or not response came from native cache
13905
- * @param response
13906
- * @returns
13907
- */
13908
- isResponseFromCache(mats) {
13909
- if (typeof mats.is_cached === "undefined") {
13910
- this.logger.verbose("NativeInteractionClient - MATS telemetry does not contain field indicating if response was served from cache. Returning false.");
13911
- return false;
13869
+ catch (e) {
13870
+ if (e instanceof NativeAuthError) {
13871
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13872
+ }
13873
+ throw e;
13912
13874
  }
13913
- return !!mats.is_cached;
13914
13875
  }
13915
13876
  /**
13916
- * Translates developer provided request object into NativeRequest object
13877
+ * Creates silent flow request
13917
13878
  * @param request
13879
+ * @param cachedAccount
13880
+ * @returns CommonSilentFlowRequest
13918
13881
  */
13919
- async initializeNativeRequest(request) {
13920
- this.logger.trace("NativeInteractionClient - initializeNativeRequest called");
13921
- const requestAuthority = request.authority || this.config.auth.authority;
13922
- if (request.account) {
13923
- // validate authority
13924
- await this.getDiscoveredAuthority({
13925
- requestAuthority,
13926
- requestAzureCloudOptions: request.azureCloudOptions,
13927
- account: request.account,
13928
- });
13929
- }
13930
- const canonicalAuthority = new UrlString(requestAuthority);
13931
- canonicalAuthority.validateAsUri();
13932
- // 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.
13933
- const { scopes, ...remainingProperties } = request;
13934
- const scopeSet = new ScopeSet(scopes || []);
13935
- scopeSet.appendScopes(OIDC_DEFAULT_SCOPES);
13936
- const getPrompt = () => {
13937
- // If request is silent, prompt is always none
13938
- switch (this.apiId) {
13939
- case ApiId.ssoSilent:
13940
- case ApiId.acquireTokenSilent_silentFlow:
13941
- this.logger.trace("initializeNativeRequest: silent request sets prompt to none");
13942
- return PromptValue.NONE;
13943
- }
13944
- // Prompt not provided, request may proceed and native broker decides if it needs to prompt
13945
- if (!request.prompt) {
13946
- this.logger.trace("initializeNativeRequest: prompt was not provided");
13947
- return undefined;
13948
- }
13949
- // If request is interactive, check if prompt provided is allowed to go directly to native broker
13950
- switch (request.prompt) {
13951
- case PromptValue.NONE:
13952
- case PromptValue.CONSENT:
13953
- case PromptValue.LOGIN:
13954
- this.logger.trace("initializeNativeRequest: prompt is compatible with native flow");
13955
- return request.prompt;
13956
- default:
13957
- this.logger.trace(`initializeNativeRequest: prompt = ${request.prompt} is not compatible with native flow`);
13958
- throw createBrowserAuthError(nativePromptNotSupported);
13959
- }
13960
- };
13961
- const validatedRequest = {
13962
- ...remainingProperties,
13963
- accountId: this.accountId,
13964
- clientId: this.config.auth.clientId,
13965
- authority: canonicalAuthority.urlString,
13966
- scope: scopeSet.printScopes(),
13967
- redirectUri: this.getRedirectUri(request.redirectUri),
13968
- prompt: getPrompt(),
13882
+ createSilentCacheRequest(request, cachedAccount) {
13883
+ return {
13884
+ authority: request.authority,
13969
13885
  correlationId: this.correlationId,
13970
- tokenType: request.authenticationScheme,
13971
- windowTitleSubstring: document.title,
13972
- extraParameters: {
13973
- ...request.extraQueryParameters,
13974
- ...request.tokenQueryParameters,
13975
- },
13976
- extendedExpiryToken: false,
13977
- keyId: request.popKid,
13886
+ scopes: ScopeSet.fromString(request.scope).asArray(),
13887
+ account: cachedAccount,
13888
+ forceRefresh: false,
13978
13889
  };
13979
- // Check for PoP token requests: signPopToken should only be set to true if popKid is not set
13980
- if (validatedRequest.signPopToken && !!request.popKid) {
13981
- throw createBrowserAuthError(invalidPopTokenRequest);
13982
- }
13983
- this.handleExtraBrokerParams(validatedRequest);
13984
- validatedRequest.extraParameters =
13985
- validatedRequest.extraParameters || {};
13986
- validatedRequest.extraParameters.telemetry =
13987
- NativeConstants.MATS_TELEMETRY;
13988
- if (request.authenticationScheme === AuthenticationScheme.POP) {
13989
- // add POP request type
13990
- const shrParameters = {
13991
- resourceRequestUri: request.resourceRequestUri,
13992
- resourceRequestMethod: request.resourceRequestMethod,
13993
- shrClaims: request.shrClaims,
13994
- shrNonce: request.shrNonce,
13995
- };
13996
- const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
13997
- // generate reqCnf if not provided in the request
13998
- let reqCnfData;
13999
- if (!validatedRequest.keyId) {
14000
- const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, this.logger, this.performanceClient, request.correlationId)(shrParameters, this.logger);
14001
- reqCnfData = generatedReqCnfData.reqCnfString;
14002
- validatedRequest.keyId = generatedReqCnfData.kid;
14003
- validatedRequest.signPopToken = true;
14004
- }
14005
- else {
14006
- reqCnfData = this.browserCrypto.base64UrlEncode(JSON.stringify({ kid: validatedRequest.keyId }));
14007
- validatedRequest.signPopToken = false;
14008
- }
14009
- // SPAs require whole string to be passed to broker
14010
- validatedRequest.reqCnf = reqCnfData;
14011
- }
14012
- this.addRequestSKUs(validatedRequest);
14013
- return validatedRequest;
14014
13890
  }
14015
13891
  /**
14016
- * Handles extra broker request parameters
14017
- * @param request {NativeTokenRequest}
14018
- * @private
13892
+ * Fetches the tokens from the cache if un-expired
13893
+ * @param nativeAccountId
13894
+ * @param request
13895
+ * @returns authenticationResult
14019
13896
  */
14020
- handleExtraBrokerParams(request) {
14021
- const hasExtraBrokerParams = request.extraParameters &&
14022
- request.extraParameters.hasOwnProperty(BROKER_CLIENT_ID) &&
14023
- request.extraParameters.hasOwnProperty(BROKER_REDIRECT_URI) &&
14024
- request.extraParameters.hasOwnProperty(CLIENT_ID);
14025
- if (!request.embeddedClientId && !hasExtraBrokerParams) {
14026
- return;
13897
+ async acquireTokensFromCache(nativeAccountId, request) {
13898
+ if (!nativeAccountId) {
13899
+ this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13900
+ throw createClientAuthError(noAccountFound);
14027
13901
  }
14028
- let child_client_id = "";
14029
- const child_redirect_uri = request.redirectUri;
14030
- if (request.embeddedClientId) {
14031
- request.redirectUri = this.config.auth.redirectUri;
14032
- child_client_id = request.embeddedClientId;
13902
+ // fetch the account from browser cache
13903
+ const account = this.browserStorage.getBaseAccountInfo({
13904
+ nativeAccountId,
13905
+ });
13906
+ if (!account) {
13907
+ throw createClientAuthError(noAccountFound);
14033
13908
  }
14034
- else if (request.extraParameters) {
14035
- request.redirectUri =
14036
- request.extraParameters[BROKER_REDIRECT_URI];
14037
- child_client_id =
14038
- request.extraParameters[CLIENT_ID];
13909
+ // leverage silent flow for cached tokens retrieval
13910
+ try {
13911
+ const silentRequest = this.createSilentCacheRequest(request, account);
13912
+ const result = await this.silentCacheClient.acquireToken(silentRequest);
13913
+ const fullAccount = {
13914
+ ...account,
13915
+ idTokenClaims: result?.idTokenClaims,
13916
+ idToken: result?.idToken,
13917
+ };
13918
+ return {
13919
+ ...result,
13920
+ account: fullAccount,
13921
+ };
13922
+ }
13923
+ catch (e) {
13924
+ throw e;
14039
13925
  }
14040
- request.extraParameters = {
14041
- child_client_id,
14042
- child_redirect_uri,
14043
- };
14044
- this.performanceClient?.addFields({
14045
- embeddedClientId: child_client_id,
14046
- embeddedRedirectUri: child_redirect_uri,
14047
- }, request.correlationId);
14048
- }
14049
- }
14050
-
14051
- /*
14052
- * Copyright (c) Microsoft Corporation. All rights reserved.
14053
- * Licensed under the MIT License.
14054
- */
14055
- class NativeMessageHandler {
14056
- constructor(logger, handshakeTimeoutMs, performanceClient, extensionId) {
14057
- this.logger = logger;
14058
- this.handshakeTimeoutMs = handshakeTimeoutMs;
14059
- this.extensionId = extensionId;
14060
- this.resolvers = new Map(); // Used for non-handshake messages
14061
- this.handshakeResolvers = new Map(); // Used for handshake messages
14062
- this.messageChannel = new MessageChannel();
14063
- this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
14064
- this.performanceClient = performanceClient;
14065
- this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
14066
13926
  }
14067
13927
  /**
14068
- * Sends a given message to the extension and resolves with the extension response
14069
- * @param body
13928
+ * Acquires a token from native platform then redirects to the redirectUri instead of returning the response
13929
+ * @param {RedirectRequest} request
13930
+ * @param {InProgressPerformanceEvent} rootMeasurement
14070
13931
  */
14071
- async sendMessage(body) {
14072
- this.logger.trace("NativeMessageHandler - sendMessage called.");
14073
- const req = {
14074
- channel: NativeConstants.CHANNEL_ID,
14075
- extensionId: this.extensionId,
14076
- responseId: createNewGuid(),
14077
- body: body,
13932
+ async acquireTokenRedirect(request, rootMeasurement) {
13933
+ this.logger.trace("NativeInteractionClient - acquireTokenRedirect called.");
13934
+ const { ...remainingParameters } = request;
13935
+ delete remainingParameters.onRedirectNavigate;
13936
+ const nativeRequest = await this.initializeNativeRequest(remainingParameters);
13937
+ const messageBody = {
13938
+ method: NativeExtensionMethod.GetToken,
13939
+ request: nativeRequest,
14078
13940
  };
14079
- this.logger.trace("NativeMessageHandler - Sending request to browser extension");
14080
- this.logger.tracePii(`NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(req)}`);
14081
- this.messageChannel.port1.postMessage(req);
14082
- return new Promise((resolve, reject) => {
14083
- this.resolvers.set(req.responseId, { resolve, reject });
14084
- });
13941
+ try {
13942
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13943
+ this.validateNativeResponse(response);
13944
+ }
13945
+ catch (e) {
13946
+ // Only throw fatal errors here to allow application to fallback to regular redirect. Otherwise proceed and the error will be thrown in handleRedirectPromise
13947
+ if (e instanceof NativeAuthError) {
13948
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13949
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13950
+ if (isFatalNativeAuthError(e)) {
13951
+ throw e;
13952
+ }
13953
+ }
13954
+ }
13955
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, JSON.stringify(nativeRequest), true);
13956
+ const navigationOptions = {
13957
+ apiId: ApiId.acquireTokenRedirect,
13958
+ timeout: this.config.system.redirectNavigationTimeout,
13959
+ noHistory: false,
13960
+ };
13961
+ const redirectUri = this.config.auth.navigateToLoginRequestUrl
13962
+ ? window.location.href
13963
+ : this.getRedirectUri(request.redirectUri);
13964
+ rootMeasurement.end({ success: true });
13965
+ await this.navigationClient.navigateExternal(redirectUri, navigationOptions); // Need to treat this as external to ensure handleRedirectPromise is run again
14085
13966
  }
14086
13967
  /**
14087
- * Returns an instance of the MessageHandler that has successfully established a connection with an extension
14088
- * @param {Logger} logger
14089
- * @param {number} handshakeTimeoutMs
14090
- * @param {IPerformanceClient} performanceClient
14091
- * @param {ICrypto} crypto
13968
+ * If the previous page called native platform for a token using redirect APIs, send the same request again and return the response
13969
+ * @param performanceClient {IPerformanceClient?}
13970
+ * @param correlationId {string?} correlation identifier
14092
13971
  */
14093
- static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
14094
- logger.trace("NativeMessageHandler - createProvider called.");
13972
+ async handleRedirectPromise(performanceClient, correlationId) {
13973
+ this.logger.trace("NativeInteractionClient - handleRedirectPromise called.");
13974
+ if (!this.browserStorage.isInteractionInProgress(true)) {
13975
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
13976
+ return null;
13977
+ }
13978
+ // remove prompt from the request to prevent WAM from prompting twice
13979
+ const cachedRequest = this.browserStorage.getCachedNativeRequest();
13980
+ if (!cachedRequest) {
13981
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise called but there is no cached request, returning null.");
13982
+ if (performanceClient && correlationId) {
13983
+ performanceClient?.addFields({ errorCode: "no_cached_request" }, correlationId);
13984
+ }
13985
+ return null;
13986
+ }
13987
+ const { prompt, ...request } = cachedRequest;
13988
+ if (prompt) {
13989
+ 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.");
13990
+ }
13991
+ this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
13992
+ const messageBody = {
13993
+ method: NativeExtensionMethod.GetToken,
13994
+ request: request,
13995
+ };
13996
+ const reqTimestamp = nowSeconds();
14095
13997
  try {
14096
- const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
14097
- await preferredProvider.sendHandshakeRequest();
14098
- return preferredProvider;
13998
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise sending message to native broker.");
13999
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
14000
+ this.validateNativeResponse(response);
14001
+ const result = this.handleNativeResponse(response, request, reqTimestamp);
14002
+ const res = await result;
14003
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
14004
+ serverTelemetryManager.clearNativeBrokerErrorCode();
14005
+ return res;
14099
14006
  }
14100
14007
  catch (e) {
14101
- // If preferred extension fails for whatever reason, fallback to using any installed extension
14102
- const backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
14103
- await backupProvider.sendHandshakeRequest();
14104
- return backupProvider;
14008
+ throw e;
14105
14009
  }
14106
14010
  }
14107
14011
  /**
14108
- * Send handshake request helper.
14012
+ * Logout from native platform via browser extension
14013
+ * @param request
14109
14014
  */
14110
- async sendHandshakeRequest() {
14111
- this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
14112
- // Register this event listener before sending handshake
14113
- window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
14114
- const req = {
14115
- channel: NativeConstants.CHANNEL_ID,
14116
- extensionId: this.extensionId,
14117
- responseId: createNewGuid(),
14118
- body: {
14119
- method: NativeExtensionMethod.HandshakeRequest,
14120
- },
14121
- };
14122
- this.handshakeEvent.add({
14123
- extensionId: this.extensionId,
14124
- extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
14125
- });
14126
- this.messageChannel.port1.onmessage = (event) => {
14127
- this.onChannelMessage(event);
14128
- };
14129
- window.postMessage(req, window.origin, [this.messageChannel.port2]);
14130
- return new Promise((resolve, reject) => {
14131
- this.handshakeResolvers.set(req.responseId, { resolve, reject });
14132
- this.timeoutId = window.setTimeout(() => {
14133
- /*
14134
- * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
14135
- * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
14136
- */
14137
- window.removeEventListener("message", this.windowListener, false);
14138
- this.messageChannel.port1.close();
14139
- this.messageChannel.port2.close();
14140
- this.handshakeEvent.end({
14141
- extensionHandshakeTimedOut: true,
14142
- success: false,
14143
- });
14144
- reject(createBrowserAuthError(nativeHandshakeTimeout));
14145
- this.handshakeResolvers.delete(req.responseId);
14146
- }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
14147
- });
14015
+ logout() {
14016
+ this.logger.trace("NativeInteractionClient - logout called.");
14017
+ return Promise.reject("Logout not implemented yet");
14148
14018
  }
14149
14019
  /**
14150
- * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
14151
- * @param event
14020
+ * Transform response from native platform into AuthenticationResult object which will be returned to the end user
14021
+ * @param response
14022
+ * @param request
14023
+ * @param reqTimestamp
14152
14024
  */
14153
- onWindowMessage(event) {
14154
- this.logger.trace("NativeMessageHandler - onWindowMessage called");
14155
- // We only accept messages from ourselves
14156
- if (event.source !== window) {
14157
- return;
14158
- }
14159
- const request = event.data;
14160
- if (!request.channel ||
14161
- request.channel !== NativeConstants.CHANNEL_ID) {
14162
- return;
14163
- }
14164
- if (request.extensionId && request.extensionId !== this.extensionId) {
14165
- return;
14025
+ async handleNativeResponse(response, request, reqTimestamp) {
14026
+ this.logger.trace("NativeInteractionClient - handleNativeResponse called.");
14027
+ // generate identifiers
14028
+ const idTokenClaims = extractTokenClaims(response.id_token, base64Decode);
14029
+ const homeAccountIdentifier = this.createHomeAccountIdentifier(response, idTokenClaims);
14030
+ const cachedhomeAccountId = this.browserStorage.getAccountInfoFilteredBy({
14031
+ nativeAccountId: request.accountId,
14032
+ })?.homeAccountId;
14033
+ // add exception for double brokering, please note this is temporary and will be fortified in future
14034
+ if (request.extraParameters?.child_client_id &&
14035
+ response.account.id !== request.accountId) {
14036
+ this.logger.info("handleNativeServerResponse: Double broker flow detected, ignoring accountId mismatch");
14166
14037
  }
14167
- if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
14168
- const handshakeResolver = this.handshakeResolvers.get(request.responseId);
14169
- /*
14170
- * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
14171
- * the proper response.
14172
- */
14173
- if (!handshakeResolver) {
14174
- this.logger.trace(`NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`);
14175
- return;
14176
- }
14177
- // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
14178
- this.logger.verbose(request.extensionId
14179
- ? `Extension with id: ${request.extensionId} not installed`
14180
- : "No extension installed");
14181
- clearTimeout(this.timeoutId);
14182
- this.messageChannel.port1.close();
14183
- this.messageChannel.port2.close();
14184
- window.removeEventListener("message", this.windowListener, false);
14185
- this.handshakeEvent.end({
14186
- success: false,
14187
- extensionInstalled: false,
14188
- });
14189
- handshakeResolver.reject(createBrowserAuthError(nativeExtensionNotInstalled));
14038
+ else if (homeAccountIdentifier !== cachedhomeAccountId &&
14039
+ response.account.id !== request.accountId) {
14040
+ // 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
14041
+ throw createNativeAuthError(userSwitch);
14190
14042
  }
14043
+ // Get the preferred_cache domain for the given authority
14044
+ const authority = await this.getDiscoveredAuthority({
14045
+ requestAuthority: request.authority,
14046
+ });
14047
+ const baseAccount = buildAccountToCache(this.browserStorage, authority, homeAccountIdentifier, base64Decode, idTokenClaims, response.client_info, undefined, // environment
14048
+ idTokenClaims.tid, undefined, // auth code payload
14049
+ response.account.id, this.logger);
14050
+ // Ensure expires_in is in number format
14051
+ response.expires_in = Number(response.expires_in);
14052
+ // generate authenticationResult
14053
+ const result = await this.generateAuthenticationResult(response, request, idTokenClaims, baseAccount, authority.canonicalAuthority, reqTimestamp);
14054
+ // cache accounts and tokens in the appropriate storage
14055
+ await this.cacheAccount(baseAccount);
14056
+ await this.cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, response.access_token, result.tenantId, reqTimestamp);
14057
+ return result;
14191
14058
  }
14192
14059
  /**
14193
- * Invoked when a message is received from the extension on the MessageChannel port
14194
- * @param event
14060
+ * creates an homeAccountIdentifier for the account
14061
+ * @param response
14062
+ * @param idTokenObj
14063
+ * @returns
14195
14064
  */
14196
- onChannelMessage(event) {
14197
- this.logger.trace("NativeMessageHandler - onChannelMessage called.");
14198
- const request = event.data;
14199
- const resolver = this.resolvers.get(request.responseId);
14200
- const handshakeResolver = this.handshakeResolvers.get(request.responseId);
14201
- try {
14202
- const method = request.body.method;
14203
- if (method === NativeExtensionMethod.Response) {
14204
- if (!resolver) {
14205
- return;
14206
- }
14207
- const response = request.body.response;
14208
- this.logger.trace("NativeMessageHandler - Received response from browser extension");
14209
- this.logger.tracePii(`NativeMessageHandler - Received response from browser extension: ${JSON.stringify(response)}`);
14210
- if (response.status !== "Success") {
14211
- resolver.reject(createNativeAuthError(response.code, response.description, response.ext));
14212
- }
14213
- else if (response.result) {
14214
- if (response.result["code"] &&
14215
- response.result["description"]) {
14216
- resolver.reject(createNativeAuthError(response.result["code"], response.result["description"], response.result["ext"]));
14217
- }
14218
- else {
14219
- resolver.resolve(response.result);
14220
- }
14221
- }
14222
- else {
14223
- throw createAuthError(unexpectedError, "Event does not contain result.");
14224
- }
14225
- this.resolvers.delete(request.responseId);
14065
+ createHomeAccountIdentifier(response, idTokenClaims) {
14066
+ // Save account in browser storage
14067
+ const homeAccountIdentifier = AccountEntity.generateHomeAccountId(response.client_info || Constants.EMPTY_STRING, AuthorityType.Default, this.logger, this.browserCrypto, idTokenClaims);
14068
+ return homeAccountIdentifier;
14069
+ }
14070
+ /**
14071
+ * Helper to generate scopes
14072
+ * @param response
14073
+ * @param request
14074
+ * @returns
14075
+ */
14076
+ generateScopes(response, request) {
14077
+ return response.scope
14078
+ ? ScopeSet.fromString(response.scope)
14079
+ : ScopeSet.fromString(request.scope);
14080
+ }
14081
+ /**
14082
+ * If PoP token is requesred, records the PoP token if returned from the WAM, else generates one in the browser
14083
+ * @param request
14084
+ * @param response
14085
+ */
14086
+ async generatePopAccessToken(response, request) {
14087
+ if (request.tokenType === AuthenticationScheme.POP &&
14088
+ request.signPopToken) {
14089
+ /**
14090
+ * This code prioritizes SHR returned from the native layer. In case of error/SHR not calculated from WAM and the AT
14091
+ * is still received, SHR is calculated locally
14092
+ */
14093
+ // Check if native layer returned an SHR token
14094
+ if (response.shr) {
14095
+ this.logger.trace("handleNativeServerResponse: SHR is enabled in native layer");
14096
+ return response.shr;
14226
14097
  }
14227
- else if (method === NativeExtensionMethod.HandshakeResponse) {
14228
- if (!handshakeResolver) {
14229
- this.logger.trace(`NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`);
14230
- return;
14231
- }
14232
- clearTimeout(this.timeoutId); // Clear setTimeout
14233
- window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
14234
- this.extensionId = request.extensionId;
14235
- this.extensionVersion = request.body.version;
14236
- this.logger.verbose(`NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`);
14237
- this.handshakeEvent.end({
14238
- extensionInstalled: true,
14239
- success: true,
14240
- });
14241
- handshakeResolver.resolve();
14242
- this.handshakeResolvers.delete(request.responseId);
14098
+ // Generate SHR in msal js if WAM does not compute it when POP is enabled
14099
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14100
+ const shrParameters = {
14101
+ resourceRequestMethod: request.resourceRequestMethod,
14102
+ resourceRequestUri: request.resourceRequestUri,
14103
+ shrClaims: request.shrClaims,
14104
+ shrNonce: request.shrNonce,
14105
+ };
14106
+ /**
14107
+ * KeyID must be present in the native request from when the PoP key was generated in order for
14108
+ * PopTokenGenerator to query the full key for signing
14109
+ */
14110
+ if (!request.keyId) {
14111
+ throw createClientAuthError(keyIdMissing);
14243
14112
  }
14244
- // Do nothing if method is not Response or HandshakeResponse
14113
+ return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
14245
14114
  }
14246
- catch (err) {
14247
- this.logger.error("Error parsing response from WAM Extension");
14248
- this.logger.errorPii(`Error parsing response from WAM Extension: ${err}`);
14249
- this.logger.errorPii(`Unable to parse ${event}`);
14250
- if (resolver) {
14251
- resolver.reject(err);
14252
- }
14253
- else if (handshakeResolver) {
14254
- handshakeResolver.reject(err);
14255
- }
14115
+ else {
14116
+ return response.access_token;
14256
14117
  }
14257
14118
  }
14258
14119
  /**
14259
- * Returns the Id for the browser extension this handler is communicating with
14120
+ * Generates authentication result
14121
+ * @param response
14122
+ * @param request
14123
+ * @param idTokenObj
14124
+ * @param accountEntity
14125
+ * @param authority
14126
+ * @param reqTimestamp
14260
14127
  * @returns
14261
14128
  */
14262
- getExtensionId() {
14263
- return this.extensionId;
14129
+ async generateAuthenticationResult(response, request, idTokenClaims, accountEntity, authority, reqTimestamp) {
14130
+ // Add Native Broker fields to Telemetry
14131
+ const mats = this.addTelemetryFromNativeResponse(response);
14132
+ // If scopes not returned in server response, use request scopes
14133
+ const responseScopes = response.scope
14134
+ ? ScopeSet.fromString(response.scope)
14135
+ : ScopeSet.fromString(request.scope);
14136
+ const accountProperties = response.account.properties || {};
14137
+ const uid = accountProperties["UID"] ||
14138
+ idTokenClaims.oid ||
14139
+ idTokenClaims.sub ||
14140
+ Constants.EMPTY_STRING;
14141
+ const tid = accountProperties["TenantId"] ||
14142
+ idTokenClaims.tid ||
14143
+ Constants.EMPTY_STRING;
14144
+ const accountInfo = updateAccountTenantProfileData(accountEntity.getAccountInfo(), undefined, // tenantProfile optional
14145
+ idTokenClaims, response.id_token);
14146
+ /**
14147
+ * In pairwise broker flows, this check prevents the broker's native account id
14148
+ * from being returned over the embedded app's account id.
14149
+ */
14150
+ if (accountInfo.nativeAccountId !== response.account.id) {
14151
+ accountInfo.nativeAccountId = response.account.id;
14152
+ }
14153
+ // generate PoP token as needed
14154
+ const responseAccessToken = await this.generatePopAccessToken(response, request);
14155
+ const tokenType = request.tokenType === AuthenticationScheme.POP
14156
+ ? AuthenticationScheme.POP
14157
+ : AuthenticationScheme.BEARER;
14158
+ const result = {
14159
+ authority: authority,
14160
+ uniqueId: uid,
14161
+ tenantId: tid,
14162
+ scopes: responseScopes.asArray(),
14163
+ account: accountInfo,
14164
+ idToken: response.id_token,
14165
+ idTokenClaims: idTokenClaims,
14166
+ accessToken: responseAccessToken,
14167
+ fromCache: mats ? this.isResponseFromCache(mats) : false,
14168
+ // Request timestamp and NativeResponse expires_in are in seconds, converting to Date for AuthenticationResult
14169
+ expiresOn: toDateFromSeconds(reqTimestamp + response.expires_in),
14170
+ tokenType: tokenType,
14171
+ correlationId: this.correlationId,
14172
+ state: response.state,
14173
+ fromNativeBroker: true,
14174
+ };
14175
+ return result;
14264
14176
  }
14265
14177
  /**
14266
- * Returns the version for the browser extension this handler is communicating with
14267
- * @returns
14178
+ * cache the account entity in browser storage
14179
+ * @param accountEntity
14268
14180
  */
14269
- getExtensionVersion() {
14270
- return this.extensionVersion;
14181
+ async cacheAccount(accountEntity) {
14182
+ // Store the account info and hence `nativeAccountId` in browser cache
14183
+ await this.browserStorage.setAccount(accountEntity, this.correlationId);
14184
+ // Remove any existing cached tokens for this account in browser storage
14185
+ this.browserStorage.removeAccountContext(accountEntity).catch((e) => {
14186
+ this.logger.error(`Error occurred while removing account context from browser storage. ${e}`);
14187
+ });
14271
14188
  }
14272
14189
  /**
14273
- * Returns boolean indicating whether or not the request should attempt to use native broker
14274
- * @param logger
14275
- * @param config
14276
- * @param nativeExtensionProvider
14277
- * @param authenticationScheme
14190
+ * Stores the access_token and id_token in inmemory storage
14191
+ * @param response
14192
+ * @param request
14193
+ * @param homeAccountIdentifier
14194
+ * @param idTokenObj
14195
+ * @param responseAccessToken
14196
+ * @param tenantId
14197
+ * @param reqTimestamp
14278
14198
  */
14279
- static isPlatformBrokerAvailable(config, logger, nativeExtensionProvider, authenticationScheme) {
14280
- logger.trace("isPlatformBrokerAvailable called");
14281
- if (!config.system.allowPlatformBroker) {
14282
- logger.trace("isPlatformBrokerAvailable: allowPlatformBroker is not enabled, returning false");
14283
- // Developer disabled WAM
14284
- return false;
14199
+ cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, responseAccessToken, tenantId, reqTimestamp) {
14200
+ const cachedIdToken = createIdTokenEntity(homeAccountIdentifier, request.authority, response.id_token || "", request.clientId, idTokenClaims.tid || "");
14201
+ // cache accessToken in inmemory storage
14202
+ const expiresIn = request.tokenType === AuthenticationScheme.POP
14203
+ ? Constants.SHR_NONCE_VALIDITY
14204
+ : (typeof response.expires_in === "string"
14205
+ ? parseInt(response.expires_in, 10)
14206
+ : response.expires_in) || 0;
14207
+ const tokenExpirationSeconds = reqTimestamp + expiresIn;
14208
+ const responseScopes = this.generateScopes(response, request);
14209
+ const cachedAccessToken = createAccessTokenEntity(homeAccountIdentifier, request.authority, responseAccessToken, request.clientId, idTokenClaims.tid || tenantId, responseScopes.printScopes(), tokenExpirationSeconds, 0, base64Decode, undefined, request.tokenType, undefined, request.keyId);
14210
+ const nativeCacheRecord = {
14211
+ idToken: cachedIdToken,
14212
+ accessToken: cachedAccessToken,
14213
+ };
14214
+ return this.nativeStorageManager.saveCacheRecord(nativeCacheRecord, this.correlationId, request.storeInCache);
14215
+ }
14216
+ addTelemetryFromNativeResponse(response) {
14217
+ const mats = this.getMATSFromResponse(response);
14218
+ if (!mats) {
14219
+ return null;
14285
14220
  }
14286
- if (!nativeExtensionProvider) {
14287
- logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
14288
- // Extension is not available
14289
- return false;
14221
+ this.performanceClient.addFields({
14222
+ extensionId: this.nativeMessageHandler.getExtensionId(),
14223
+ extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
14224
+ matsBrokerVersion: mats.broker_version,
14225
+ matsAccountJoinOnStart: mats.account_join_on_start,
14226
+ matsAccountJoinOnEnd: mats.account_join_on_end,
14227
+ matsDeviceJoin: mats.device_join,
14228
+ matsPromptBehavior: mats.prompt_behavior,
14229
+ matsApiErrorCode: mats.api_error_code,
14230
+ matsUiVisible: mats.ui_visible,
14231
+ matsSilentCode: mats.silent_code,
14232
+ matsSilentBiSubCode: mats.silent_bi_sub_code,
14233
+ matsSilentMessage: mats.silent_message,
14234
+ matsSilentStatus: mats.silent_status,
14235
+ matsHttpStatus: mats.http_status,
14236
+ matsHttpEventCount: mats.http_event_count,
14237
+ }, this.correlationId);
14238
+ return mats;
14239
+ }
14240
+ /**
14241
+ * Validates native platform response before processing
14242
+ * @param response
14243
+ */
14244
+ validateNativeResponse(response) {
14245
+ if (response.hasOwnProperty("access_token") &&
14246
+ response.hasOwnProperty("id_token") &&
14247
+ response.hasOwnProperty("client_info") &&
14248
+ response.hasOwnProperty("account") &&
14249
+ response.hasOwnProperty("scope") &&
14250
+ response.hasOwnProperty("expires_in")) {
14251
+ return response;
14290
14252
  }
14291
- if (authenticationScheme) {
14292
- switch (authenticationScheme) {
14293
- case AuthenticationScheme.BEARER:
14294
- case AuthenticationScheme.POP:
14295
- logger.trace("isPlatformBrokerAvailable: authenticationScheme is supported, returning true");
14296
- return true;
14297
- default:
14298
- logger.trace("isPlatformBrokerAvailable: authenticationScheme is not supported, returning false");
14299
- return false;
14300
- }
14253
+ else {
14254
+ throw createAuthError(unexpectedError, "Response missing expected properties.");
14301
14255
  }
14302
- return true;
14303
- }
14304
- }
14305
-
14306
- /*
14307
- * Copyright (c) Microsoft Corporation. All rights reserved.
14308
- * Licensed under the MIT License.
14309
- */
14310
- /**
14311
- * Abstract class which defines operations for a browser interaction handling class.
14312
- */
14313
- class InteractionHandler {
14314
- constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
14315
- this.authModule = authCodeModule;
14316
- this.browserStorage = storageImpl;
14317
- this.authCodeRequest = authCodeRequest;
14318
- this.logger = logger;
14319
- this.performanceClient = performanceClient;
14320
14256
  }
14321
14257
  /**
14322
- * Function to handle response parameters from hash.
14323
- * @param locationHash
14258
+ * Gets MATS telemetry from native response
14259
+ * @param response
14260
+ * @returns
14324
14261
  */
14325
- async handleCodeResponse(response, request) {
14326
- this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponse, request.correlationId);
14327
- let authCodeResponse;
14328
- try {
14329
- authCodeResponse = getAuthorizationCodePayload(response, request.state);
14330
- }
14331
- catch (e) {
14332
- if (e instanceof ServerError &&
14333
- e.subError === userCancelled) {
14334
- // Translate server error caused by user closing native prompt to corresponding first class MSAL error
14335
- throw createBrowserAuthError(userCancelled);
14262
+ getMATSFromResponse(response) {
14263
+ if (response.properties.MATS) {
14264
+ try {
14265
+ return JSON.parse(response.properties.MATS);
14336
14266
  }
14337
- else {
14338
- throw e;
14267
+ catch (e) {
14268
+ this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
14339
14269
  }
14340
14270
  }
14341
- return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
14271
+ return null;
14342
14272
  }
14343
14273
  /**
14344
- * Process auth code response from AAD
14345
- * @param authCodeResponse
14346
- * @param state
14347
- * @param authority
14348
- * @param networkModule
14274
+ * Returns whether or not response came from native cache
14275
+ * @param response
14349
14276
  * @returns
14350
14277
  */
14351
- async handleCodeResponseFromServer(authCodeResponse, request, validateNonce = true) {
14352
- this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponseFromServer, request.correlationId);
14353
- this.logger.trace("InteractionHandler.handleCodeResponseFromServer called");
14354
- // Assign code to request
14355
- this.authCodeRequest.code = authCodeResponse.code;
14356
- // Check for new cloud instance
14357
- if (authCodeResponse.cloud_instance_host_name) {
14358
- await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, request.correlationId)(authCodeResponse.cloud_instance_host_name, request.correlationId);
14359
- }
14360
- // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
14361
- if (validateNonce) {
14362
- // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
14363
- authCodeResponse.nonce = request.nonce || undefined;
14364
- }
14365
- authCodeResponse.state = request.state;
14366
- // Add CCS parameters if available
14367
- if (authCodeResponse.client_info) {
14368
- this.authCodeRequest.clientInfo = authCodeResponse.client_info;
14369
- }
14370
- else {
14371
- const ccsCred = this.createCcsCredentials(request);
14372
- if (ccsCred) {
14373
- this.authCodeRequest.ccsCredential = ccsCred;
14374
- }
14278
+ isResponseFromCache(mats) {
14279
+ if (typeof mats.is_cached === "undefined") {
14280
+ this.logger.verbose("NativeInteractionClient - MATS telemetry does not contain field indicating if response was served from cache. Returning false.");
14281
+ return false;
14375
14282
  }
14376
- // Acquire token with retrieved code.
14377
- const tokenResponse = (await invokeAsync(this.authModule.acquireToken.bind(this.authModule), PerformanceEvents.AuthClientAcquireToken, this.logger, this.performanceClient, request.correlationId)(this.authCodeRequest, authCodeResponse));
14378
- return tokenResponse;
14283
+ return !!mats.is_cached;
14379
14284
  }
14380
14285
  /**
14381
- * Build ccs creds if available
14286
+ * Translates developer provided request object into NativeRequest object
14287
+ * @param request
14382
14288
  */
14383
- createCcsCredentials(request) {
14289
+ async initializeNativeRequest(request) {
14290
+ this.logger.trace("NativeInteractionClient - initializeNativeRequest called");
14291
+ const requestAuthority = request.authority || this.config.auth.authority;
14384
14292
  if (request.account) {
14385
- return {
14386
- credential: request.account.homeAccountId,
14387
- type: CcsCredentialType.HOME_ACCOUNT_ID,
14388
- };
14293
+ // validate authority
14294
+ await this.getDiscoveredAuthority({
14295
+ requestAuthority,
14296
+ requestAzureCloudOptions: request.azureCloudOptions,
14297
+ account: request.account,
14298
+ });
14389
14299
  }
14390
- else if (request.loginHint) {
14391
- return {
14392
- credential: request.loginHint,
14393
- type: CcsCredentialType.UPN,
14300
+ const canonicalAuthority = new UrlString(requestAuthority);
14301
+ canonicalAuthority.validateAsUri();
14302
+ // 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.
14303
+ const { scopes, ...remainingProperties } = request;
14304
+ const scopeSet = new ScopeSet(scopes || []);
14305
+ scopeSet.appendScopes(OIDC_DEFAULT_SCOPES);
14306
+ const getPrompt = () => {
14307
+ // If request is silent, prompt is always none
14308
+ switch (this.apiId) {
14309
+ case ApiId.ssoSilent:
14310
+ case ApiId.acquireTokenSilent_silentFlow:
14311
+ this.logger.trace("initializeNativeRequest: silent request sets prompt to none");
14312
+ return PromptValue.NONE;
14313
+ }
14314
+ // Prompt not provided, request may proceed and native broker decides if it needs to prompt
14315
+ if (!request.prompt) {
14316
+ this.logger.trace("initializeNativeRequest: prompt was not provided");
14317
+ return undefined;
14318
+ }
14319
+ // If request is interactive, check if prompt provided is allowed to go directly to native broker
14320
+ switch (request.prompt) {
14321
+ case PromptValue.NONE:
14322
+ case PromptValue.CONSENT:
14323
+ case PromptValue.LOGIN:
14324
+ this.logger.trace("initializeNativeRequest: prompt is compatible with native flow");
14325
+ return request.prompt;
14326
+ default:
14327
+ this.logger.trace(`initializeNativeRequest: prompt = ${request.prompt} is not compatible with native flow`);
14328
+ throw createBrowserAuthError(nativePromptNotSupported);
14329
+ }
14330
+ };
14331
+ const validatedRequest = {
14332
+ ...remainingProperties,
14333
+ accountId: this.accountId,
14334
+ clientId: this.config.auth.clientId,
14335
+ authority: canonicalAuthority.urlString,
14336
+ scope: scopeSet.printScopes(),
14337
+ redirectUri: this.getRedirectUri(request.redirectUri),
14338
+ prompt: getPrompt(),
14339
+ correlationId: this.correlationId,
14340
+ tokenType: request.authenticationScheme,
14341
+ windowTitleSubstring: document.title,
14342
+ extraParameters: {
14343
+ ...request.extraQueryParameters,
14344
+ ...request.tokenQueryParameters,
14345
+ },
14346
+ extendedExpiryToken: false,
14347
+ keyId: request.popKid,
14348
+ };
14349
+ // Check for PoP token requests: signPopToken should only be set to true if popKid is not set
14350
+ if (validatedRequest.signPopToken && !!request.popKid) {
14351
+ throw createBrowserAuthError(invalidPopTokenRequest);
14352
+ }
14353
+ this.handleExtraBrokerParams(validatedRequest);
14354
+ validatedRequest.extraParameters =
14355
+ validatedRequest.extraParameters || {};
14356
+ validatedRequest.extraParameters.telemetry =
14357
+ NativeConstants.MATS_TELEMETRY;
14358
+ if (request.authenticationScheme === AuthenticationScheme.POP) {
14359
+ // add POP request type
14360
+ const shrParameters = {
14361
+ resourceRequestUri: request.resourceRequestUri,
14362
+ resourceRequestMethod: request.resourceRequestMethod,
14363
+ shrClaims: request.shrClaims,
14364
+ shrNonce: request.shrNonce,
14394
14365
  };
14366
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14367
+ // generate reqCnf if not provided in the request
14368
+ let reqCnfData;
14369
+ if (!validatedRequest.keyId) {
14370
+ const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, this.logger, this.performanceClient, request.correlationId)(shrParameters, this.logger);
14371
+ reqCnfData = generatedReqCnfData.reqCnfString;
14372
+ validatedRequest.keyId = generatedReqCnfData.kid;
14373
+ validatedRequest.signPopToken = true;
14374
+ }
14375
+ else {
14376
+ reqCnfData = this.browserCrypto.base64UrlEncode(JSON.stringify({ kid: validatedRequest.keyId }));
14377
+ validatedRequest.signPopToken = false;
14378
+ }
14379
+ // SPAs require whole string to be passed to broker
14380
+ validatedRequest.reqCnf = reqCnfData;
14395
14381
  }
14396
- return null;
14382
+ this.addRequestSKUs(validatedRequest);
14383
+ return validatedRequest;
14397
14384
  }
14398
- }
14399
-
14400
- /*
14401
- * Copyright (c) Microsoft Corporation. All rights reserved.
14402
- * Licensed under the MIT License.
14403
- */
14404
- function deserializeResponse(responseString, responseLocation, logger) {
14405
- // Deserialize hash fragment response parameters.
14406
- const serverParams = getDeserializedResponse(responseString);
14407
- if (!serverParams) {
14408
- if (!stripLeadingHashOrQuery(responseString)) {
14409
- // Hash or Query string is empty
14410
- 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.`);
14411
- throw createBrowserAuthError(hashEmptyError);
14385
+ /**
14386
+ * Handles extra broker request parameters
14387
+ * @param request {NativeTokenRequest}
14388
+ * @private
14389
+ */
14390
+ handleExtraBrokerParams(request) {
14391
+ const hasExtraBrokerParams = request.extraParameters &&
14392
+ request.extraParameters.hasOwnProperty(BROKER_CLIENT_ID) &&
14393
+ request.extraParameters.hasOwnProperty(BROKER_REDIRECT_URI) &&
14394
+ request.extraParameters.hasOwnProperty(CLIENT_ID);
14395
+ if (!request.embeddedClientId && !hasExtraBrokerParams) {
14396
+ return;
14412
14397
  }
14413
- else {
14414
- 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.`);
14415
- logger.errorPii(`The ${responseLocation} detected is: ${responseString}`);
14416
- throw createBrowserAuthError(hashDoesNotContainKnownProperties);
14398
+ let child_client_id = "";
14399
+ const child_redirect_uri = request.redirectUri;
14400
+ if (request.embeddedClientId) {
14401
+ request.redirectUri = this.config.auth.redirectUri;
14402
+ child_client_id = request.embeddedClientId;
14417
14403
  }
14418
- }
14419
- return serverParams;
14420
- }
14421
- /**
14422
- * Returns the interaction type that the response object belongs to
14423
- */
14424
- function validateInteractionType(response, browserCrypto, interactionType) {
14425
- if (!response.state) {
14426
- throw createBrowserAuthError(noStateInHash);
14427
- }
14428
- const platformStateObj = extractBrowserRequestState(browserCrypto, response.state);
14429
- if (!platformStateObj) {
14430
- throw createBrowserAuthError(unableToParseState);
14431
- }
14432
- if (platformStateObj.interactionType !== interactionType) {
14433
- throw createBrowserAuthError(stateInteractionTypeMismatch);
14404
+ else if (request.extraParameters) {
14405
+ request.redirectUri =
14406
+ request.extraParameters[BROKER_REDIRECT_URI];
14407
+ child_client_id =
14408
+ request.extraParameters[CLIENT_ID];
14409
+ }
14410
+ request.extraParameters = {
14411
+ child_client_id,
14412
+ child_redirect_uri,
14413
+ };
14414
+ this.performanceClient?.addFields({
14415
+ embeddedClientId: child_client_id,
14416
+ embeddedRedirectUri: child_redirect_uri,
14417
+ }, request.correlationId);
14434
14418
  }
14435
14419
  }
14436
14420
 
@@ -14494,9 +14478,147 @@
14494
14478
  throw createClientConfigurationError(pkceParamsMissing);
14495
14479
  }
14496
14480
  const parameters = await invokeAsync(getStandardParameters, PerformanceEvents.GetStandardParams, logger, performanceClient, request.correlationId)(config, authority, request, logger, performanceClient);
14497
- addResponseTypeCode(parameters);
14481
+ addResponseType(parameters, OAuthResponseType.CODE);
14498
14482
  addCodeChallengeParams(parameters, request.codeChallenge, Constants.S256_CODE_CHALLENGE_METHOD);
14483
+ addExtraQueryParameters(parameters, request.extraQueryParameters || {});
14499
14484
  return getAuthorizeUrl(authority, parameters);
14485
+ }
14486
+ /**
14487
+ * Gets the form that will be posted to /authorize with request parameters when using EAR
14488
+ */
14489
+ async function getEARForm(frame, config, authority, request, logger, performanceClient) {
14490
+ if (!request.earJwk) {
14491
+ throw createBrowserAuthError(earJwkEmpty);
14492
+ }
14493
+ const parameters = await getStandardParameters(config, authority, request, logger, performanceClient);
14494
+ addResponseType(parameters, OAuthResponseType.IDTOKEN_TOKEN_REFRESHTOKEN);
14495
+ addEARParameters(parameters, request.earJwk);
14496
+ const queryParams = new Map();
14497
+ addExtraQueryParameters(queryParams, request.extraQueryParameters || {});
14498
+ const url = getAuthorizeUrl(authority, queryParams);
14499
+ return createForm(frame, url, parameters);
14500
+ }
14501
+ /**
14502
+ * Creates form element in the provided document with auth parameters in the post body
14503
+ * @param frame
14504
+ * @param authorizeUrl
14505
+ * @param parameters
14506
+ * @returns
14507
+ */
14508
+ function createForm(frame, authorizeUrl, parameters) {
14509
+ const form = frame.createElement("form");
14510
+ form.method = "post";
14511
+ form.action = authorizeUrl;
14512
+ parameters.forEach((value, key) => {
14513
+ const param = frame.createElement("input");
14514
+ param.hidden = true;
14515
+ param.name = key;
14516
+ param.value = value;
14517
+ form.appendChild(param);
14518
+ });
14519
+ frame.body.appendChild(form);
14520
+ return form;
14521
+ }
14522
+ /**
14523
+ * Response handler when server returns accountId on the /authorize request
14524
+ * @param request
14525
+ * @param accountId
14526
+ * @param apiId
14527
+ * @param config
14528
+ * @param browserStorage
14529
+ * @param nativeStorage
14530
+ * @param eventHandler
14531
+ * @param logger
14532
+ * @param performanceClient
14533
+ * @param nativeMessageHandler
14534
+ * @returns
14535
+ */
14536
+ async function handleResponsePlatformBroker(request, accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14537
+ if (!nativeMessageHandler) {
14538
+ throw createBrowserAuthError(nativeConnectionNotEstablished);
14539
+ }
14540
+ const browserCrypto = new CryptoOps(logger, performanceClient);
14541
+ const nativeInteractionClient = new NativeInteractionClient(config, browserStorage, browserCrypto, logger, eventHandler, config.system.navigationClient, apiId, performanceClient, nativeMessageHandler, accountId, nativeStorage, request.correlationId);
14542
+ const { userRequestState } = ProtocolUtils.parseRequestState(browserCrypto, request.state);
14543
+ return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), PerformanceEvents.NativeInteractionClientAcquireToken, logger, performanceClient, request.correlationId)({
14544
+ ...request,
14545
+ state: userRequestState,
14546
+ prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
14547
+ });
14548
+ }
14549
+ /**
14550
+ * Response handler when server returns code on the /authorize request
14551
+ * @param request
14552
+ * @param response
14553
+ * @param codeVerifier
14554
+ * @param authClient
14555
+ * @param browserStorage
14556
+ * @param logger
14557
+ * @param performanceClient
14558
+ * @returns
14559
+ */
14560
+ async function handleResponseCode(request, response, codeVerifier, apiId, config, authClient, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14561
+ // Remove throttle if it exists
14562
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14563
+ if (response.accountId) {
14564
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, response.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14565
+ }
14566
+ const authCodeRequest = {
14567
+ ...request,
14568
+ code: response.code || "",
14569
+ codeVerifier: codeVerifier,
14570
+ };
14571
+ // Create popup interaction handler.
14572
+ const interactionHandler = new InteractionHandler(authClient, browserStorage, authCodeRequest, logger, performanceClient);
14573
+ // Handle response from hash string.
14574
+ const result = await invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), PerformanceEvents.HandleCodeResponse, logger, performanceClient, request.correlationId)(response, request);
14575
+ return result;
14576
+ }
14577
+ /**
14578
+ * Response handler when server returns ear_jwe on the /authorize request
14579
+ * @param request
14580
+ * @param response
14581
+ * @param apiId
14582
+ * @param config
14583
+ * @param authority
14584
+ * @param browserStorage
14585
+ * @param nativeStorage
14586
+ * @param eventHandler
14587
+ * @param logger
14588
+ * @param performanceClient
14589
+ * @param nativeMessageHandler
14590
+ * @returns
14591
+ */
14592
+ async function handleResponseEAR(request, response, apiId, config, authority, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14593
+ // Remove throttle if it exists
14594
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14595
+ // Validate state & check response for errors
14596
+ validateAuthorizationResponse(response, request.state);
14597
+ if (!response.ear_jwe) {
14598
+ throw createBrowserAuthError(earJweEmpty);
14599
+ }
14600
+ if (!request.earJwk) {
14601
+ throw createBrowserAuthError(earJwkEmpty);
14602
+ }
14603
+ const decryptedData = JSON.parse(await invokeAsync(decryptEarResponse, PerformanceEvents.DecryptEarResponse, logger, performanceClient, request.correlationId)(request.earJwk, response.ear_jwe));
14604
+ if (decryptedData.accountId) {
14605
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, decryptedData.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14606
+ }
14607
+ const responseHandler = new ResponseHandler(config.auth.clientId, browserStorage, new CryptoOps(logger, performanceClient), logger, null, null, performanceClient);
14608
+ // Validate response. This function throws a server error if an error is returned by the server.
14609
+ responseHandler.validateTokenResponse(decryptedData);
14610
+ // Temporary until response handler is refactored to be more flow agnostic.
14611
+ const additionalData = {
14612
+ code: "",
14613
+ state: request.state,
14614
+ nonce: request.nonce,
14615
+ client_info: decryptedData.client_info,
14616
+ cloud_graph_host_name: decryptedData.cloud_graph_host_name,
14617
+ cloud_instance_host_name: decryptedData.cloud_instance_host_name,
14618
+ cloud_instance_name: decryptedData.cloud_instance_name,
14619
+ msgraph_host: decryptedData.msgraph_host,
14620
+ };
14621
+ return (await invokeAsync(responseHandler.handleServerTokenResponse.bind(responseHandler), PerformanceEvents.HandleServerTokenResponse, logger, performanceClient, request.correlationId)(decryptedData, authority, nowSeconds(), request, additionalData, undefined, undefined, undefined, undefined));
14500
14622
  }
14501
14623
 
14502
14624
  /*
@@ -14564,6 +14686,7 @@
14564
14686
  // Properly sets this reference for the unload event.
14565
14687
  this.unloadWindow = this.unloadWindow.bind(this);
14566
14688
  this.nativeStorage = nativeStorageImpl;
14689
+ this.eventHandler = eventHandler;
14567
14690
  }
14568
14691
  /**
14569
14692
  * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
@@ -14639,11 +14762,7 @@
14639
14762
  */
14640
14763
  async acquireTokenPopupAsync(request, popupParams, pkceCodes) {
14641
14764
  this.logger.verbose("acquireTokenPopupAsync called");
14642
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14643
14765
  const validRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(request, exports.InteractionType.Popup);
14644
- const pkce = pkceCodes ||
14645
- (await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId));
14646
- validRequest.codeChallenge = pkce.challenge;
14647
14766
  /*
14648
14767
  * Skip pre-connect for async popups to reduce time between user interaction and popup window creation to avoid
14649
14768
  * popup from being blocked by browsers with shorter popup timers
@@ -14651,65 +14770,49 @@
14651
14770
  if (popupParams.popup) {
14652
14771
  preconnect(validRequest.authority);
14653
14772
  }
14773
+ const isPlatformBroker = NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
14774
+ validRequest.platformBroker = isPlatformBroker;
14775
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
14776
+ return this.executeEarFlow(validRequest, popupParams);
14777
+ }
14778
+ else {
14779
+ return this.executeCodeFlow(validRequest, popupParams, pkceCodes);
14780
+ }
14781
+ }
14782
+ /**
14783
+ * Executes auth code + PKCE flow
14784
+ * @param request
14785
+ * @param popupParams
14786
+ * @param pkceCodes
14787
+ * @returns
14788
+ */
14789
+ async executeCodeFlow(request, popupParams, pkceCodes) {
14790
+ const correlationId = request.correlationId;
14791
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14792
+ const pkce = pkceCodes ||
14793
+ (await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId));
14794
+ const popupRequest = {
14795
+ ...request,
14796
+ codeChallenge: pkce.challenge,
14797
+ };
14654
14798
  try {
14655
14799
  // Initialize the client
14656
- const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
14800
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, correlationId)({
14657
14801
  serverTelemetryManager,
14658
- requestAuthority: validRequest.authority,
14659
- requestAzureCloudOptions: validRequest.azureCloudOptions,
14660
- requestExtraQueryParameters: validRequest.extraQueryParameters,
14661
- account: validRequest.account,
14802
+ requestAuthority: popupRequest.authority,
14803
+ requestAzureCloudOptions: popupRequest.azureCloudOptions,
14804
+ requestExtraQueryParameters: popupRequest.extraQueryParameters,
14805
+ account: popupRequest.account,
14662
14806
  });
14663
- const isPlatformBroker = NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
14664
- // Start measurement for server calls with native brokering enabled
14665
- let fetchNativeAccountIdMeasurement;
14666
- if (isPlatformBroker) {
14667
- fetchNativeAccountIdMeasurement =
14668
- this.performanceClient.startMeasurement(PerformanceEvents.FetchAccountIdWithNativeBroker, request.correlationId);
14669
- }
14670
14807
  // Create acquire token url.
14671
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, validRequest.correlationId)(this.config, authClient.authority, {
14672
- ...validRequest,
14673
- platformBroker: isPlatformBroker,
14674
- }, this.logger, this.performanceClient);
14808
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, popupRequest, this.logger, this.performanceClient);
14675
14809
  // Show the UI once the url has been created. Get the window handle for the popup.
14676
14810
  const popupWindow = this.initiateAuthRequest(navigateUrl, popupParams);
14677
14811
  this.eventHandler.emitEvent(EventType.POPUP_OPENED, exports.InteractionType.Popup, { popupWindow }, null);
14678
14812
  // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
14679
14813
  const responseString = await this.monitorPopupForHash(popupWindow, popupParams.popupWindowParent);
14680
14814
  const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, this.config.auth.OIDCOptions.serverResponseType, this.logger);
14681
- // Remove throttle if it exists
14682
- ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, validRequest);
14683
- if (serverParams.accountId) {
14684
- this.logger.verbose("Account id found in hash, calling WAM for token");
14685
- // end measurement for server call with native brokering enabled
14686
- if (fetchNativeAccountIdMeasurement) {
14687
- fetchNativeAccountIdMeasurement.end({
14688
- success: true,
14689
- isNativeBroker: true,
14690
- });
14691
- }
14692
- if (!this.nativeMessageHandler) {
14693
- throw createBrowserAuthError(nativeConnectionNotEstablished);
14694
- }
14695
- 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);
14696
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, validRequest.state);
14697
- return await nativeInteractionClient.acquireToken({
14698
- ...validRequest,
14699
- state: userRequestState,
14700
- prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
14701
- });
14702
- }
14703
- const authCodeRequest = {
14704
- ...validRequest,
14705
- code: serverParams.code || "",
14706
- codeVerifier: pkce.verifier,
14707
- };
14708
- // Create popup interaction handler.
14709
- const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
14710
- // Handle response from hash string.
14711
- const result = await interactionHandler.handleCodeResponse(serverParams, validRequest);
14712
- return result;
14815
+ 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);
14713
14816
  }
14714
14817
  catch (e) {
14715
14818
  // Close the synchronous popup if an error is thrown before the window unload event is registered
@@ -14721,6 +14824,32 @@
14721
14824
  throw e;
14722
14825
  }
14723
14826
  }
14827
+ /**
14828
+ * Executes EAR flow
14829
+ * @param request
14830
+ */
14831
+ async executeEarFlow(request, popupParams) {
14832
+ const correlationId = request.correlationId;
14833
+ // Get the frame handle for the silent request
14834
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
14835
+ requestAuthority: request.authority,
14836
+ requestAzureCloudOptions: request.azureCloudOptions,
14837
+ requestExtraQueryParameters: request.extraQueryParameters,
14838
+ account: request.account,
14839
+ });
14840
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
14841
+ const popupRequest = {
14842
+ ...request,
14843
+ earJwk: earJwk,
14844
+ };
14845
+ const popupWindow = popupParams.popup || this.openPopup("about:blank", popupParams);
14846
+ const form = await getEARForm(popupWindow.document, this.config, discoveredAuthority, popupRequest, this.logger, this.performanceClient);
14847
+ form.submit();
14848
+ // Monitor the popup for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
14849
+ const responseString = await invokeAsync(this.monitorPopupForHash.bind(this), PerformanceEvents.SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(popupWindow, popupParams.popupWindowParent);
14850
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, this.config.auth.OIDCOptions.serverResponseType, this.logger);
14851
+ 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);
14852
+ }
14724
14853
  /**
14725
14854
  *
14726
14855
  * @param validRequest
@@ -14796,7 +14925,6 @@
14796
14925
  e.setCorrelationId(this.correlationId);
14797
14926
  serverTelemetryManager.cacheFailedRequest(e);
14798
14927
  }
14799
- this.browserStorage.setInteractionInProgress(false);
14800
14928
  this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, exports.InteractionType.Popup, null, e);
14801
14929
  this.eventHandler.emitEvent(EventType.LOGOUT_END, exports.InteractionType.Popup);
14802
14930
  throw e;
@@ -14908,7 +15036,6 @@
14908
15036
  }
14909
15037
  catch (e) {
14910
15038
  this.logger.error("error opening popup " + e.message);
14911
- this.browserStorage.setInteractionInProgress(false);
14912
15039
  throw createBrowserAuthError(popupWindowError);
14913
15040
  }
14914
15041
  }
@@ -14966,7 +15093,6 @@
14966
15093
  * Event callback to unload main window.
14967
15094
  */
14968
15095
  unloadWindow(e) {
14969
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Popup);
14970
15096
  if (this.currentWindow) {
14971
15097
  this.currentWindow.close();
14972
15098
  }
@@ -14982,8 +15108,6 @@
14982
15108
  popupWindow.close();
14983
15109
  // Remove window unload function
14984
15110
  popupWindowParent.removeEventListener("beforeunload", this.unloadWindow);
14985
- // Interaction is completed - remove interaction status.
14986
- this.browserStorage.setInteractionInProgress(false);
14987
15111
  }
14988
15112
  /**
14989
15113
  * Generates the name for the popup based on the client id and request
@@ -15004,141 +15128,6 @@
15004
15128
  }
15005
15129
  }
15006
15130
 
15007
- /*
15008
- * Copyright (c) Microsoft Corporation. All rights reserved.
15009
- * Licensed under the MIT License.
15010
- */
15011
- class RedirectHandler {
15012
- constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
15013
- this.authModule = authCodeModule;
15014
- this.browserStorage = storageImpl;
15015
- this.authCodeRequest = authCodeRequest;
15016
- this.logger = logger;
15017
- this.performanceClient = performanceClient;
15018
- }
15019
- /**
15020
- * Redirects window to given URL.
15021
- * @param urlNavigate
15022
- */
15023
- async initiateAuthRequest(requestUrl, params) {
15024
- this.logger.verbose("RedirectHandler.initiateAuthRequest called");
15025
- // Navigate if valid URL
15026
- if (requestUrl) {
15027
- // Cache start page, returns to this page after redirectUri if navigateToLoginRequestUrl is true
15028
- if (params.redirectStartPage) {
15029
- this.logger.verbose("RedirectHandler.initiateAuthRequest: redirectStartPage set, caching start page");
15030
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.ORIGIN_URI, params.redirectStartPage, true);
15031
- }
15032
- // Set interaction status in the library.
15033
- this.browserStorage.setTemporaryCache(TemporaryCacheKeys.CORRELATION_ID, this.authCodeRequest.correlationId, true);
15034
- this.browserStorage.cacheCodeRequest(this.authCodeRequest);
15035
- this.logger.infoPii(`RedirectHandler.initiateAuthRequest: Navigate to: ${requestUrl}`);
15036
- const navigationOptions = {
15037
- apiId: ApiId.acquireTokenRedirect,
15038
- timeout: params.redirectTimeout,
15039
- noHistory: false,
15040
- };
15041
- // If onRedirectNavigate is implemented, invoke it and provide requestUrl
15042
- if (typeof params.onRedirectNavigate === "function") {
15043
- this.logger.verbose("RedirectHandler.initiateAuthRequest: Invoking onRedirectNavigate callback");
15044
- const navigate = params.onRedirectNavigate(requestUrl);
15045
- // Returning false from onRedirectNavigate will stop navigation
15046
- if (navigate !== false) {
15047
- this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate did not return false, navigating");
15048
- await params.navigationClient.navigateExternal(requestUrl, navigationOptions);
15049
- return;
15050
- }
15051
- else {
15052
- this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate returned false, stopping navigation");
15053
- return;
15054
- }
15055
- }
15056
- else {
15057
- // Navigate window to request URL
15058
- this.logger.verbose("RedirectHandler.initiateAuthRequest: Navigating window to navigate url");
15059
- await params.navigationClient.navigateExternal(requestUrl, navigationOptions);
15060
- return;
15061
- }
15062
- }
15063
- else {
15064
- // Throw error if request URL is empty.
15065
- this.logger.info("RedirectHandler.initiateAuthRequest: Navigate url is empty");
15066
- throw createBrowserAuthError(emptyNavigateUri);
15067
- }
15068
- }
15069
- /**
15070
- * Handle authorization code response in the window.
15071
- * @param hash
15072
- */
15073
- async handleCodeResponse(response, state) {
15074
- this.logger.verbose("RedirectHandler.handleCodeResponse called");
15075
- // Interaction is completed - remove interaction status.
15076
- this.browserStorage.setInteractionInProgress(false);
15077
- // Handle code response.
15078
- const stateKey = this.browserStorage.generateStateKey(state);
15079
- const requestState = this.browserStorage.getTemporaryCache(stateKey);
15080
- if (!requestState) {
15081
- throw createClientAuthError(stateNotFound, "Cached State");
15082
- }
15083
- let authCodeResponse;
15084
- try {
15085
- authCodeResponse = getAuthorizationCodePayload(response, requestState);
15086
- }
15087
- catch (e) {
15088
- if (e instanceof ServerError &&
15089
- e.subError === userCancelled) {
15090
- // Translate server error caused by user closing native prompt to corresponding first class MSAL error
15091
- throw createBrowserAuthError(userCancelled);
15092
- }
15093
- else {
15094
- throw e;
15095
- }
15096
- }
15097
- // Get cached items
15098
- const nonceKey = this.browserStorage.generateNonceKey(requestState);
15099
- const cachedNonce = this.browserStorage.getTemporaryCache(nonceKey);
15100
- // Assign code to request
15101
- this.authCodeRequest.code = authCodeResponse.code;
15102
- // Check for new cloud instance
15103
- if (authCodeResponse.cloud_instance_host_name) {
15104
- 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);
15105
- }
15106
- authCodeResponse.nonce = cachedNonce || undefined;
15107
- authCodeResponse.state = requestState;
15108
- // Add CCS parameters if available
15109
- if (authCodeResponse.client_info) {
15110
- this.authCodeRequest.clientInfo = authCodeResponse.client_info;
15111
- }
15112
- else {
15113
- const cachedCcsCred = this.checkCcsCredentials();
15114
- if (cachedCcsCred) {
15115
- this.authCodeRequest.ccsCredential = cachedCcsCred;
15116
- }
15117
- }
15118
- // Acquire token with retrieved code.
15119
- const tokenResponse = (await this.authModule.acquireToken(this.authCodeRequest, authCodeResponse));
15120
- this.browserStorage.cleanRequestByState(state);
15121
- return tokenResponse;
15122
- }
15123
- /**
15124
- * Looks up ccs creds in the cache
15125
- */
15126
- checkCcsCredentials() {
15127
- // Look up ccs credential in temp cache
15128
- const cachedCcsCred = this.browserStorage.getTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, true);
15129
- if (cachedCcsCred) {
15130
- try {
15131
- return JSON.parse(cachedCcsCred);
15132
- }
15133
- catch (e) {
15134
- this.authModule.logger.error("Cache credential could not be parsed");
15135
- this.authModule.logger.errorPii(`Cache credential could not be parsed: ${cachedCcsCred}`);
15136
- }
15137
- }
15138
- return null;
15139
- }
15140
- }
15141
-
15142
15131
  /*
15143
15132
  * Copyright (c) Microsoft Corporation. All rights reserved.
15144
15133
  * Licensed under the MIT License.
@@ -15166,62 +15155,96 @@
15166
15155
  */
15167
15156
  async acquireToken(request) {
15168
15157
  const validRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(request, exports.InteractionType.Redirect);
15169
- const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId);
15170
- validRequest.codeChallenge = pkceCodes.challenge;
15171
- this.browserStorage.updateCacheEntries(validRequest.state, validRequest.nonce, validRequest.authority, validRequest.loginHint || "", validRequest.account || null);
15172
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenRedirect);
15158
+ validRequest.platformBroker =
15159
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
15173
15160
  const handleBackButton = (event) => {
15174
15161
  // Clear temporary cache if the back button is clicked during the redirect flow.
15175
15162
  if (event.persisted) {
15176
15163
  this.logger.verbose("Page was restored from back/forward cache. Clearing temporary cache.");
15177
- this.browserStorage.cleanRequestByState(validRequest.state);
15164
+ this.browserStorage.resetRequestCache();
15178
15165
  this.eventHandler.emitEvent(EventType.RESTORE_FROM_BFCACHE, exports.InteractionType.Redirect);
15179
15166
  }
15180
15167
  };
15168
+ const redirectStartPage = this.getRedirectStartPage(request.redirectStartPage);
15169
+ this.logger.verbosePii(`Redirect start page: ${redirectStartPage}`);
15170
+ // Cache start page, returns to this page after redirectUri if navigateToLoginRequestUrl is true
15171
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.ORIGIN_URI, redirectStartPage, true);
15172
+ // Clear temporary cache if the back button is clicked during the redirect flow.
15173
+ window.addEventListener("pageshow", handleBackButton);
15174
+ try {
15175
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15176
+ await this.executeEarFlow(validRequest);
15177
+ }
15178
+ else {
15179
+ await this.executeCodeFlow(validRequest, request.onRedirectNavigate);
15180
+ }
15181
+ }
15182
+ catch (e) {
15183
+ if (e instanceof AuthError) {
15184
+ e.setCorrelationId(this.correlationId);
15185
+ }
15186
+ window.removeEventListener("pageshow", handleBackButton);
15187
+ throw e;
15188
+ }
15189
+ }
15190
+ /**
15191
+ * Executes auth code + PKCE flow
15192
+ * @param request
15193
+ * @returns
15194
+ */
15195
+ async executeCodeFlow(request, onRedirectNavigate) {
15196
+ const correlationId = request.correlationId;
15197
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenRedirect);
15198
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15199
+ const redirectRequest = {
15200
+ ...request,
15201
+ codeChallenge: pkceCodes.challenge,
15202
+ };
15203
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest, pkceCodes.verifier);
15181
15204
  try {
15182
15205
  // Initialize the client
15183
15206
  const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
15184
15207
  serverTelemetryManager,
15185
- requestAuthority: validRequest.authority,
15186
- requestAzureCloudOptions: validRequest.azureCloudOptions,
15187
- requestExtraQueryParameters: validRequest.extraQueryParameters,
15188
- account: validRequest.account,
15208
+ requestAuthority: redirectRequest.authority,
15209
+ requestAzureCloudOptions: redirectRequest.azureCloudOptions,
15210
+ requestExtraQueryParameters: redirectRequest.extraQueryParameters,
15211
+ account: redirectRequest.account,
15189
15212
  });
15190
- const authCodeRequest = {
15191
- ...validRequest,
15192
- code: "",
15193
- codeVerifier: pkceCodes.verifier,
15194
- };
15195
- // Create redirect interaction handler.
15196
- const interactionHandler = new RedirectHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
15197
15213
  // Create acquire token url.
15198
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, validRequest.correlationId)(this.config, authClient.authority, {
15199
- ...validRequest,
15200
- platformBroker: NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme),
15201
- }, this.logger, this.performanceClient);
15202
- const redirectStartPage = this.getRedirectStartPage(request.redirectStartPage);
15203
- this.logger.verbosePii(`Redirect start page: ${redirectStartPage}`);
15204
- // Clear temporary cache if the back button is clicked during the redirect flow.
15205
- window.addEventListener("pageshow", handleBackButton);
15214
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, request.correlationId)(this.config, authClient.authority, redirectRequest, this.logger, this.performanceClient);
15206
15215
  // Show the UI once the url has been created. Response will come back in the hash, which will be handled in the handleRedirectCallback function.
15207
- return await interactionHandler.initiateAuthRequest(navigateUrl, {
15208
- navigationClient: this.navigationClient,
15209
- redirectTimeout: this.config.system.redirectNavigationTimeout,
15210
- redirectStartPage: redirectStartPage,
15211
- onRedirectNavigate: request.onRedirectNavigate ||
15212
- this.config.auth.onRedirectNavigate,
15213
- });
15216
+ return await this.initiateAuthRequest(navigateUrl, onRedirectNavigate);
15214
15217
  }
15215
15218
  catch (e) {
15216
15219
  if (e instanceof AuthError) {
15217
15220
  e.setCorrelationId(this.correlationId);
15218
15221
  serverTelemetryManager.cacheFailedRequest(e);
15219
15222
  }
15220
- window.removeEventListener("pageshow", handleBackButton);
15221
- this.browserStorage.cleanRequestByState(validRequest.state);
15222
15223
  throw e;
15223
15224
  }
15224
15225
  }
15226
+ /**
15227
+ * Executes EAR flow
15228
+ * @param request
15229
+ */
15230
+ async executeEarFlow(request) {
15231
+ const correlationId = request.correlationId;
15232
+ // Get the frame handle for the silent request
15233
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15234
+ requestAuthority: request.authority,
15235
+ requestAzureCloudOptions: request.azureCloudOptions,
15236
+ requestExtraQueryParameters: request.extraQueryParameters,
15237
+ account: request.account,
15238
+ });
15239
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15240
+ const redirectRequest = {
15241
+ ...request,
15242
+ earJwk: earJwk,
15243
+ };
15244
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest);
15245
+ const form = await getEARForm(document, this.config, discoveredAuthority, redirectRequest, this.logger, this.performanceClient);
15246
+ form.submit();
15247
+ }
15225
15248
  /**
15226
15249
  * Checks if navigateToLoginRequestUrl is set, and:
15227
15250
  * - if true, performs logic to cache and navigate
@@ -15229,18 +15252,14 @@
15229
15252
  * @param hash {string} url hash
15230
15253
  * @param parentMeasurement {InProgressPerformanceEvent} parent measurement
15231
15254
  */
15232
- async handleRedirectPromise(hash = "", parentMeasurement) {
15255
+ async handleRedirectPromise(hash = "", request, pkceVerifier, parentMeasurement) {
15233
15256
  const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.handleRedirectPromise);
15234
15257
  try {
15235
- if (!this.browserStorage.isInteractionInProgress(true)) {
15236
- this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
15237
- return null;
15238
- }
15239
15258
  const [serverParams, responseString] = this.getRedirectResponse(hash || "");
15240
15259
  if (!serverParams) {
15241
15260
  // Not a recognized server response hash or hash not associated with a redirect request
15242
15261
  this.logger.info("handleRedirectPromise did not detect a response as a result of a redirect. Cleaning temporary cache.");
15243
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Redirect);
15262
+ this.browserStorage.resetRequestCache();
15244
15263
  // Do not instrument "no_server_response" if user clicked back button
15245
15264
  if (getNavigationType() !== "back_forward") {
15246
15265
  parentMeasurement.event.errorCode = "no_server_response";
@@ -15262,12 +15281,12 @@
15262
15281
  // Replace current hash with non-msal hash, if present
15263
15282
  replaceHash(loginRequestUrl);
15264
15283
  }
15265
- const handleHashResult = await this.handleResponse(serverParams, serverTelemetryManager);
15284
+ const handleHashResult = await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15266
15285
  return handleHashResult;
15267
15286
  }
15268
15287
  else if (!this.config.auth.navigateToLoginRequestUrl) {
15269
15288
  this.logger.verbose("NavigateToLoginRequestUrl set to false, handling response");
15270
- return await this.handleResponse(serverParams, serverTelemetryManager);
15289
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15271
15290
  }
15272
15291
  else if (!isInIframe() ||
15273
15292
  this.config.system.allowRedirectInIframe) {
@@ -15303,7 +15322,7 @@
15303
15322
  }
15304
15323
  // If navigateInternal implementation returns false, handle the hash now
15305
15324
  if (!processHashOnRedirect) {
15306
- return await this.handleResponse(serverParams, serverTelemetryManager);
15325
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15307
15326
  }
15308
15327
  }
15309
15328
  return null;
@@ -15313,7 +15332,6 @@
15313
15332
  e.setCorrelationId(this.correlationId);
15314
15333
  serverTelemetryManager.cacheFailedRequest(e);
15315
15334
  }
15316
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Redirect);
15317
15335
  throw e;
15318
15336
  }
15319
15337
  }
@@ -15366,39 +15384,67 @@
15366
15384
  * @param hash
15367
15385
  * @param state
15368
15386
  */
15369
- async handleResponse(serverParams, serverTelemetryManager) {
15387
+ async handleResponse(serverParams, request, codeVerifier, serverTelemetryManager) {
15370
15388
  const state = serverParams.state;
15371
15389
  if (!state) {
15372
15390
  throw createBrowserAuthError(noStateInHash);
15373
15391
  }
15374
- const cachedRequest = this.browserStorage.getCachedRequest(state);
15375
- this.logger.verbose("handleResponse called, retrieved cached request");
15376
- if (serverParams.accountId) {
15377
- this.logger.verbose("Account id found in hash, calling WAM for token");
15378
- if (!this.nativeMessageHandler) {
15379
- throw createBrowserAuthError(nativeConnectionNotEstablished);
15380
- }
15381
- 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);
15382
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, state);
15383
- return nativeInteractionClient
15384
- .acquireToken({
15385
- ...cachedRequest,
15386
- state: userRequestState,
15387
- prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
15388
- })
15389
- .finally(() => {
15390
- this.browserStorage.cleanRequestByState(state);
15392
+ if (serverParams.ear_jwe) {
15393
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, request.correlationId)({
15394
+ requestAuthority: request.authority,
15395
+ requestAzureCloudOptions: request.azureCloudOptions,
15396
+ requestExtraQueryParameters: request.extraQueryParameters,
15397
+ account: request.account,
15391
15398
  });
15399
+ 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);
15400
+ }
15401
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({ serverTelemetryManager, requestAuthority: request.authority });
15402
+ 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);
15403
+ }
15404
+ /**
15405
+ * Redirects window to given URL.
15406
+ * @param urlNavigate
15407
+ * @param onRedirectNavigateRequest - onRedirectNavigate callback provided on the request
15408
+ */
15409
+ async initiateAuthRequest(requestUrl, onRedirectNavigateRequest) {
15410
+ this.logger.verbose("RedirectHandler.initiateAuthRequest called");
15411
+ // Navigate if valid URL
15412
+ if (requestUrl) {
15413
+ this.logger.infoPii(`RedirectHandler.initiateAuthRequest: Navigate to: ${requestUrl}`);
15414
+ const navigationOptions = {
15415
+ apiId: ApiId.acquireTokenRedirect,
15416
+ timeout: this.config.system.redirectNavigationTimeout,
15417
+ noHistory: false,
15418
+ };
15419
+ const onRedirectNavigate = onRedirectNavigateRequest ||
15420
+ this.config.auth.onRedirectNavigate;
15421
+ // If onRedirectNavigate is implemented, invoke it and provide requestUrl
15422
+ if (typeof onRedirectNavigate === "function") {
15423
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Invoking onRedirectNavigate callback");
15424
+ const navigate = onRedirectNavigate(requestUrl);
15425
+ // Returning false from onRedirectNavigate will stop navigation
15426
+ if (navigate !== false) {
15427
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate did not return false, navigating");
15428
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15429
+ return;
15430
+ }
15431
+ else {
15432
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate returned false, stopping navigation");
15433
+ return;
15434
+ }
15435
+ }
15436
+ else {
15437
+ // Navigate window to request URL
15438
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Navigating window to navigate url");
15439
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15440
+ return;
15441
+ }
15392
15442
  }
15393
- // Hash contains known properties - handle and return in callback
15394
- const currentAuthority = this.browserStorage.getCachedAuthority(state);
15395
- if (!currentAuthority) {
15396
- throw createBrowserAuthError(noCachedAuthorityError);
15443
+ else {
15444
+ // Throw error if request URL is empty.
15445
+ this.logger.info("RedirectHandler.initiateAuthRequest: Navigate url is empty");
15446
+ throw createBrowserAuthError(emptyNavigateUri);
15397
15447
  }
15398
- const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({ serverTelemetryManager, requestAuthority: currentAuthority });
15399
- ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, cachedRequest);
15400
- const interactionHandler = new RedirectHandler(authClient, this.browserStorage, cachedRequest, this.logger, this.performanceClient);
15401
- return interactionHandler.handleCodeResponse(serverParams, state);
15402
15448
  }
15403
15449
  /**
15404
15450
  * Use to log out the current user, and redirect the user to the postLogoutRedirectUri.
@@ -15497,7 +15543,7 @@
15497
15543
  * @param urlNavigate
15498
15544
  * @param userRequestScopes
15499
15545
  */
15500
- async function initiateAuthRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15546
+ async function initiateCodeRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15501
15547
  performanceClient.addQueueMeasurement(PerformanceEvents.SilentHandlerInitiateAuthRequest, correlationId);
15502
15548
  if (!requestUrl) {
15503
15549
  // Throw error if request URL is empty.
@@ -15509,6 +15555,15 @@
15509
15555
  }
15510
15556
  return invoke(loadFrameSync, PerformanceEvents.SilentHandlerLoadFrameSync, logger, performanceClient, correlationId)(requestUrl);
15511
15557
  }
15558
+ async function initiateEarRequest(config, authority, request, logger, performanceClient) {
15559
+ const frame = createHiddenIframe();
15560
+ if (!frame.contentDocument) {
15561
+ throw "No document associated with iframe!";
15562
+ }
15563
+ const form = await getEARForm(frame.contentDocument, config, authority, request, logger, performanceClient);
15564
+ form.submit();
15565
+ return frame;
15566
+ }
15512
15567
  /**
15513
15568
  * Monitors an iframe content window until it loads a url with a known hash, or hits a specified timeout.
15514
15569
  * @param iframe
@@ -15659,19 +15714,34 @@
15659
15714
  }
15660
15715
  // Create silent request
15661
15716
  const silentRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, request.correlationId)(inputRequest, exports.InteractionType.Silent);
15717
+ silentRequest.platformBroker =
15718
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, silentRequest.authenticationScheme);
15662
15719
  preconnect(silentRequest.authority);
15663
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15720
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15721
+ return this.executeEarFlow(silentRequest);
15722
+ }
15723
+ else {
15724
+ return this.executeCodeFlow(silentRequest);
15725
+ }
15726
+ }
15727
+ /**
15728
+ * Executes auth code + PKCE flow
15729
+ * @param request
15730
+ * @returns
15731
+ */
15732
+ async executeCodeFlow(request) {
15664
15733
  let authClient;
15734
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15665
15735
  try {
15666
15736
  // Initialize the client
15667
15737
  authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, request.correlationId)({
15668
15738
  serverTelemetryManager,
15669
- requestAuthority: silentRequest.authority,
15670
- requestAzureCloudOptions: silentRequest.azureCloudOptions,
15671
- requestExtraQueryParameters: silentRequest.extraQueryParameters,
15672
- account: silentRequest.account,
15739
+ requestAuthority: request.authority,
15740
+ requestAzureCloudOptions: request.azureCloudOptions,
15741
+ requestExtraQueryParameters: request.extraQueryParameters,
15742
+ account: request.account,
15673
15743
  });
15674
- return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, silentRequest);
15744
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, request);
15675
15745
  }
15676
15746
  catch (e) {
15677
15747
  if (e instanceof AuthError) {
@@ -15686,10 +15756,33 @@
15686
15756
  this.performanceClient.addFields({
15687
15757
  retryError: e.errorCode,
15688
15758
  }, this.correlationId);
15689
- const retrySilentRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, request.correlationId)(inputRequest, exports.InteractionType.Silent);
15690
- return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, retrySilentRequest);
15759
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, request);
15691
15760
  }
15692
15761
  }
15762
+ /**
15763
+ * Executes EAR flow
15764
+ * @param request
15765
+ */
15766
+ async executeEarFlow(request) {
15767
+ const correlationId = request.correlationId;
15768
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15769
+ requestAuthority: request.authority,
15770
+ requestAzureCloudOptions: request.azureCloudOptions,
15771
+ requestExtraQueryParameters: request.extraQueryParameters,
15772
+ account: request.account,
15773
+ });
15774
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15775
+ const silentRequest = {
15776
+ ...request,
15777
+ earJwk: earJwk,
15778
+ };
15779
+ const msalFrame = await invokeAsync(initiateEarRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(this.config, discoveredAuthority, silentRequest, this.logger, this.performanceClient);
15780
+ const responseType = this.config.auth.OIDCOptions.serverResponseType;
15781
+ // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
15782
+ 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);
15783
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15784
+ 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);
15785
+ }
15693
15786
  /**
15694
15787
  * Currently Unsupported
15695
15788
  */
@@ -15706,44 +15799,20 @@
15706
15799
  async silentTokenHelper(authClient, request) {
15707
15800
  const correlationId = request.correlationId;
15708
15801
  this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentIframeClientTokenHelper, correlationId);
15709
- const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, this.correlationId)(this.performanceClient, this.logger, this.correlationId);
15802
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15710
15803
  const silentRequest = {
15711
15804
  ...request,
15712
15805
  codeChallenge: pkceCodes.challenge,
15713
15806
  };
15714
15807
  // Create authorize request url
15715
- const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, {
15716
- ...silentRequest,
15717
- platformBroker: NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, silentRequest.authenticationScheme),
15718
- }, this.logger, this.performanceClient);
15808
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, silentRequest, this.logger, this.performanceClient);
15719
15809
  // Get the frame handle for the silent request
15720
- const msalFrame = await invokeAsync(initiateAuthRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId, this.config.system.navigateFrameWait);
15810
+ const msalFrame = await invokeAsync(initiateCodeRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId, this.config.system.navigateFrameWait);
15721
15811
  const responseType = this.config.auth.OIDCOptions.serverResponseType;
15722
15812
  // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
15723
15813
  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);
15724
- const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, responseType, this.logger);
15725
- if (serverParams.accountId) {
15726
- this.logger.verbose("Account id found in hash, calling WAM for token");
15727
- if (!this.nativeMessageHandler) {
15728
- throw createBrowserAuthError(nativeConnectionNotEstablished);
15729
- }
15730
- 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);
15731
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, silentRequest.state);
15732
- return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), PerformanceEvents.NativeInteractionClientAcquireToken, this.logger, this.performanceClient, correlationId)({
15733
- ...silentRequest,
15734
- state: userRequestState,
15735
- prompt: silentRequest.prompt || PromptValue.NONE,
15736
- });
15737
- }
15738
- const authCodeRequest = {
15739
- ...silentRequest,
15740
- code: serverParams.code || "",
15741
- codeVerifier: pkceCodes.verifier,
15742
- };
15743
- // Create silent handler
15744
- const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
15745
- // Handle response from hash string
15746
- return invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), PerformanceEvents.HandleCodeResponse, this.logger, this.performanceClient, correlationId)(serverParams, silentRequest);
15814
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15815
+ 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);
15747
15816
  }
15748
15817
  }
15749
15818
 
@@ -16272,31 +16341,38 @@
16272
16341
  * @returns
16273
16342
  */
16274
16343
  async handleRedirectPromiseInternal(hash) {
16344
+ if (!this.browserStorage.isInteractionInProgress(true)) {
16345
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
16346
+ return null;
16347
+ }
16275
16348
  const loggedInAccounts = this.getAllAccounts();
16276
- const request = this.browserStorage.getCachedNativeRequest();
16277
- const useNative = request &&
16349
+ const platformBrokerRequest = this.browserStorage.getCachedNativeRequest();
16350
+ const useNative = platformBrokerRequest &&
16278
16351
  NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeExtensionProvider) &&
16279
16352
  this.nativeExtensionProvider &&
16280
16353
  !hash;
16281
- const correlationId = useNative
16282
- ? request?.correlationId
16283
- : this.browserStorage.getTemporaryCache(TemporaryCacheKeys.CORRELATION_ID, true) || "";
16284
- const rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, correlationId);
16354
+ let rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, platformBrokerRequest?.correlationId || "");
16285
16355
  this.eventHandler.emitEvent(EventType.HANDLE_REDIRECT_START, exports.InteractionType.Redirect);
16286
16356
  let redirectResponse;
16287
16357
  if (useNative && this.nativeExtensionProvider) {
16288
16358
  this.logger.trace("handleRedirectPromise - acquiring token from native platform");
16289
- 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);
16359
+ 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);
16290
16360
  redirectResponse = invokeAsync(nativeClient.handleRedirectPromise.bind(nativeClient), PerformanceEvents.HandleNativeRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(this.performanceClient, rootMeasurement.event.correlationId);
16291
16361
  }
16292
16362
  else {
16363
+ const [standardRequest, codeVerifier] = this.browserStorage.getCachedRequest();
16364
+ const correlationId = standardRequest.correlationId;
16365
+ // Reset rootMeasurement now that we have correlationId
16366
+ rootMeasurement.discard();
16367
+ rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, correlationId);
16293
16368
  this.logger.trace("handleRedirectPromise - acquiring token from web flow");
16294
16369
  const redirectClient = this.createRedirectClient(correlationId);
16295
- redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, rootMeasurement);
16370
+ redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, standardRequest, codeVerifier, rootMeasurement);
16296
16371
  }
16297
16372
  return redirectResponse
16298
16373
  .then((result) => {
16299
16374
  if (result) {
16375
+ this.browserStorage.resetRequestCache();
16300
16376
  // Emit login event if number of accounts change
16301
16377
  const isLoggingIn = loggedInAccounts.length < this.getAllAccounts().length;
16302
16378
  if (isLoggingIn) {
@@ -16328,6 +16404,7 @@
16328
16404
  return result;
16329
16405
  })
16330
16406
  .catch((e) => {
16407
+ this.browserStorage.resetRequestCache();
16331
16408
  const eventError = e;
16332
16409
  // Emit login event if there is an account
16333
16410
  if (loggedInAccounts.length > 0) {
@@ -16421,7 +16498,6 @@
16421
16498
  const redirectClient = this.createRedirectClient(correlationId);
16422
16499
  return redirectClient.acquireToken(request);
16423
16500
  }
16424
- this.browserStorage.setInteractionInProgress(false);
16425
16501
  throw e;
16426
16502
  });
16427
16503
  }
@@ -16432,6 +16508,7 @@
16432
16508
  return await result;
16433
16509
  }
16434
16510
  catch (e) {
16511
+ this.browserStorage.resetRequestCache();
16435
16512
  atrMeasurement.end({ success: false }, e);
16436
16513
  if (isLoggedIn) {
16437
16514
  this.eventHandler.emitEvent(EventType.ACQUIRE_TOKEN_FAILURE, exports.InteractionType.Redirect, null, e);
@@ -16483,7 +16560,6 @@
16483
16560
  correlationId,
16484
16561
  }, ApiId.acquireTokenPopup)
16485
16562
  .then((response) => {
16486
- this.browserStorage.setInteractionInProgress(false);
16487
16563
  atPopupMeasurement.end({
16488
16564
  success: true,
16489
16565
  isNativeBroker: true,
@@ -16503,7 +16579,6 @@
16503
16579
  const popupClient = this.createPopupClient(correlationId);
16504
16580
  return popupClient.acquireToken(request, pkce);
16505
16581
  }
16506
- this.browserStorage.setInteractionInProgress(false);
16507
16582
  throw e;
16508
16583
  });
16509
16584
  }
@@ -16544,8 +16619,12 @@
16544
16619
  // Since this function is syncronous we need to reject
16545
16620
  return Promise.reject(e);
16546
16621
  })
16547
- .finally(() => this.config.system.asyncPopups &&
16548
- this.preGeneratePkceCodes(correlationId));
16622
+ .finally(async () => {
16623
+ this.browserStorage.setInteractionInProgress(false);
16624
+ if (this.config.system.asyncPopups) {
16625
+ await this.preGeneratePkceCodes(correlationId);
16626
+ }
16627
+ });
16549
16628
  }
16550
16629
  trackPageVisibilityWithMeasurement() {
16551
16630
  const measurement = this.ssoSilentMeasurement ||
@@ -16848,7 +16927,9 @@
16848
16927
  preflightCheck$1(this.initialized);
16849
16928
  this.browserStorage.setInteractionInProgress(true);
16850
16929
  const popupClient = this.createPopupClient(correlationId);
16851
- return popupClient.logout(logoutRequest);
16930
+ return popupClient.logout(logoutRequest).finally(() => {
16931
+ this.browserStorage.setInteractionInProgress(false);
16932
+ });
16852
16933
  }
16853
16934
  catch (e) {
16854
16935
  // Since this function is syncronous we need to reject