@azure/msal-browser 4.8.0 → 4.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) 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 +2 -2
  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.mjs +5 -5
  20. package/dist/config/Configuration.mjs.map +1 -1
  21. package/dist/controllers/ControllerFactory.mjs +1 -1
  22. package/dist/controllers/NestedAppAuthController.d.ts +2 -2
  23. package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
  24. package/dist/controllers/NestedAppAuthController.mjs +1 -1
  25. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  26. package/dist/controllers/StandardController.d.ts.map +1 -1
  27. package/dist/controllers/StandardController.mjs +28 -16
  28. package/dist/controllers/StandardController.mjs.map +1 -1
  29. package/dist/controllers/UnknownOperatingContextController.d.ts +2 -2
  30. package/dist/controllers/UnknownOperatingContextController.d.ts.map +1 -1
  31. package/dist/controllers/UnknownOperatingContextController.mjs +1 -1
  32. package/dist/controllers/UnknownOperatingContextController.mjs.map +1 -1
  33. package/dist/crypto/BrowserCrypto.d.ts +11 -0
  34. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  35. package/dist/crypto/BrowserCrypto.mjs +68 -5
  36. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  37. package/dist/crypto/CryptoOps.mjs +1 -1
  38. package/dist/crypto/PkceGenerator.mjs +1 -1
  39. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  40. package/dist/encode/Base64Decode.mjs +1 -1
  41. package/dist/encode/Base64Encode.mjs +1 -1
  42. package/dist/error/BrowserAuthError.d.ts +3 -5
  43. package/dist/error/BrowserAuthError.d.ts.map +1 -1
  44. package/dist/error/BrowserAuthError.mjs +5 -7
  45. package/dist/error/BrowserAuthError.mjs.map +1 -1
  46. package/dist/error/BrowserAuthErrorCodes.d.ts +3 -1
  47. package/dist/error/BrowserAuthErrorCodes.d.ts.map +1 -1
  48. package/dist/error/BrowserAuthErrorCodes.mjs +6 -4
  49. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -1
  50. package/dist/error/BrowserConfigurationAuthError.mjs +2 -2
  51. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
  52. package/dist/error/NativeAuthError.mjs +1 -1
  53. package/dist/error/NativeAuthErrorCodes.mjs +1 -1
  54. package/dist/error/NestedAppAuthError.mjs +1 -1
  55. package/dist/event/EventHandler.mjs +1 -1
  56. package/dist/event/EventMessage.mjs +1 -1
  57. package/dist/event/EventType.mjs +1 -1
  58. package/dist/index.mjs +1 -1
  59. package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
  60. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  61. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  62. package/dist/interaction_client/NativeInteractionClient.mjs +1 -3
  63. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  64. package/dist/interaction_client/PopupClient.d.ts +14 -1
  65. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  66. package/dist/interaction_client/PopupClient.mjs +65 -64
  67. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  68. package/dist/interaction_client/RedirectClient.d.ts +20 -3
  69. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  70. package/dist/interaction_client/RedirectClient.mjs +132 -76
  71. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  72. package/dist/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  73. package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
  74. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  75. package/dist/interaction_client/SilentCacheClient.mjs +1 -1
  76. package/dist/interaction_client/SilentIframeClient.d.ts +13 -3
  77. package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
  78. package/dist/interaction_client/SilentIframeClient.mjs +57 -44
  79. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  80. package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
  81. package/dist/interaction_client/StandardInteractionClient.d.ts +2 -3
  82. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  83. package/dist/interaction_client/StandardInteractionClient.mjs +1 -1
  84. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  85. package/dist/interaction_handler/InteractionHandler.d.ts +4 -5
  86. package/dist/interaction_handler/InteractionHandler.d.ts.map +1 -1
  87. package/dist/interaction_handler/InteractionHandler.mjs +1 -1
  88. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  89. package/dist/interaction_handler/SilentHandler.d.ts +4 -2
  90. package/dist/interaction_handler/SilentHandler.d.ts.map +1 -1
  91. package/dist/interaction_handler/SilentHandler.mjs +15 -5
  92. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  93. package/dist/naa/BridgeError.mjs +1 -1
  94. package/dist/naa/BridgeProxy.mjs +1 -1
  95. package/dist/naa/BridgeStatusCode.mjs +1 -1
  96. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +2 -2
  97. package/dist/navigation/NavigationClient.mjs +1 -1
  98. package/dist/network/FetchClient.mjs +1 -1
  99. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  100. package/dist/operatingcontext/NestedAppOperatingContext.mjs +1 -1
  101. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  102. package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
  103. package/dist/packageMetadata.d.ts +1 -1
  104. package/dist/packageMetadata.mjs +2 -2
  105. package/dist/protocol/Authorize.d.ts +53 -1
  106. package/dist/protocol/Authorize.d.ts.map +1 -1
  107. package/dist/protocol/Authorize.mjs +147 -4
  108. package/dist/protocol/Authorize.mjs.map +1 -1
  109. package/dist/request/AuthorizationUrlRequest.d.ts +2 -4
  110. package/dist/request/AuthorizationUrlRequest.d.ts.map +1 -1
  111. package/dist/request/PopupRequest.d.ts +1 -1
  112. package/dist/request/PopupRequest.d.ts.map +1 -1
  113. package/dist/request/RedirectRequest.d.ts +1 -1
  114. package/dist/request/RedirectRequest.d.ts.map +1 -1
  115. package/dist/request/RequestHelpers.mjs +1 -1
  116. package/dist/request/SsoSilentRequest.d.ts +1 -1
  117. package/dist/request/SsoSilentRequest.d.ts.map +1 -1
  118. package/dist/response/ResponseHandler.mjs +1 -1
  119. package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
  120. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  121. package/dist/utils/BrowserConstants.d.ts +1 -10
  122. package/dist/utils/BrowserConstants.d.ts.map +1 -1
  123. package/dist/utils/BrowserConstants.mjs +2 -15
  124. package/dist/utils/BrowserConstants.mjs.map +1 -1
  125. package/dist/utils/BrowserProtocolUtils.mjs +1 -1
  126. package/dist/utils/BrowserUtils.mjs +1 -1
  127. package/lib/msal-browser.cjs +1588 -1555
  128. package/lib/msal-browser.cjs.map +1 -1
  129. package/lib/msal-browser.js +1588 -1555
  130. package/lib/msal-browser.js.map +1 -1
  131. package/lib/msal-browser.min.js +68 -67
  132. package/lib/types/cache/BrowserCacheManager.d.ts +4 -41
  133. package/lib/types/cache/BrowserCacheManager.d.ts.map +1 -1
  134. package/lib/types/controllers/NestedAppAuthController.d.ts +2 -2
  135. package/lib/types/controllers/NestedAppAuthController.d.ts.map +1 -1
  136. package/lib/types/controllers/StandardController.d.ts.map +1 -1
  137. package/lib/types/controllers/UnknownOperatingContextController.d.ts +2 -2
  138. package/lib/types/controllers/UnknownOperatingContextController.d.ts.map +1 -1
  139. package/lib/types/crypto/BrowserCrypto.d.ts +11 -0
  140. package/lib/types/crypto/BrowserCrypto.d.ts.map +1 -1
  141. package/lib/types/error/BrowserAuthError.d.ts +3 -5
  142. package/lib/types/error/BrowserAuthError.d.ts.map +1 -1
  143. package/lib/types/error/BrowserAuthErrorCodes.d.ts +3 -1
  144. package/lib/types/error/BrowserAuthErrorCodes.d.ts.map +1 -1
  145. package/lib/types/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  146. package/lib/types/interaction_client/PopupClient.d.ts +14 -1
  147. package/lib/types/interaction_client/PopupClient.d.ts.map +1 -1
  148. package/lib/types/interaction_client/RedirectClient.d.ts +20 -3
  149. package/lib/types/interaction_client/RedirectClient.d.ts.map +1 -1
  150. package/lib/types/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  151. package/lib/types/interaction_client/SilentIframeClient.d.ts +13 -3
  152. package/lib/types/interaction_client/SilentIframeClient.d.ts.map +1 -1
  153. package/lib/types/interaction_client/StandardInteractionClient.d.ts +2 -3
  154. package/lib/types/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  155. package/lib/types/interaction_handler/InteractionHandler.d.ts +4 -5
  156. package/lib/types/interaction_handler/InteractionHandler.d.ts.map +1 -1
  157. package/lib/types/interaction_handler/SilentHandler.d.ts +4 -2
  158. package/lib/types/interaction_handler/SilentHandler.d.ts.map +1 -1
  159. package/lib/types/packageMetadata.d.ts +1 -1
  160. package/lib/types/protocol/Authorize.d.ts +53 -1
  161. package/lib/types/protocol/Authorize.d.ts.map +1 -1
  162. package/lib/types/request/AuthorizationUrlRequest.d.ts +2 -4
  163. package/lib/types/request/AuthorizationUrlRequest.d.ts.map +1 -1
  164. package/lib/types/request/PopupRequest.d.ts +1 -1
  165. package/lib/types/request/PopupRequest.d.ts.map +1 -1
  166. package/lib/types/request/RedirectRequest.d.ts +1 -1
  167. package/lib/types/request/RedirectRequest.d.ts.map +1 -1
  168. package/lib/types/request/SsoSilentRequest.d.ts +1 -1
  169. package/lib/types/request/SsoSilentRequest.d.ts.map +1 -1
  170. package/lib/types/utils/BrowserConstants.d.ts +1 -10
  171. package/lib/types/utils/BrowserConstants.d.ts.map +1 -1
  172. package/package.json +2 -2
  173. package/src/cache/BrowserCacheManager.ts +30 -205
  174. package/src/config/Configuration.ts +2 -2
  175. package/src/controllers/NestedAppAuthController.ts +2 -0
  176. package/src/controllers/StandardController.ts +35 -24
  177. package/src/controllers/UnknownOperatingContextController.ts +2 -0
  178. package/src/crypto/BrowserCrypto.ts +91 -2
  179. package/src/error/BrowserAuthError.ts +6 -8
  180. package/src/error/BrowserAuthErrorCodes.ts +3 -1
  181. package/src/interaction_client/NativeInteractionClient.ts +0 -2
  182. package/src/interaction_client/PopupClient.ts +163 -119
  183. package/src/interaction_client/RedirectClient.ts +258 -140
  184. package/src/interaction_client/SilentAuthCodeClient.ts +2 -2
  185. package/src/interaction_client/SilentIframeClient.ts +154 -99
  186. package/src/interaction_client/StandardInteractionClient.ts +3 -3
  187. package/src/interaction_handler/InteractionHandler.ts +4 -4
  188. package/src/interaction_handler/SilentHandler.ts +31 -2
  189. package/src/packageMetadata.ts +1 -1
  190. package/src/protocol/Authorize.ts +356 -2
  191. package/src/request/AuthorizationUrlRequest.ts +3 -5
  192. package/src/request/PopupRequest.ts +1 -0
  193. package/src/request/RedirectRequest.ts +1 -0
  194. package/src/request/SsoSilentRequest.ts +1 -0
  195. package/src/utils/BrowserConstants.ts +1 -10
  196. package/dist/interaction_handler/RedirectHandler.d.ts +0 -33
  197. package/dist/interaction_handler/RedirectHandler.d.ts.map +0 -1
  198. package/dist/interaction_handler/RedirectHandler.mjs +0 -144
  199. package/dist/interaction_handler/RedirectHandler.mjs.map +0 -1
  200. package/lib/types/interaction_handler/RedirectHandler.d.ts +0 -33
  201. package/lib/types/interaction_handler/RedirectHandler.d.ts.map +0 -1
  202. 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.1 2025-03-26 */
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-26 */
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,31 +62,14 @@
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
  };
73
68
  const HttpStatus = {
74
- SUCCESS: 200,
75
- SUCCESS_RANGE_START: 200,
76
- SUCCESS_RANGE_END: 299,
77
- REDIRECT: 302,
78
- CLIENT_ERROR: 400,
79
69
  CLIENT_ERROR_RANGE_START: 400,
80
- BAD_REQUEST: 400,
81
- UNAUTHORIZED: 401,
82
- NOT_FOUND: 404,
83
- REQUEST_TIMEOUT: 408,
84
- TOO_MANY_REQUESTS: 429,
85
70
  CLIENT_ERROR_RANGE_END: 499,
86
- SERVER_ERROR: 500,
87
71
  SERVER_ERROR_RANGE_START: 500,
88
- SERVICE_UNAVAILABLE: 503,
89
- GATEWAY_TIMEOUT: 504,
90
- SERVER_ERROR_RANGE_END: 599,
91
- MULTI_SIDED_ERROR: 600,
92
- };
72
+ SERVER_ERROR_RANGE_END: 599};
93
73
  const OIDC_DEFAULT_SCOPES = [
94
74
  Constants.OPENID_SCOPE,
95
75
  Constants.PROFILE_SCOPE,
@@ -150,8 +130,16 @@
150
130
  PLAIN: "plain",
151
131
  S256: "S256",
152
132
  };
133
+ /**
134
+ * Allowed values for response_type
135
+ */
136
+ const OAuthResponseType = {
137
+ CODE: "code",
138
+ IDTOKEN_TOKEN_REFRESHTOKEN: "id_token token refresh_token",
139
+ };
153
140
  /**
154
141
  * allowed values for server response type
142
+ * @deprecated Use ResponseMode instead
155
143
  */
156
144
  const ServerResponseType = {
157
145
  QUERY: "query",
@@ -161,28 +149,19 @@
161
149
  * allowed values for response_mode
162
150
  */
163
151
  const ResponseMode = {
164
- ...ServerResponseType,
165
- FORM_POST: "form_post",
166
- };
152
+ QUERY: "query"};
167
153
  /**
168
154
  * allowed grant_type
169
155
  */
170
156
  const GrantType = {
171
- IMPLICIT_GRANT: "implicit",
172
157
  AUTHORIZATION_CODE_GRANT: "authorization_code",
173
- CLIENT_CREDENTIALS_GRANT: "client_credentials",
174
- RESOURCE_OWNER_PASSWORD_GRANT: "password",
175
- REFRESH_TOKEN_GRANT: "refresh_token",
176
- DEVICE_CODE_GRANT: "device_code",
177
- JWT_BEARER: "urn:ietf:params:oauth:grant-type:jwt-bearer",
178
- };
158
+ REFRESH_TOKEN_GRANT: "refresh_token"};
179
159
  /**
180
160
  * Account types in Cache
181
161
  */
182
162
  const CacheAccountType = {
183
163
  MSSTS_ACCOUNT_TYPE: "MSSTS",
184
164
  ADFS_ACCOUNT_TYPE: "ADFS",
185
- MSAV1_ACCOUNT_TYPE: "MSA",
186
165
  GENERIC_ACCOUNT_TYPE: "Generic", // NTLM, Kerberos, FBA, Basic etc
187
166
  };
188
167
  /**
@@ -219,7 +198,6 @@
219
198
  };
220
199
  const SERVER_TELEM_CONSTANTS = {
221
200
  SCHEMA_VERSION: 5,
222
- MAX_CUR_HEADER_BYTES: 80,
223
201
  MAX_LAST_HEADER_BYTES: 330,
224
202
  MAX_CACHED_ERRORS: 50,
225
203
  CACHE_KEY: "server-telemetry",
@@ -274,9 +252,7 @@
274
252
  * Region Discovery Outcomes
275
253
  */
276
254
  const RegionDiscoveryOutcomes = {
277
- CONFIGURED_MATCHES_DETECTED: "1",
278
255
  CONFIGURED_NO_AUTO_DETECTION: "2",
279
- CONFIGURED_NOT_DETECTED: "3",
280
256
  AUTO_DETECTION_REQUESTED_SUCCESSFUL: "4",
281
257
  AUTO_DETECTION_REQUESTED_FAILED: "5",
282
258
  };
@@ -303,7 +279,7 @@
303
279
  // Token renewal offset default in seconds
304
280
  const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
305
281
 
306
- /*! @azure/msal-common v15.3.0 2025-03-20 */
282
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
307
283
  /*
308
284
  * Copyright (c) Microsoft Corporation. All rights reserved.
309
285
  * Licensed under the MIT License.
@@ -320,7 +296,7 @@
320
296
  unexpectedError: unexpectedError
321
297
  });
322
298
 
323
- /*! @azure/msal-common v15.3.0 2025-03-20 */
299
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
324
300
 
325
301
  /*
326
302
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -369,7 +345,7 @@
369
345
  : AuthErrorMessages[code]);
370
346
  }
371
347
 
372
- /*! @azure/msal-common v15.3.0 2025-03-20 */
348
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
373
349
  /*
374
350
  * Copyright (c) Microsoft Corporation. All rights reserved.
375
351
  * Licensed under the MIT License.
@@ -467,7 +443,7 @@
467
443
  userTimeoutReached: userTimeoutReached
468
444
  });
469
445
 
470
- /*! @azure/msal-common v15.3.0 2025-03-20 */
446
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
471
447
 
472
448
  /*
473
449
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -719,7 +695,7 @@
719
695
  return new ClientAuthError(errorCode, additionalMessage);
720
696
  }
721
697
 
722
- /*! @azure/msal-common v15.3.0 2025-03-20 */
698
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
723
699
 
724
700
  /*
725
701
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -758,7 +734,7 @@
758
734
  },
759
735
  };
760
736
 
761
- /*! @azure/msal-common v15.3.0 2025-03-20 */
737
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
762
738
 
763
739
  /*
764
740
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -949,12 +925,12 @@
949
925
  }
950
926
  }
951
927
 
952
- /*! @azure/msal-common v15.3.0 2025-03-20 */
928
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
953
929
  /* eslint-disable header/header */
954
930
  const name$1 = "@azure/msal-common";
955
- const version$1 = "15.3.0";
931
+ const version$1 = "15.4.0";
956
932
 
957
- /*! @azure/msal-common v15.3.0 2025-03-20 */
933
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
958
934
  /*
959
935
  * Copyright (c) Microsoft Corporation. All rights reserved.
960
936
  * Licensed under the MIT License.
@@ -974,7 +950,7 @@
974
950
  AzureUsGovernment: "https://login.microsoftonline.us",
975
951
  };
976
952
 
977
- /*! @azure/msal-common v15.3.0 2025-03-20 */
953
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
978
954
 
979
955
  /*
980
956
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1035,7 +1011,7 @@
1035
1011
  }
1036
1012
  }
1037
1013
 
1038
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1014
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1039
1015
  /*
1040
1016
  * Copyright (c) Microsoft Corporation. All rights reserved.
1041
1017
  * Licensed under the MIT License.
@@ -1090,7 +1066,7 @@
1090
1066
  return cachedAtSec > nowSeconds();
1091
1067
  }
1092
1068
 
1093
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1069
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1094
1070
 
1095
1071
  /*
1096
1072
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1417,7 +1393,7 @@
1417
1393
  return metadata.expiresAt <= nowSeconds();
1418
1394
  }
1419
1395
 
1420
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1396
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1421
1397
  /*
1422
1398
  * Copyright (c) Microsoft Corporation. All rights reserved.
1423
1399
  * Licensed under the MIT License.
@@ -1471,7 +1447,7 @@
1471
1447
  urlParseError: urlParseError
1472
1448
  });
1473
1449
 
1474
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1450
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1475
1451
 
1476
1452
  /*
1477
1453
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1609,7 +1585,7 @@
1609
1585
  return new ClientConfigurationError(errorCode);
1610
1586
  }
1611
1587
 
1612
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1588
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1613
1589
  /*
1614
1590
  * Copyright (c) Microsoft Corporation. All rights reserved.
1615
1591
  * Licensed under the MIT License.
@@ -1706,7 +1682,7 @@
1706
1682
  }
1707
1683
  }
1708
1684
 
1709
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1685
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1710
1686
 
1711
1687
  /*
1712
1688
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1897,7 +1873,7 @@
1897
1873
  }
1898
1874
  }
1899
1875
 
1900
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1876
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1901
1877
 
1902
1878
  /*
1903
1879
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -1937,7 +1913,7 @@
1937
1913
  };
1938
1914
  }
1939
1915
 
1940
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1916
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
1941
1917
  /*
1942
1918
  * Copyright (c) Microsoft Corporation. All rights reserved.
1943
1919
  * Licensed under the MIT License.
@@ -2016,7 +1992,7 @@
2016
1992
  return updatedAccountInfo;
2017
1993
  }
2018
1994
 
2019
- /*! @azure/msal-common v15.3.0 2025-03-20 */
1995
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2020
1996
  /*
2021
1997
  * Copyright (c) Microsoft Corporation. All rights reserved.
2022
1998
  * Licensed under the MIT License.
@@ -2031,7 +2007,7 @@
2031
2007
  Ciam: 3,
2032
2008
  };
2033
2009
 
2034
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2010
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2035
2011
  /*
2036
2012
  * Copyright (c) Microsoft Corporation. All rights reserved.
2037
2013
  * Licensed under the MIT License.
@@ -2053,7 +2029,7 @@
2053
2029
  return null;
2054
2030
  }
2055
2031
 
2056
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2032
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2057
2033
  /*
2058
2034
  * Copyright (c) Microsoft Corporation. All rights reserved.
2059
2035
  * Licensed under the MIT License.
@@ -2062,11 +2038,22 @@
2062
2038
  * Protocol modes supported by MSAL.
2063
2039
  */
2064
2040
  const ProtocolMode = {
2041
+ /**
2042
+ * Auth Code + PKCE with Entra ID (formerly AAD) specific optimizations and features
2043
+ */
2065
2044
  AAD: "AAD",
2045
+ /**
2046
+ * Auth Code + PKCE without Entra ID specific optimizations and features. For use only with non-Microsoft owned authorities.
2047
+ * Support is limited for this mode.
2048
+ */
2066
2049
  OIDC: "OIDC",
2050
+ /**
2051
+ * Encrypted Authorize Response (EAR) with Entra ID specific optimizations and features
2052
+ */
2053
+ EAR: "EAR",
2067
2054
  };
2068
2055
 
2069
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2056
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2070
2057
 
2071
2058
  /*
2072
2059
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2162,11 +2149,11 @@
2162
2149
  if (authority.authorityType === AuthorityType.Adfs) {
2163
2150
  account.authorityType = CacheAccountType.ADFS_ACCOUNT_TYPE;
2164
2151
  }
2165
- else if (authority.protocolMode === ProtocolMode.AAD) {
2166
- account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2152
+ else if (authority.protocolMode === ProtocolMode.OIDC) {
2153
+ account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2167
2154
  }
2168
2155
  else {
2169
- account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE;
2156
+ account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE;
2170
2157
  }
2171
2158
  let clientInfo;
2172
2159
  if (accountDetails.clientInfo && base64Decode) {
@@ -2309,7 +2296,7 @@
2309
2296
  }
2310
2297
  }
2311
2298
 
2312
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2299
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2313
2300
 
2314
2301
  /*
2315
2302
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2344,6 +2331,7 @@
2344
2331
  const deserializedHash = Object.fromEntries(new URLSearchParams(normalizedResponse));
2345
2332
  // Check for known response properties
2346
2333
  if (deserializedHash.code ||
2334
+ deserializedHash.ear_jwe ||
2347
2335
  deserializedHash.error ||
2348
2336
  deserializedHash.error_description ||
2349
2337
  deserializedHash.state) {
@@ -2366,7 +2354,7 @@
2366
2354
  return queryParameterArray.join("&");
2367
2355
  }
2368
2356
 
2369
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2357
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2370
2358
 
2371
2359
  /*
2372
2360
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2530,7 +2518,7 @@
2530
2518
  }
2531
2519
  }
2532
2520
 
2533
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2521
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2534
2522
 
2535
2523
  /*
2536
2524
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2561,7 +2549,6 @@
2561
2549
  },
2562
2550
  },
2563
2551
  instanceDiscoveryMetadata: {
2564
- tenant_discovery_endpoint: "https://{canonicalAuthority}/v2.0/.well-known/openid-configuration",
2565
2552
  metadata: [
2566
2553
  {
2567
2554
  preferred_network: "login.microsoftonline.com",
@@ -2670,7 +2657,7 @@
2670
2657
  return null;
2671
2658
  }
2672
2659
 
2673
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2660
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2674
2661
  /*
2675
2662
  * Copyright (c) Microsoft Corporation. All rights reserved.
2676
2663
  * Licensed under the MIT License.
@@ -2678,7 +2665,7 @@
2678
2665
  const cacheQuotaExceededErrorCode = "cache_quota_exceeded";
2679
2666
  const cacheUnknownErrorCode = "cache_error_unknown";
2680
2667
 
2681
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2668
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2682
2669
 
2683
2670
  /*
2684
2671
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -2705,7 +2692,7 @@
2705
2692
  }
2706
2693
  }
2707
2694
 
2708
- /*! @azure/msal-common v15.3.0 2025-03-20 */
2695
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
2709
2696
 
2710
2697
  /*
2711
2698
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3890,7 +3877,7 @@
3890
3877
  }
3891
3878
  }
3892
3879
 
3893
- /*! @azure/msal-common v15.3.0 2025-03-20 */
3880
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
3894
3881
 
3895
3882
  /*
3896
3883
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -3989,7 +3976,7 @@
3989
3976
  return (config.authOptions.authority.options.protocolMode === ProtocolMode.OIDC);
3990
3977
  }
3991
3978
 
3992
- /*! @azure/msal-common v15.3.0 2025-03-20 */
3979
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
3993
3980
  /*
3994
3981
  * Copyright (c) Microsoft Corporation. All rights reserved.
3995
3982
  * Licensed under the MIT License.
@@ -3999,7 +3986,7 @@
3999
3986
  UPN: "UPN",
4000
3987
  };
4001
3988
 
4002
- /*! @azure/msal-common v15.3.0 2025-03-20 */
3989
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4003
3990
  /*
4004
3991
  * Copyright (c) Microsoft Corporation. All rights reserved.
4005
3992
  * Licensed under the MIT License.
@@ -4045,9 +4032,11 @@
4045
4032
  const X_CLIENT_EXTRA_SKU = "x-client-xtra-sku";
4046
4033
  const BROKER_CLIENT_ID = "brk_client_id";
4047
4034
  const BROKER_REDIRECT_URI = "brk_redirect_uri";
4048
- const INSTANCE_AWARE = "instance_aware";
4035
+ const INSTANCE_AWARE = "instance_aware";
4036
+ const EAR_JWK = "ear_jwk";
4037
+ const EAR_JWE_CRYPTO = "ear_jwe_crypto";
4049
4038
 
4050
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4039
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4051
4040
 
4052
4041
  /*
4053
4042
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -4066,10 +4055,12 @@
4066
4055
  }
4067
4056
  }
4068
4057
  /**
4069
- * add response_type = code
4058
+ * Add the given response_type
4059
+ * @param parameters
4060
+ * @param responseType
4070
4061
  */
4071
- function addResponseTypeCode(parameters) {
4072
- parameters.set(RESPONSE_TYPE, Constants.CODE_RESPONSE_TYPE);
4062
+ function addResponseType(parameters, responseType) {
4063
+ parameters.set(RESPONSE_TYPE, responseType);
4073
4064
  }
4074
4065
  /**
4075
4066
  * add response_mode. defaults to query.
@@ -4400,9 +4391,20 @@
4400
4391
  if (!parameters.has(BROKER_REDIRECT_URI)) {
4401
4392
  parameters.set(BROKER_REDIRECT_URI, brokerRedirectUri);
4402
4393
  }
4394
+ }
4395
+ /**
4396
+ * Add EAR (Encrypted Authorize Response) request parameters
4397
+ * @param parameters
4398
+ * @param jwk
4399
+ */
4400
+ function addEARParameters(parameters, jwk) {
4401
+ parameters.set(EAR_JWK, encodeURIComponent(jwk));
4402
+ // ear_jwe_crypto will always have value: {"alg":"dir","enc":"A256GCM"} so we can hardcode this
4403
+ const jweCryptoB64Encoded = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0";
4404
+ parameters.set(EAR_JWE_CRYPTO, jweCryptoB64Encoded);
4403
4405
  }
4404
4406
 
4405
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4407
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4406
4408
  /*
4407
4409
  * Copyright (c) Microsoft Corporation. All rights reserved.
4408
4410
  * Licensed under the MIT License.
@@ -4414,7 +4416,7 @@
4414
4416
  response.hasOwnProperty("jwks_uri"));
4415
4417
  }
4416
4418
 
4417
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4419
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4418
4420
  /*
4419
4421
  * Copyright (c) Microsoft Corporation. All rights reserved.
4420
4422
  * Licensed under the MIT License.
@@ -4424,7 +4426,7 @@
4424
4426
  response.hasOwnProperty("metadata"));
4425
4427
  }
4426
4428
 
4427
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4429
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4428
4430
  /*
4429
4431
  * Copyright (c) Microsoft Corporation. All rights reserved.
4430
4432
  * Licensed under the MIT License.
@@ -4434,7 +4436,7 @@
4434
4436
  response.hasOwnProperty("error_description"));
4435
4437
  }
4436
4438
 
4437
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4439
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4438
4440
  /*
4439
4441
  * Copyright (c) Microsoft Corporation. All rights reserved.
4440
4442
  * Licensed under the MIT License.
@@ -4620,6 +4622,9 @@
4620
4622
  */
4621
4623
  HandleCodeResponseFromServer: "handleCodeResponseFromServer",
4622
4624
  HandleCodeResponse: "handleCodeResponse",
4625
+ HandleResponseEar: "handleResponseEar",
4626
+ HandleResponsePlatformBroker: "handleResponsePlatformBroker",
4627
+ HandleResponseCode: "handleResponseCode",
4623
4628
  UpdateTokenEndpointAuthority: "updateTokenEndpointAuthority",
4624
4629
  /**
4625
4630
  * APIs in Authorization Code Client (msal-common)
@@ -4686,6 +4691,8 @@
4686
4691
  UrlEncodeArr: "urlEncodeArr",
4687
4692
  Encrypt: "encrypt",
4688
4693
  Decrypt: "decrypt",
4694
+ GenerateEarKey: "generateEarKey",
4695
+ DecryptEarResponse: "decryptEarResponse",
4689
4696
  };
4690
4697
  const PerformanceEventAbbreviations = new Map([
4691
4698
  [PerformanceEvents.AcquireTokenByCode, "ATByCode"],
@@ -4803,6 +4810,12 @@
4803
4810
  "HandleCodeResFromServer",
4804
4811
  ],
4805
4812
  [PerformanceEvents.HandleCodeResponse, "HandleCodeResp"],
4813
+ [PerformanceEvents.HandleResponseEar, "HandleRespEar"],
4814
+ [PerformanceEvents.HandleResponseCode, "HandleRespCode"],
4815
+ [
4816
+ PerformanceEvents.HandleResponsePlatformBroker,
4817
+ "HandleRespPlatBroker",
4818
+ ],
4806
4819
  [PerformanceEvents.UpdateTokenEndpointAuthority, "UpdTEndpointAuth"],
4807
4820
  [PerformanceEvents.AuthClientAcquireToken, "AuthClientAT"],
4808
4821
  [PerformanceEvents.AuthClientExecuteTokenRequest, "AuthClientExecTReq"],
@@ -4905,6 +4918,8 @@
4905
4918
  [PerformanceEvents.UrlEncodeArr, "urlEncArr"],
4906
4919
  [PerformanceEvents.Encrypt, "encrypt"],
4907
4920
  [PerformanceEvents.Decrypt, "decrypt"],
4921
+ [PerformanceEvents.GenerateEarKey, "genEarKey"],
4922
+ [PerformanceEvents.DecryptEarResponse, "decryptEarResp"],
4908
4923
  ]);
4909
4924
  /**
4910
4925
  * State of the performance event.
@@ -4913,7 +4928,6 @@
4913
4928
  * @enum {number}
4914
4929
  */
4915
4930
  const PerformanceEventStatus = {
4916
- NotStarted: 0,
4917
4931
  InProgress: 1,
4918
4932
  Completed: 2,
4919
4933
  };
@@ -4934,7 +4948,7 @@
4934
4948
  "encryptedCacheExpiredCount",
4935
4949
  ]);
4936
4950
 
4937
- /*! @azure/msal-common v15.3.0 2025-03-20 */
4951
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
4938
4952
  /*
4939
4953
  * Copyright (c) Microsoft Corporation. All rights reserved.
4940
4954
  * Licensed under the MIT License.
@@ -5030,7 +5044,7 @@
5030
5044
  };
5031
5045
  };
5032
5046
 
5033
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5047
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
5034
5048
 
5035
5049
  /*
5036
5050
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5139,7 +5153,7 @@
5139
5153
  },
5140
5154
  };
5141
5155
 
5142
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5156
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
5143
5157
 
5144
5158
  /*
5145
5159
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -5317,7 +5331,7 @@
5317
5331
  return (authorityUri.PathSegments.length === 1 &&
5318
5332
  !Authority.reservedTenantDomains.has(authorityUri.PathSegments[0]) &&
5319
5333
  this.getAuthorityType(authorityUri) === AuthorityType.Default &&
5320
- this.protocolMode === ProtocolMode.AAD);
5334
+ this.protocolMode !== ProtocolMode.OIDC);
5321
5335
  }
5322
5336
  /**
5323
5337
  * Replaces tenant in url path with current tenant. Defaults to common.
@@ -5364,7 +5378,7 @@
5364
5378
  const canonicalAuthorityHost = this.hostnameAndPort;
5365
5379
  if (this.canonicalAuthority.endsWith("v2.0/") ||
5366
5380
  this.authorityType === AuthorityType.Adfs ||
5367
- (this.protocolMode !== ProtocolMode.AAD &&
5381
+ (this.protocolMode === ProtocolMode.OIDC &&
5368
5382
  !this.isAliasOfKnownMicrosoftAuthority(canonicalAuthorityHost))) {
5369
5383
  return `${this.canonicalAuthority}.well-known/openid-configuration`;
5370
5384
  }
@@ -5978,7 +5992,7 @@
5978
5992
  };
5979
5993
  }
5980
5994
 
5981
- /*! @azure/msal-common v15.3.0 2025-03-20 */
5995
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
5982
5996
 
5983
5997
  /*
5984
5998
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6009,7 +6023,7 @@
6009
6023
  }
6010
6024
  }
6011
6025
 
6012
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6026
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6013
6027
 
6014
6028
  /*
6015
6029
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6028,7 +6042,7 @@
6028
6042
  }
6029
6043
  }
6030
6044
 
6031
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6045
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6032
6046
  /*
6033
6047
  * Copyright (c) Microsoft Corporation. All rights reserved.
6034
6048
  * Licensed under the MIT License.
@@ -6049,7 +6063,7 @@
6049
6063
  };
6050
6064
  }
6051
6065
 
6052
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6066
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6053
6067
 
6054
6068
  /*
6055
6069
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6136,7 +6150,7 @@
6136
6150
  }
6137
6151
  }
6138
6152
 
6139
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6153
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6140
6154
 
6141
6155
  /*
6142
6156
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6166,7 +6180,7 @@
6166
6180
  return new NetworkError(error, httpStatus, responseHeaders);
6167
6181
  }
6168
6182
 
6169
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6183
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6170
6184
 
6171
6185
  /*
6172
6186
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6314,7 +6328,7 @@
6314
6328
  }
6315
6329
  }
6316
6330
 
6317
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6331
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6318
6332
  /*
6319
6333
  * Copyright (c) Microsoft Corporation. All rights reserved.
6320
6334
  * Licensed under the MIT License.
@@ -6340,7 +6354,7 @@
6340
6354
  refreshTokenExpired: refreshTokenExpired
6341
6355
  });
6342
6356
 
6343
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6357
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6344
6358
 
6345
6359
  /*
6346
6360
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6428,7 +6442,7 @@
6428
6442
  return new InteractionRequiredAuthError(errorCode, InteractionRequiredAuthErrorMessages[errorCode]);
6429
6443
  }
6430
6444
 
6431
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6445
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6432
6446
 
6433
6447
  /*
6434
6448
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6500,16 +6514,14 @@
6500
6514
  }
6501
6515
  }
6502
6516
 
6503
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6517
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6504
6518
 
6505
6519
  /*
6506
6520
  * Copyright (c) Microsoft Corporation. All rights reserved.
6507
6521
  * Licensed under the MIT License.
6508
6522
  */
6509
6523
  const KeyLocation = {
6510
- SW: "sw",
6511
- UHW: "uhw",
6512
- };
6524
+ SW: "sw"};
6513
6525
  /** @internal */
6514
6526
  class PopTokenGenerator {
6515
6527
  constructor(cryptoUtils, performanceClient) {
@@ -6584,7 +6596,7 @@
6584
6596
  }
6585
6597
  }
6586
6598
 
6587
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6599
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6588
6600
  /*
6589
6601
  * Copyright (c) Microsoft Corporation. All rights reserved.
6590
6602
  * Licensed under the MIT License.
@@ -6611,7 +6623,7 @@
6611
6623
  }
6612
6624
  }
6613
6625
 
6614
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6626
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6615
6627
 
6616
6628
  /*
6617
6629
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -6942,7 +6954,7 @@
6942
6954
  return baseAccount;
6943
6955
  }
6944
6956
 
6945
- /*! @azure/msal-common v15.3.0 2025-03-20 */
6957
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
6946
6958
 
6947
6959
  /*
6948
6960
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7009,7 +7021,7 @@
7009
7021
  }
7010
7022
  }
7011
7023
 
7012
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7024
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7013
7025
  /*
7014
7026
  * Copyright (c) Microsoft Corporation. All rights reserved.
7015
7027
  * Licensed under the MIT License.
@@ -7027,7 +7039,7 @@
7027
7039
  }
7028
7040
  }
7029
7041
 
7030
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7042
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7031
7043
 
7032
7044
  /*
7033
7045
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7260,7 +7272,7 @@
7260
7272
  }
7261
7273
  }
7262
7274
 
7263
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7275
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7264
7276
 
7265
7277
  /*
7266
7278
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7469,7 +7481,7 @@
7469
7481
  }
7470
7482
  }
7471
7483
 
7472
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7484
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7473
7485
 
7474
7486
  /*
7475
7487
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7567,7 +7579,7 @@
7567
7579
  }
7568
7580
  }
7569
7581
 
7570
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7582
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7571
7583
 
7572
7584
  /*
7573
7585
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7582,7 +7594,7 @@
7582
7594
  },
7583
7595
  };
7584
7596
 
7585
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7597
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7586
7598
 
7587
7599
  /*
7588
7600
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7711,10 +7723,10 @@
7711
7723
  if (request.embeddedClientId) {
7712
7724
  addBrokerParameters(parameters, authOptions.clientId, authOptions.redirectUri);
7713
7725
  }
7714
- if (request.extraQueryParameters) {
7715
- addExtraQueryParameters(parameters, request.extraQueryParameters);
7716
- }
7717
- if (authOptions.instanceAware) {
7726
+ // If extraQueryParameters includes instance_aware its value will be added when extraQueryParameters are added
7727
+ if (authOptions.instanceAware &&
7728
+ (!request.extraQueryParameters ||
7729
+ !Object.keys(request.extraQueryParameters).includes(INSTANCE_AWARE))) {
7718
7730
  addInstanceAware(parameters);
7719
7731
  }
7720
7732
  return parameters;
@@ -7806,7 +7818,7 @@
7806
7818
  return account.idTokenClaims?.login_hint || null;
7807
7819
  }
7808
7820
 
7809
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7821
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7810
7822
 
7811
7823
  /*
7812
7824
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -7864,7 +7876,7 @@
7864
7876
  }
7865
7877
  }
7866
7878
 
7867
- /*! @azure/msal-common v15.3.0 2025-03-20 */
7879
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
7868
7880
 
7869
7881
  /*
7870
7882
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8127,7 +8139,7 @@
8127
8139
  }
8128
8140
  }
8129
8141
 
8130
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8142
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
8131
8143
  /*
8132
8144
  * Copyright (c) Microsoft Corporation. All rights reserved.
8133
8145
  * Licensed under the MIT License.
@@ -8135,7 +8147,7 @@
8135
8147
  const missingKidError = "missing_kid_error";
8136
8148
  const missingAlgError = "missing_alg_error";
8137
8149
 
8138
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8150
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
8139
8151
 
8140
8152
  /*
8141
8153
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8160,7 +8172,7 @@
8160
8172
  return new JoseHeaderError(code, JoseHeaderErrorMessages[code]);
8161
8173
  }
8162
8174
 
8163
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8175
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
8164
8176
 
8165
8177
  /*
8166
8178
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8200,7 +8212,7 @@
8200
8212
  }
8201
8213
  }
8202
8214
 
8203
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8215
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
8204
8216
 
8205
8217
  /*
8206
8218
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8279,7 +8291,7 @@
8279
8291
  }
8280
8292
  }
8281
8293
 
8282
- /*! @azure/msal-common v15.3.0 2025-03-20 */
8294
+ /*! @azure/msal-common v15.4.0 2025-03-26 */
8283
8295
 
8284
8296
  /*
8285
8297
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -8893,6 +8905,8 @@
8893
8905
  * Licensed under the MIT License.
8894
8906
  */
8895
8907
  const pkceNotCreated = "pkce_not_created";
8908
+ const earJwkEmpty = "ear_jwk_empty";
8909
+ const earJweEmpty = "ear_jwe_empty";
8896
8910
  const cryptoNonExistent = "crypto_nonexistent";
8897
8911
  const emptyNavigateUri = "empty_navigate_uri";
8898
8912
  const hashEmptyError = "hash_empty_error";
@@ -8915,7 +8929,6 @@
8915
8929
  const silentPromptValueError = "silent_prompt_value_error";
8916
8930
  const noTokenRequestCacheError = "no_token_request_cache_error";
8917
8931
  const unableToParseTokenRequestCacheError = "unable_to_parse_token_request_cache_error";
8918
- const noCachedAuthorityError = "no_cached_authority_error";
8919
8932
  const authRequestNotSetError = "auth_request_not_set_error";
8920
8933
  const invalidCacheType = "invalid_cache_type";
8921
8934
  const nonBrowserEnvironment = "non_browser_environment";
@@ -8939,7 +8952,8 @@
8939
8952
  const invalidBase64String = "invalid_base64_string";
8940
8953
  const invalidPopTokenRequest = "invalid_pop_token_request";
8941
8954
  const failedToBuildHeaders = "failed_to_build_headers";
8942
- const failedToParseHeaders = "failed_to_parse_headers";
8955
+ const failedToParseHeaders = "failed_to_parse_headers";
8956
+ const failedToDecryptEarResponse = "failed_to_decrypt_ear_response";
8943
8957
 
8944
8958
  var BrowserAuthErrorCodes = /*#__PURE__*/Object.freeze({
8945
8959
  __proto__: null,
@@ -8952,9 +8966,12 @@
8952
8966
  cryptoNonExistent: cryptoNonExistent,
8953
8967
  databaseNotOpen: databaseNotOpen,
8954
8968
  databaseUnavailable: databaseUnavailable,
8969
+ earJweEmpty: earJweEmpty,
8970
+ earJwkEmpty: earJwkEmpty,
8955
8971
  emptyNavigateUri: emptyNavigateUri,
8956
8972
  emptyWindowError: emptyWindowError,
8957
8973
  failedToBuildHeaders: failedToBuildHeaders,
8974
+ failedToDecryptEarResponse: failedToDecryptEarResponse,
8958
8975
  failedToParseHeaders: failedToParseHeaders,
8959
8976
  failedToParseResponse: failedToParseResponse,
8960
8977
  getRequestFailed: getRequestFailed,
@@ -8972,7 +8989,6 @@
8972
8989
  nativeHandshakeTimeout: nativeHandshakeTimeout,
8973
8990
  nativePromptNotSupported: nativePromptNotSupported,
8974
8991
  noAccountError: noAccountError,
8975
- noCachedAuthorityError: noCachedAuthorityError,
8976
8992
  noNetworkConnectivity: noNetworkConnectivity,
8977
8993
  noStateInHash: noStateInHash,
8978
8994
  noTokenRequestCacheError: noTokenRequestCacheError,
@@ -9003,6 +9019,8 @@
9003
9019
  */
9004
9020
  const BrowserAuthErrorMessages = {
9005
9021
  [pkceNotCreated]: "The PKCE code challenge and verifier could not be generated.",
9022
+ [earJwkEmpty]: "No EAR encryption key provided. This is unexpected.",
9023
+ [earJweEmpty]: "Server response does not contain ear_jwe property. This is unexpected.",
9006
9024
  [cryptoNonExistent]: "The crypto object or function is not available.",
9007
9025
  [emptyNavigateUri]: "Navigation URI is empty. Please check stack trace for more info.",
9008
9026
  [hashEmptyError]: `Hash value cannot be processed because it is empty. Please verify that your redirectUri is not clearing the hash. ${ErrorLink}`,
@@ -9025,7 +9043,6 @@
9025
9043
  [silentPromptValueError]: "The value given for the prompt value is not valid for silent requests - must be set to 'none' or 'no_session'.",
9026
9044
  [noTokenRequestCacheError]: "No token request found in cache.",
9027
9045
  [unableToParseTokenRequestCacheError]: "The cached token request could not be parsed.",
9028
- [noCachedAuthorityError]: "No cached authority found.",
9029
9046
  [authRequestNotSetError]: "Auth Request not set. Please ensure initiateAuthRequest was called from the InteractionHandler",
9030
9047
  [invalidCacheType]: "Invalid cache type",
9031
9048
  [nonBrowserEnvironment]: "Login and token requests are not supported in non-browser environments.",
@@ -9050,6 +9067,7 @@
9050
9067
  [invalidPopTokenRequest]: "Invalid PoP token request. The request should not have both a popKid value and signPopToken set to true.",
9051
9068
  [failedToBuildHeaders]: "Failed to build request headers object.",
9052
9069
  [failedToParseHeaders]: "Failed to parse response headers",
9070
+ [failedToDecryptEarResponse]: "Failed to decrypt ear response",
9053
9071
  };
9054
9072
  /**
9055
9073
  * BrowserAuthErrorMessage class containing string constants used by error codes and messages.
@@ -9152,10 +9170,6 @@
9152
9170
  code: unableToParseTokenRequestCacheError,
9153
9171
  desc: BrowserAuthErrorMessages[unableToParseTokenRequestCacheError],
9154
9172
  },
9155
- noCachedAuthorityError: {
9156
- code: noCachedAuthorityError,
9157
- desc: BrowserAuthErrorMessages[noCachedAuthorityError],
9158
- },
9159
9173
  authRequestNotSet: {
9160
9174
  code: authRequestNotSetError,
9161
9175
  desc: BrowserAuthErrorMessages[authRequestNotSetError],
@@ -9267,10 +9281,6 @@
9267
9281
  * Constants
9268
9282
  */
9269
9283
  const BrowserConstants = {
9270
- /**
9271
- * Interaction in progress cache value
9272
- */
9273
- INTERACTION_IN_PROGRESS_VALUE: "interaction_in_progress",
9274
9284
  /**
9275
9285
  * Invalid grant error code
9276
9286
  */
@@ -9323,21 +9333,12 @@
9323
9333
  * Temporary cache keys for MSAL, deleted after any request.
9324
9334
  */
9325
9335
  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
9336
  ORIGIN_URI: "request.origin",
9332
- RENEW_STATUS: "token.renew.status",
9333
9337
  URL_HASH: "urlHash",
9334
9338
  REQUEST_PARAMS: "request.params",
9335
- SCOPES: "scopes",
9339
+ VERIFIER: "code.verifier",
9336
9340
  INTERACTION_STATUS_KEY: "interaction.status",
9337
- CCS_CREDENTIAL: "ccs.credential",
9338
- CORRELATION_ID: "request.correlationId",
9339
9341
  NATIVE_REQUEST: "request.native",
9340
- REDIRECT_CONTEXT: "request.redirect.context",
9341
9342
  };
9342
9343
  const StaticCacheKeys = {
9343
9344
  ACCOUNT_KEYS: "msal.account.keys",
@@ -9707,6 +9708,69 @@
9707
9708
  async function sign(key, data) {
9708
9709
  return window.crypto.subtle.sign(keygenAlgorithmOptions, key, data);
9709
9710
  }
9711
+ /**
9712
+ * Generates Base64 encoded jwk used in the Encrypted Authorize Response (EAR) flow
9713
+ */
9714
+ async function generateEarKey() {
9715
+ const key = await generateBaseKey();
9716
+ const keyStr = urlEncodeArr(new Uint8Array(key));
9717
+ const jwk = {
9718
+ alg: "dir",
9719
+ kty: "oct",
9720
+ k: keyStr,
9721
+ };
9722
+ return base64Encode(JSON.stringify(jwk));
9723
+ }
9724
+ /**
9725
+ * Parses earJwk for encryption key and returns CryptoKey object
9726
+ * @param earJwk
9727
+ * @returns
9728
+ */
9729
+ async function importEarKey(earJwk) {
9730
+ const b64DecodedJwk = base64Decode(earJwk);
9731
+ const jwkJson = JSON.parse(b64DecodedJwk);
9732
+ const rawKey = jwkJson.k;
9733
+ const keyBuffer = base64DecToArr(rawKey);
9734
+ return window.crypto.subtle.importKey(RAW, keyBuffer, AES_GCM, false, [
9735
+ DECRYPT,
9736
+ ]);
9737
+ }
9738
+ /**
9739
+ * Decrypt ear_jwe response returned in the Encrypted Authorize Response (EAR) flow
9740
+ * @param earJwk
9741
+ * @param earJwe
9742
+ * @returns
9743
+ */
9744
+ async function decryptEarResponse(earJwk, earJwe) {
9745
+ const earJweParts = earJwe.split(".");
9746
+ if (earJweParts.length !== 5) {
9747
+ throw createBrowserAuthError(failedToDecryptEarResponse, "jwe_length");
9748
+ }
9749
+ const key = await importEarKey(earJwk).catch(() => {
9750
+ throw createBrowserAuthError(failedToDecryptEarResponse, "import_key");
9751
+ });
9752
+ try {
9753
+ const header = new TextEncoder().encode(earJweParts[0]);
9754
+ const iv = base64DecToArr(earJweParts[2]);
9755
+ const ciphertext = base64DecToArr(earJweParts[3]);
9756
+ const tag = base64DecToArr(earJweParts[4]);
9757
+ const tagLengthBits = tag.byteLength * 8;
9758
+ // Concat ciphertext and tag
9759
+ const encryptedData = new Uint8Array(ciphertext.length + tag.length);
9760
+ encryptedData.set(ciphertext);
9761
+ encryptedData.set(tag, ciphertext.length);
9762
+ const decryptedData = await window.crypto.subtle.decrypt({
9763
+ name: AES_GCM,
9764
+ iv: iv,
9765
+ tagLength: tagLengthBits,
9766
+ additionalData: header,
9767
+ }, key, encryptedData);
9768
+ return new TextDecoder().decode(decryptedData);
9769
+ }
9770
+ catch (e) {
9771
+ throw createBrowserAuthError(failedToDecryptEarResponse, "decrypt");
9772
+ }
9773
+ }
9710
9774
  /**
9711
9775
  * Generates symmetric base encryption key. This may be stored as all encryption/decryption keys will be derived from this one.
9712
9776
  */
@@ -10292,9 +10356,9 @@
10292
10356
  const logger = new Logger(providedSystemOptions.loggerOptions);
10293
10357
  logger.warning(JSON.stringify(createClientConfigurationError(cannotSetOIDCOptions)));
10294
10358
  }
10295
- // Throw an error if user has set allowPlatformBroker to true without being in AAD protocol mode
10359
+ // Throw an error if user has set allowPlatformBroker to true with OIDC protocol mode
10296
10360
  if (userInputAuth?.protocolMode &&
10297
- userInputAuth.protocolMode !== ProtocolMode.AAD &&
10361
+ userInputAuth.protocolMode === ProtocolMode.OIDC &&
10298
10362
  providedSystemOptions?.allowPlatformBroker) {
10299
10363
  throw createClientConfigurationError(cannotAllowPlatformBroker);
10300
10364
  }
@@ -10316,7 +10380,7 @@
10316
10380
 
10317
10381
  /* eslint-disable header/header */
10318
10382
  const name = "@azure/msal-browser";
10319
- const version = "4.8.0";
10383
+ const version = "4.9.1";
10320
10384
 
10321
10385
  /*
10322
10386
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -11637,28 +11701,6 @@
11637
11701
  }
11638
11702
  }
11639
11703
 
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
11704
  /*
11663
11705
  * Copyright (c) Microsoft Corporation. All rights reserved.
11664
11706
  * Licensed under the MIT License.
@@ -12358,177 +12400,53 @@
12358
12400
  }
12359
12401
  return JSON.stringify(key);
12360
12402
  }
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
12403
  /**
12430
12404
  * Reset all temporary cache items
12431
12405
  * @param state
12432
12406
  */
12433
- resetRequestCache(state) {
12407
+ resetRequestCache() {
12434
12408
  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
12409
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
12410
+ this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.VERIFIER));
12448
12411
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
12449
12412
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
12450
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
12451
- this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
12452
12413
  this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
12453
12414
  this.setInteractionInProgress(false);
12454
12415
  }
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");
12416
+ cacheAuthorizeRequest(authCodeRequest, codeVerifier) {
12417
+ this.logger.trace("BrowserCacheManager.cacheAuthorizeRequest called");
12499
12418
  const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
12500
12419
  this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
12420
+ if (codeVerifier) {
12421
+ const encodedVerifier = base64Encode(codeVerifier);
12422
+ this.setTemporaryCache(TemporaryCacheKeys.VERIFIER, encodedVerifier, true);
12423
+ }
12501
12424
  }
12502
12425
  /**
12503
12426
  * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
12504
12427
  */
12505
- getCachedRequest(state) {
12428
+ getCachedRequest() {
12506
12429
  this.logger.trace("BrowserCacheManager.getCachedRequest called");
12507
12430
  // Get token request from cache and parse as TokenExchangeParameters.
12508
12431
  const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
12509
12432
  if (!encodedTokenRequest) {
12510
12433
  throw createBrowserAuthError(noTokenRequestCacheError);
12511
12434
  }
12435
+ const encodedVerifier = this.getTemporaryCache(TemporaryCacheKeys.VERIFIER, true);
12512
12436
  let parsedRequest;
12437
+ let verifier = "";
12513
12438
  try {
12514
12439
  parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
12440
+ if (encodedVerifier) {
12441
+ verifier = base64Decode(encodedVerifier);
12442
+ }
12515
12443
  }
12516
12444
  catch (e) {
12517
12445
  this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
12518
12446
  this.logger.error(`Parsing cached token request threw with error: ${e}`);
12519
12447
  throw createBrowserAuthError(unableToParseTokenRequestCacheError);
12520
12448
  }
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;
12449
+ return [parsedRequest, verifier];
12532
12450
  }
12533
12451
  /**
12534
12452
  * Gets cached native request for redirect flows
@@ -13354,1083 +13272,1103 @@
13354
13272
  * Copyright (c) Microsoft Corporation. All rights reserved.
13355
13273
  * Licensed under the MIT License.
13356
13274
  */
13357
- class SilentCacheClient extends StandardInteractionClient {
13275
+ class NativeMessageHandler {
13276
+ constructor(logger, handshakeTimeoutMs, performanceClient, extensionId) {
13277
+ this.logger = logger;
13278
+ this.handshakeTimeoutMs = handshakeTimeoutMs;
13279
+ this.extensionId = extensionId;
13280
+ this.resolvers = new Map(); // Used for non-handshake messages
13281
+ this.handshakeResolvers = new Map(); // Used for handshake messages
13282
+ this.messageChannel = new MessageChannel();
13283
+ this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
13284
+ this.performanceClient = performanceClient;
13285
+ this.handshakeEvent = performanceClient.startMeasurement(PerformanceEvents.NativeMessageHandlerHandshake);
13286
+ }
13358
13287
  /**
13359
- * Returns unexpired tokens from the cache, if available
13360
- * @param silentRequest
13288
+ * Sends a given message to the extension and resolves with the extension response
13289
+ * @param body
13361
13290
  */
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,
13291
+ async sendMessage(body) {
13292
+ this.logger.trace("NativeMessageHandler - sendMessage called.");
13293
+ const req = {
13294
+ channel: NativeConstants.CHANNEL_ID,
13295
+ extensionId: this.extensionId,
13296
+ responseId: createNewGuid(),
13297
+ body: body,
13298
+ };
13299
+ this.logger.trace("NativeMessageHandler - Sending request to browser extension");
13300
+ this.logger.tracePii(`NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(req)}`);
13301
+ this.messageChannel.port1.postMessage(req);
13302
+ return new Promise((resolve, reject) => {
13303
+ this.resolvers.set(req.responseId, { resolve, reject });
13371
13304
  });
13372
- const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13373
- this.logger.verbose("Silent auth client created");
13305
+ }
13306
+ /**
13307
+ * Returns an instance of the MessageHandler that has successfully established a connection with an extension
13308
+ * @param {Logger} logger
13309
+ * @param {number} handshakeTimeoutMs
13310
+ * @param {IPerformanceClient} performanceClient
13311
+ * @param {ICrypto} crypto
13312
+ */
13313
+ static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
13314
+ logger.trace("NativeMessageHandler - createProvider called.");
13374
13315
  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;
13316
+ const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
13317
+ await preferredProvider.sendHandshakeRequest();
13318
+ return preferredProvider;
13381
13319
  }
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;
13320
+ catch (e) {
13321
+ // If preferred extension fails for whatever reason, fallback to using any installed extension
13322
+ const backupProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient);
13323
+ await backupProvider.sendHandshakeRequest();
13324
+ return backupProvider;
13388
13325
  }
13389
13326
  }
13390
13327
  /**
13391
- * API to silenty clear the browser cache.
13392
- * @param logoutRequest
13328
+ * Send handshake request helper.
13393
13329
  */
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(),
13330
+ async sendHandshakeRequest() {
13331
+ this.logger.trace("NativeMessageHandler - sendHandshakeRequest called.");
13332
+ // Register this event listener before sending handshake
13333
+ window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
13334
+ const req = {
13335
+ channel: NativeConstants.CHANNEL_ID,
13336
+ extensionId: this.extensionId,
13337
+ responseId: createNewGuid(),
13338
+ body: {
13339
+ method: NativeExtensionMethod.HandshakeRequest,
13340
+ },
13341
+ };
13342
+ this.handshakeEvent.add({
13343
+ extensionId: this.extensionId,
13344
+ extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
13424
13345
  });
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,
13346
+ this.messageChannel.port1.onmessage = (event) => {
13347
+ this.onChannelMessage(event);
13435
13348
  };
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({
13349
+ window.postMessage(req, window.origin, [this.messageChannel.port2]);
13350
+ return new Promise((resolve, reject) => {
13351
+ this.handshakeResolvers.set(req.responseId, { resolve, reject });
13352
+ this.timeoutId = window.setTimeout(() => {
13353
+ /*
13354
+ * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
13355
+ * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
13356
+ */
13357
+ window.removeEventListener("message", this.windowListener, false);
13358
+ this.messageChannel.port1.close();
13359
+ this.messageChannel.port2.close();
13360
+ this.handshakeEvent.end({
13361
+ extensionHandshakeTimedOut: true,
13489
13362
  success: false,
13490
- errorCode: error.errorCode,
13491
- subErrorCode: error.subError,
13492
- isNativeBroker: true,
13493
13363
  });
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
- };
13364
+ reject(createBrowserAuthError(nativeHandshakeTimeout));
13365
+ this.handshakeResolvers.delete(req.responseId);
13366
+ }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
13367
+ });
13518
13368
  }
13519
13369
  /**
13520
- * Fetches the tokens from the cache if un-expired
13521
- * @param nativeAccountId
13522
- * @param request
13523
- * @returns authenticationResult
13370
+ * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
13371
+ * @param event
13524
13372
  */
13525
- async acquireTokensFromCache(nativeAccountId, request) {
13526
- if (!nativeAccountId) {
13527
- this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13528
- throw createClientAuthError(noAccountFound);
13373
+ onWindowMessage(event) {
13374
+ this.logger.trace("NativeMessageHandler - onWindowMessage called");
13375
+ // We only accept messages from ourselves
13376
+ if (event.source !== window) {
13377
+ return;
13529
13378
  }
13530
- // fetch the account from browser cache
13531
- const account = this.browserStorage.getBaseAccountInfo({
13532
- nativeAccountId,
13533
- });
13534
- if (!account) {
13535
- throw createClientAuthError(noAccountFound);
13379
+ const request = event.data;
13380
+ if (!request.channel ||
13381
+ request.channel !== NativeConstants.CHANNEL_ID) {
13382
+ return;
13536
13383
  }
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
- };
13384
+ if (request.extensionId && request.extensionId !== this.extensionId) {
13385
+ return;
13550
13386
  }
13551
- catch (e) {
13552
- throw e;
13387
+ if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
13388
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13389
+ /*
13390
+ * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
13391
+ * the proper response.
13392
+ */
13393
+ if (!handshakeResolver) {
13394
+ this.logger.trace(`NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`);
13395
+ return;
13396
+ }
13397
+ // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
13398
+ this.logger.verbose(request.extensionId
13399
+ ? `Extension with id: ${request.extensionId} not installed`
13400
+ : "No extension installed");
13401
+ clearTimeout(this.timeoutId);
13402
+ this.messageChannel.port1.close();
13403
+ this.messageChannel.port2.close();
13404
+ window.removeEventListener("message", this.windowListener, false);
13405
+ this.handshakeEvent.end({
13406
+ success: false,
13407
+ extensionInstalled: false,
13408
+ });
13409
+ handshakeResolver.reject(createBrowserAuthError(nativeExtensionNotInstalled));
13553
13410
  }
13554
13411
  }
13555
13412
  /**
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
13413
+ * Invoked when a message is received from the extension on the MessageChannel port
13414
+ * @param event
13559
13415
  */
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
- };
13416
+ onChannelMessage(event) {
13417
+ this.logger.trace("NativeMessageHandler - onChannelMessage called.");
13418
+ const request = event.data;
13419
+ const resolver = this.resolvers.get(request.responseId);
13420
+ const handshakeResolver = this.handshakeResolvers.get(request.responseId);
13569
13421
  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;
13422
+ const method = request.body.method;
13423
+ if (method === NativeExtensionMethod.Response) {
13424
+ if (!resolver) {
13425
+ return;
13426
+ }
13427
+ const response = request.body.response;
13428
+ this.logger.trace("NativeMessageHandler - Received response from browser extension");
13429
+ this.logger.tracePii(`NativeMessageHandler - Received response from browser extension: ${JSON.stringify(response)}`);
13430
+ if (response.status !== "Success") {
13431
+ resolver.reject(createNativeAuthError(response.code, response.description, response.ext));
13432
+ }
13433
+ else if (response.result) {
13434
+ if (response.result["code"] &&
13435
+ response.result["description"]) {
13436
+ resolver.reject(createNativeAuthError(response.result["code"], response.result["description"], response.result["ext"]));
13437
+ }
13438
+ else {
13439
+ resolver.resolve(response.result);
13440
+ }
13441
+ }
13442
+ else {
13443
+ throw createAuthError(unexpectedError, "Event does not contain result.");
13580
13444
  }
13445
+ this.resolvers.delete(request.responseId);
13446
+ }
13447
+ else if (method === NativeExtensionMethod.HandshakeResponse) {
13448
+ if (!handshakeResolver) {
13449
+ this.logger.trace(`NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`);
13450
+ return;
13451
+ }
13452
+ clearTimeout(this.timeoutId); // Clear setTimeout
13453
+ window.removeEventListener("message", this.windowListener, false); // Remove 'No extension' listener
13454
+ this.extensionId = request.extensionId;
13455
+ this.extensionVersion = request.body.version;
13456
+ this.logger.verbose(`NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`);
13457
+ this.handshakeEvent.end({
13458
+ extensionInstalled: true,
13459
+ success: true,
13460
+ });
13461
+ handshakeResolver.resolve();
13462
+ this.handshakeResolvers.delete(request.responseId);
13463
+ }
13464
+ // Do nothing if method is not Response or HandshakeResponse
13465
+ }
13466
+ catch (err) {
13467
+ this.logger.error("Error parsing response from WAM Extension");
13468
+ this.logger.errorPii(`Error parsing response from WAM Extension: ${err}`);
13469
+ this.logger.errorPii(`Unable to parse ${event}`);
13470
+ if (resolver) {
13471
+ resolver.reject(err);
13472
+ }
13473
+ else if (handshakeResolver) {
13474
+ handshakeResolver.reject(err);
13581
13475
  }
13582
13476
  }
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
13477
  }
13595
13478
  /**
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
13479
+ * Returns the Id for the browser extension this handler is communicating with
13480
+ * @returns
13599
13481
  */
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
- }
13482
+ getExtensionId() {
13483
+ return this.extensionId;
13640
13484
  }
13641
13485
  /**
13642
- * Logout from native platform via browser extension
13643
- * @param request
13486
+ * Returns the version for the browser extension this handler is communicating with
13487
+ * @returns
13644
13488
  */
13645
- logout() {
13646
- this.logger.trace("NativeInteractionClient - logout called.");
13647
- return Promise.reject("Logout not implemented yet");
13489
+ getExtensionVersion() {
13490
+ return this.extensionVersion;
13648
13491
  }
13649
13492
  /**
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
13493
+ * Returns boolean indicating whether or not the request should attempt to use native broker
13494
+ * @param logger
13495
+ * @param config
13496
+ * @param nativeExtensionProvider
13497
+ * @param authenticationScheme
13654
13498
  */
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");
13499
+ static isPlatformBrokerAvailable(config, logger, nativeExtensionProvider, authenticationScheme) {
13500
+ logger.trace("isPlatformBrokerAvailable called");
13501
+ if (!config.system.allowPlatformBroker) {
13502
+ logger.trace("isPlatformBrokerAvailable: allowPlatformBroker is not enabled, returning false");
13503
+ // Developer disabled WAM
13504
+ return false;
13667
13505
  }
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);
13506
+ if (!nativeExtensionProvider) {
13507
+ logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
13508
+ // Extension is not available
13509
+ return false;
13672
13510
  }
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;
13511
+ if (authenticationScheme) {
13512
+ switch (authenticationScheme) {
13513
+ case AuthenticationScheme.BEARER:
13514
+ case AuthenticationScheme.POP:
13515
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is supported, returning true");
13516
+ return true;
13517
+ default:
13518
+ logger.trace("isPlatformBrokerAvailable: authenticationScheme is not supported, returning false");
13519
+ return false;
13520
+ }
13521
+ }
13522
+ return true;
13688
13523
  }
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;
13524
+ }
13525
+
13526
+ /*
13527
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13528
+ * Licensed under the MIT License.
13529
+ */
13530
+ /**
13531
+ * Extracts the BrowserStateObject from the state string.
13532
+ * @param browserCrypto
13533
+ * @param state
13534
+ */
13535
+ function extractBrowserRequestState(browserCrypto, state) {
13536
+ if (!state) {
13537
+ return null;
13699
13538
  }
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);
13539
+ try {
13540
+ const requestStateObj = ProtocolUtils.parseRequestState(browserCrypto, state);
13541
+ return requestStateObj.libraryState.meta;
13542
+ }
13543
+ catch (e) {
13544
+ throw createClientAuthError(invalidState);
13545
+ }
13546
+ }
13547
+
13548
+ /*
13549
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13550
+ * Licensed under the MIT License.
13551
+ */
13552
+ function deserializeResponse(responseString, responseLocation, logger) {
13553
+ // Deserialize hash fragment response parameters.
13554
+ const serverParams = getDeserializedResponse(responseString);
13555
+ if (!serverParams) {
13556
+ if (!stripLeadingHashOrQuery(responseString)) {
13557
+ // Hash or Query string is empty
13558
+ 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.`);
13559
+ throw createBrowserAuthError(hashEmptyError);
13560
+ }
13561
+ else {
13562
+ 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.`);
13563
+ logger.errorPii(`The ${responseLocation} detected is: ${responseString}`);
13564
+ throw createBrowserAuthError(hashDoesNotContainKnownProperties);
13565
+ }
13566
+ }
13567
+ return serverParams;
13568
+ }
13569
+ /**
13570
+ * Returns the interaction type that the response object belongs to
13571
+ */
13572
+ function validateInteractionType(response, browserCrypto, interactionType) {
13573
+ if (!response.state) {
13574
+ throw createBrowserAuthError(noStateInHash);
13575
+ }
13576
+ const platformStateObj = extractBrowserRequestState(browserCrypto, response.state);
13577
+ if (!platformStateObj) {
13578
+ throw createBrowserAuthError(unableToParseState);
13579
+ }
13580
+ if (platformStateObj.interactionType !== interactionType) {
13581
+ throw createBrowserAuthError(stateInteractionTypeMismatch);
13582
+ }
13583
+ }
13584
+
13585
+ /*
13586
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13587
+ * Licensed under the MIT License.
13588
+ */
13589
+ /**
13590
+ * Abstract class which defines operations for a browser interaction handling class.
13591
+ */
13592
+ class InteractionHandler {
13593
+ constructor(authCodeModule, storageImpl, authCodeRequest, logger, performanceClient) {
13594
+ this.authModule = authCodeModule;
13595
+ this.browserStorage = storageImpl;
13596
+ this.authCodeRequest = authCodeRequest;
13597
+ this.logger = logger;
13598
+ this.performanceClient = performanceClient;
13710
13599
  }
13711
13600
  /**
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
13601
+ * Function to handle response parameters from hash.
13602
+ * @param locationHash
13715
13603
  */
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;
13604
+ async handleCodeResponse(response, request) {
13605
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponse, request.correlationId);
13606
+ let authCodeResponse;
13607
+ try {
13608
+ authCodeResponse = getAuthorizationCodePayload(response, request.state);
13609
+ }
13610
+ catch (e) {
13611
+ if (e instanceof ServerError &&
13612
+ e.subError === userCancelled) {
13613
+ // Translate server error caused by user closing native prompt to corresponding first class MSAL error
13614
+ throw createBrowserAuthError(userCancelled);
13727
13615
  }
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);
13616
+ else {
13617
+ throw e;
13742
13618
  }
13743
- return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
13619
+ }
13620
+ return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
13621
+ }
13622
+ /**
13623
+ * Process auth code response from AAD
13624
+ * @param authCodeResponse
13625
+ * @param state
13626
+ * @param authority
13627
+ * @param networkModule
13628
+ * @returns
13629
+ */
13630
+ async handleCodeResponseFromServer(authCodeResponse, request, validateNonce = true) {
13631
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.HandleCodeResponseFromServer, request.correlationId);
13632
+ this.logger.trace("InteractionHandler.handleCodeResponseFromServer called");
13633
+ // Assign code to request
13634
+ this.authCodeRequest.code = authCodeResponse.code;
13635
+ // Check for new cloud instance
13636
+ if (authCodeResponse.cloud_instance_host_name) {
13637
+ await invokeAsync(this.authModule.updateAuthority.bind(this.authModule), PerformanceEvents.UpdateTokenEndpointAuthority, this.logger, this.performanceClient, request.correlationId)(authCodeResponse.cloud_instance_host_name, request.correlationId);
13638
+ }
13639
+ // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
13640
+ if (validateNonce) {
13641
+ // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
13642
+ authCodeResponse.nonce = request.nonce || undefined;
13643
+ }
13644
+ authCodeResponse.state = request.state;
13645
+ // Add CCS parameters if available
13646
+ if (authCodeResponse.client_info) {
13647
+ this.authCodeRequest.clientInfo = authCodeResponse.client_info;
13744
13648
  }
13745
13649
  else {
13746
- return response.access_token;
13650
+ const ccsCred = this.createCcsCredentials(request);
13651
+ if (ccsCred) {
13652
+ this.authCodeRequest.ccsCredential = ccsCred;
13653
+ }
13747
13654
  }
13655
+ // Acquire token with retrieved code.
13656
+ const tokenResponse = (await invokeAsync(this.authModule.acquireToken.bind(this.authModule), PerformanceEvents.AuthClientAcquireToken, this.logger, this.performanceClient, request.correlationId)(this.authCodeRequest, authCodeResponse));
13657
+ return tokenResponse;
13748
13658
  }
13749
13659
  /**
13750
- * Generates authentication result
13751
- * @param response
13752
- * @param request
13753
- * @param idTokenObj
13754
- * @param accountEntity
13755
- * @param authority
13756
- * @param reqTimestamp
13757
- * @returns
13660
+ * Build ccs creds if available
13758
13661
  */
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;
13662
+ createCcsCredentials(request) {
13663
+ if (request.account) {
13664
+ return {
13665
+ credential: request.account.homeAccountId,
13666
+ type: CcsCredentialType.HOME_ACCOUNT_ID,
13667
+ };
13782
13668
  }
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;
13669
+ else if (request.loginHint) {
13670
+ return {
13671
+ credential: request.loginHint,
13672
+ type: CcsCredentialType.UPN,
13673
+ };
13674
+ }
13675
+ return null;
13806
13676
  }
13677
+ }
13678
+
13679
+ /*
13680
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13681
+ * Licensed under the MIT License.
13682
+ */
13683
+ class SilentCacheClient extends StandardInteractionClient {
13807
13684
  /**
13808
- * cache the account entity in browser storage
13809
- * @param accountEntity
13685
+ * Returns unexpired tokens from the cache, if available
13686
+ * @param silentRequest
13810
13687
  */
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}`);
13688
+ async acquireToken(silentRequest) {
13689
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.SilentCacheClientAcquireToken, silentRequest.correlationId);
13690
+ // Telemetry manager only used to increment cacheHits here
13691
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent_silentFlow);
13692
+ const clientConfig = await invokeAsync(this.getClientConfiguration.bind(this), PerformanceEvents.StandardInteractionClientGetClientConfiguration, this.logger, this.performanceClient, this.correlationId)({
13693
+ serverTelemetryManager,
13694
+ requestAuthority: silentRequest.authority,
13695
+ requestAzureCloudOptions: silentRequest.azureCloudOptions,
13696
+ account: silentRequest.account,
13817
13697
  });
13698
+ const silentAuthClient = new SilentFlowClient(clientConfig, this.performanceClient);
13699
+ this.logger.verbose("Silent auth client created");
13700
+ try {
13701
+ const response = await invokeAsync(silentAuthClient.acquireCachedToken.bind(silentAuthClient), PerformanceEvents.SilentFlowClientAcquireCachedToken, this.logger, this.performanceClient, silentRequest.correlationId)(silentRequest);
13702
+ const authResponse = response[0];
13703
+ this.performanceClient.addFields({
13704
+ fromCache: true,
13705
+ }, silentRequest.correlationId);
13706
+ return authResponse;
13707
+ }
13708
+ catch (error) {
13709
+ if (error instanceof BrowserAuthError &&
13710
+ error.errorCode === cryptoKeyNotFound) {
13711
+ this.logger.verbose("Signing keypair for bound access token not found. Refreshing bound access token and generating a new crypto keypair.");
13712
+ }
13713
+ throw error;
13714
+ }
13818
13715
  }
13819
13716
  /**
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
13717
+ * API to silenty clear the browser cache.
13718
+ * @param logoutRequest
13828
13719
  */
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);
13720
+ logout(logoutRequest) {
13721
+ this.logger.verbose("logoutRedirect called");
13722
+ const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
13723
+ return this.clearCacheOnLogout(validLogoutRequest?.account);
13845
13724
  }
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(),
13725
+ }
13726
+
13727
+ /*
13728
+ * Copyright (c) Microsoft Corporation. All rights reserved.
13729
+ * Licensed under the MIT License.
13730
+ */
13731
+ class NativeInteractionClient extends BaseInteractionClient {
13732
+ constructor(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, provider, accountId, nativeStorageImpl, correlationId) {
13733
+ super(config, browserStorage, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13734
+ this.apiId = apiId;
13735
+ this.accountId = accountId;
13736
+ this.nativeMessageHandler = provider;
13737
+ this.nativeStorageManager = nativeStorageImpl;
13738
+ this.silentCacheClient = new SilentCacheClient(config, this.nativeStorageManager, browserCrypto, logger, eventHandler, navigationClient, performanceClient, provider, correlationId);
13739
+ const extensionName = this.nativeMessageHandler.getExtensionId() ===
13740
+ NativeConstants.PREFERRED_EXTENSION_ID
13741
+ ? "chrome"
13742
+ : this.nativeMessageHandler.getExtensionId()?.length
13743
+ ? "unknown"
13744
+ : undefined;
13745
+ this.skus = ServerTelemetryManager.makeExtraSkuString({
13746
+ libraryName: BrowserConstants.MSAL_SKU,
13747
+ libraryVersion: version,
13748
+ extensionName: extensionName,
13853
13749
  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;
13750
+ });
13869
13751
  }
13870
13752
  /**
13871
- * Validates native platform response before processing
13872
- * @param response
13753
+ * Adds SKUs to request extra query parameters
13754
+ * @param request {NativeTokenRequest}
13755
+ * @private
13873
13756
  */
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
- }
13757
+ addRequestSKUs(request) {
13758
+ request.extraParameters = {
13759
+ ...request.extraParameters,
13760
+ [X_CLIENT_EXTRA_SKU]: this.skus,
13761
+ };
13886
13762
  }
13887
13763
  /**
13888
- * Gets MATS telemetry from native response
13889
- * @param response
13890
- * @returns
13764
+ * Acquire token from native platform via browser extension
13765
+ * @param request
13891
13766
  */
13892
- getMATSFromResponse(response) {
13893
- if (response.properties.MATS) {
13767
+ async acquireToken(request, cacheLookupPolicy) {
13768
+ this.performanceClient.addQueueMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13769
+ this.logger.trace("NativeInteractionClient - acquireToken called.");
13770
+ // start the perf measurement
13771
+ const nativeATMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.NativeInteractionClientAcquireToken, request.correlationId);
13772
+ const reqTimestamp = nowSeconds();
13773
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13774
+ try {
13775
+ // initialize native request
13776
+ const nativeRequest = await this.initializeNativeRequest(request);
13777
+ // check if the tokens can be retrieved from internal cache
13894
13778
  try {
13895
- return JSON.parse(response.properties.MATS);
13779
+ const result = await this.acquireTokensFromCache(this.accountId, nativeRequest);
13780
+ nativeATMeasurement.end({
13781
+ success: true,
13782
+ isNativeBroker: false,
13783
+ fromCache: true,
13784
+ });
13785
+ return result;
13896
13786
  }
13897
13787
  catch (e) {
13898
- this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
13788
+ if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
13789
+ this.logger.info("MSAL internal Cache does not contain tokens, return error as per cache policy");
13790
+ throw e;
13791
+ }
13792
+ // continue with a native call for any and all errors
13793
+ this.logger.info("MSAL internal Cache does not contain tokens, proceed to make a native call");
13899
13794
  }
13795
+ const { ...nativeTokenRequest } = nativeRequest;
13796
+ // fall back to native calls
13797
+ const messageBody = {
13798
+ method: NativeExtensionMethod.GetToken,
13799
+ request: nativeTokenRequest,
13800
+ };
13801
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13802
+ const validatedResponse = this.validateNativeResponse(response);
13803
+ return await this.handleNativeResponse(validatedResponse, nativeRequest, reqTimestamp)
13804
+ .then((result) => {
13805
+ nativeATMeasurement.end({
13806
+ success: true,
13807
+ isNativeBroker: true,
13808
+ requestId: result.requestId,
13809
+ });
13810
+ serverTelemetryManager.clearNativeBrokerErrorCode();
13811
+ return result;
13812
+ })
13813
+ .catch((error) => {
13814
+ nativeATMeasurement.end({
13815
+ success: false,
13816
+ errorCode: error.errorCode,
13817
+ subErrorCode: error.subError,
13818
+ isNativeBroker: true,
13819
+ });
13820
+ throw error;
13821
+ });
13900
13822
  }
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;
13823
+ catch (e) {
13824
+ if (e instanceof NativeAuthError) {
13825
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13826
+ }
13827
+ throw e;
13912
13828
  }
13913
- return !!mats.is_cached;
13914
13829
  }
13915
13830
  /**
13916
- * Translates developer provided request object into NativeRequest object
13831
+ * Creates silent flow request
13917
13832
  * @param request
13833
+ * @param cachedAccount
13834
+ * @returns CommonSilentFlowRequest
13918
13835
  */
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(),
13836
+ createSilentCacheRequest(request, cachedAccount) {
13837
+ return {
13838
+ authority: request.authority,
13969
13839
  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,
13840
+ scopes: ScopeSet.fromString(request.scope).asArray(),
13841
+ account: cachedAccount,
13842
+ forceRefresh: false,
13978
13843
  };
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
13844
  }
14015
13845
  /**
14016
- * Handles extra broker request parameters
14017
- * @param request {NativeTokenRequest}
14018
- * @private
13846
+ * Fetches the tokens from the cache if un-expired
13847
+ * @param nativeAccountId
13848
+ * @param request
13849
+ * @returns authenticationResult
14019
13850
  */
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;
13851
+ async acquireTokensFromCache(nativeAccountId, request) {
13852
+ if (!nativeAccountId) {
13853
+ this.logger.warning("NativeInteractionClient:acquireTokensFromCache - No nativeAccountId provided");
13854
+ throw createClientAuthError(noAccountFound);
14027
13855
  }
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;
13856
+ // fetch the account from browser cache
13857
+ const account = this.browserStorage.getBaseAccountInfo({
13858
+ nativeAccountId,
13859
+ });
13860
+ if (!account) {
13861
+ throw createClientAuthError(noAccountFound);
14033
13862
  }
14034
- else if (request.extraParameters) {
14035
- request.redirectUri =
14036
- request.extraParameters[BROKER_REDIRECT_URI];
14037
- child_client_id =
14038
- request.extraParameters[CLIENT_ID];
13863
+ // leverage silent flow for cached tokens retrieval
13864
+ try {
13865
+ const silentRequest = this.createSilentCacheRequest(request, account);
13866
+ const result = await this.silentCacheClient.acquireToken(silentRequest);
13867
+ const fullAccount = {
13868
+ ...account,
13869
+ idTokenClaims: result?.idTokenClaims,
13870
+ idToken: result?.idToken,
13871
+ };
13872
+ return {
13873
+ ...result,
13874
+ account: fullAccount,
13875
+ };
13876
+ }
13877
+ catch (e) {
13878
+ throw e;
14039
13879
  }
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
13880
  }
14067
13881
  /**
14068
- * Sends a given message to the extension and resolves with the extension response
14069
- * @param body
13882
+ * Acquires a token from native platform then redirects to the redirectUri instead of returning the response
13883
+ * @param {RedirectRequest} request
13884
+ * @param {InProgressPerformanceEvent} rootMeasurement
14070
13885
  */
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,
13886
+ async acquireTokenRedirect(request, rootMeasurement) {
13887
+ this.logger.trace("NativeInteractionClient - acquireTokenRedirect called.");
13888
+ const { ...remainingParameters } = request;
13889
+ delete remainingParameters.onRedirectNavigate;
13890
+ const nativeRequest = await this.initializeNativeRequest(remainingParameters);
13891
+ const messageBody = {
13892
+ method: NativeExtensionMethod.GetToken,
13893
+ request: nativeRequest,
14078
13894
  };
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
- });
13895
+ try {
13896
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13897
+ this.validateNativeResponse(response);
13898
+ }
13899
+ catch (e) {
13900
+ // Only throw fatal errors here to allow application to fallback to regular redirect. Otherwise proceed and the error will be thrown in handleRedirectPromise
13901
+ if (e instanceof NativeAuthError) {
13902
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13903
+ serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode);
13904
+ if (isFatalNativeAuthError(e)) {
13905
+ throw e;
13906
+ }
13907
+ }
13908
+ }
13909
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, JSON.stringify(nativeRequest), true);
13910
+ const navigationOptions = {
13911
+ apiId: ApiId.acquireTokenRedirect,
13912
+ timeout: this.config.system.redirectNavigationTimeout,
13913
+ noHistory: false,
13914
+ };
13915
+ const redirectUri = this.config.auth.navigateToLoginRequestUrl
13916
+ ? window.location.href
13917
+ : this.getRedirectUri(request.redirectUri);
13918
+ rootMeasurement.end({ success: true });
13919
+ await this.navigationClient.navigateExternal(redirectUri, navigationOptions); // Need to treat this as external to ensure handleRedirectPromise is run again
14085
13920
  }
14086
13921
  /**
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
13922
+ * If the previous page called native platform for a token using redirect APIs, send the same request again and return the response
13923
+ * @param performanceClient {IPerformanceClient?}
13924
+ * @param correlationId {string?} correlation identifier
14092
13925
  */
14093
- static async createProvider(logger, handshakeTimeoutMs, performanceClient) {
14094
- logger.trace("NativeMessageHandler - createProvider called.");
13926
+ async handleRedirectPromise(performanceClient, correlationId) {
13927
+ this.logger.trace("NativeInteractionClient - handleRedirectPromise called.");
13928
+ if (!this.browserStorage.isInteractionInProgress(true)) {
13929
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
13930
+ return null;
13931
+ }
13932
+ // remove prompt from the request to prevent WAM from prompting twice
13933
+ const cachedRequest = this.browserStorage.getCachedNativeRequest();
13934
+ if (!cachedRequest) {
13935
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise called but there is no cached request, returning null.");
13936
+ if (performanceClient && correlationId) {
13937
+ performanceClient?.addFields({ errorCode: "no_cached_request" }, correlationId);
13938
+ }
13939
+ return null;
13940
+ }
13941
+ const { prompt, ...request } = cachedRequest;
13942
+ if (prompt) {
13943
+ 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.");
13944
+ }
13945
+ this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
13946
+ const messageBody = {
13947
+ method: NativeExtensionMethod.GetToken,
13948
+ request: request,
13949
+ };
13950
+ const reqTimestamp = nowSeconds();
14095
13951
  try {
14096
- const preferredProvider = new NativeMessageHandler(logger, handshakeTimeoutMs, performanceClient, NativeConstants.PREFERRED_EXTENSION_ID);
14097
- await preferredProvider.sendHandshakeRequest();
14098
- return preferredProvider;
13952
+ this.logger.verbose("NativeInteractionClient - handleRedirectPromise sending message to native broker.");
13953
+ const response = await this.nativeMessageHandler.sendMessage(messageBody);
13954
+ this.validateNativeResponse(response);
13955
+ const result = this.handleNativeResponse(response, request, reqTimestamp);
13956
+ const res = await result;
13957
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
13958
+ serverTelemetryManager.clearNativeBrokerErrorCode();
13959
+ return res;
14099
13960
  }
14100
13961
  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;
13962
+ throw e;
14105
13963
  }
14106
13964
  }
14107
13965
  /**
14108
- * Send handshake request helper.
13966
+ * Logout from native platform via browser extension
13967
+ * @param request
14109
13968
  */
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
- });
13969
+ logout() {
13970
+ this.logger.trace("NativeInteractionClient - logout called.");
13971
+ return Promise.reject("Logout not implemented yet");
14148
13972
  }
14149
13973
  /**
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
13974
+ * Transform response from native platform into AuthenticationResult object which will be returned to the end user
13975
+ * @param response
13976
+ * @param request
13977
+ * @param reqTimestamp
14152
13978
  */
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;
13979
+ async handleNativeResponse(response, request, reqTimestamp) {
13980
+ this.logger.trace("NativeInteractionClient - handleNativeResponse called.");
13981
+ // generate identifiers
13982
+ const idTokenClaims = extractTokenClaims(response.id_token, base64Decode);
13983
+ const homeAccountIdentifier = this.createHomeAccountIdentifier(response, idTokenClaims);
13984
+ const cachedhomeAccountId = this.browserStorage.getAccountInfoFilteredBy({
13985
+ nativeAccountId: request.accountId,
13986
+ })?.homeAccountId;
13987
+ // add exception for double brokering, please note this is temporary and will be fortified in future
13988
+ if (request.extraParameters?.child_client_id &&
13989
+ response.account.id !== request.accountId) {
13990
+ this.logger.info("handleNativeServerResponse: Double broker flow detected, ignoring accountId mismatch");
14166
13991
  }
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));
13992
+ else if (homeAccountIdentifier !== cachedhomeAccountId &&
13993
+ response.account.id !== request.accountId) {
13994
+ // 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
13995
+ throw createNativeAuthError(userSwitch);
14190
13996
  }
13997
+ // Get the preferred_cache domain for the given authority
13998
+ const authority = await this.getDiscoveredAuthority({
13999
+ requestAuthority: request.authority,
14000
+ });
14001
+ const baseAccount = buildAccountToCache(this.browserStorage, authority, homeAccountIdentifier, base64Decode, idTokenClaims, response.client_info, undefined, // environment
14002
+ idTokenClaims.tid, undefined, // auth code payload
14003
+ response.account.id, this.logger);
14004
+ // Ensure expires_in is in number format
14005
+ response.expires_in = Number(response.expires_in);
14006
+ // generate authenticationResult
14007
+ const result = await this.generateAuthenticationResult(response, request, idTokenClaims, baseAccount, authority.canonicalAuthority, reqTimestamp);
14008
+ // cache accounts and tokens in the appropriate storage
14009
+ await this.cacheAccount(baseAccount);
14010
+ await this.cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, response.access_token, result.tenantId, reqTimestamp);
14011
+ return result;
14191
14012
  }
14192
14013
  /**
14193
- * Invoked when a message is received from the extension on the MessageChannel port
14194
- * @param event
14014
+ * creates an homeAccountIdentifier for the account
14015
+ * @param response
14016
+ * @param idTokenObj
14017
+ * @returns
14195
14018
  */
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);
14019
+ createHomeAccountIdentifier(response, idTokenClaims) {
14020
+ // Save account in browser storage
14021
+ const homeAccountIdentifier = AccountEntity.generateHomeAccountId(response.client_info || Constants.EMPTY_STRING, AuthorityType.Default, this.logger, this.browserCrypto, idTokenClaims);
14022
+ return homeAccountIdentifier;
14023
+ }
14024
+ /**
14025
+ * Helper to generate scopes
14026
+ * @param response
14027
+ * @param request
14028
+ * @returns
14029
+ */
14030
+ generateScopes(response, request) {
14031
+ return response.scope
14032
+ ? ScopeSet.fromString(response.scope)
14033
+ : ScopeSet.fromString(request.scope);
14034
+ }
14035
+ /**
14036
+ * If PoP token is requesred, records the PoP token if returned from the WAM, else generates one in the browser
14037
+ * @param request
14038
+ * @param response
14039
+ */
14040
+ async generatePopAccessToken(response, request) {
14041
+ if (request.tokenType === AuthenticationScheme.POP &&
14042
+ request.signPopToken) {
14043
+ /**
14044
+ * This code prioritizes SHR returned from the native layer. In case of error/SHR not calculated from WAM and the AT
14045
+ * is still received, SHR is calculated locally
14046
+ */
14047
+ // Check if native layer returned an SHR token
14048
+ if (response.shr) {
14049
+ this.logger.trace("handleNativeServerResponse: SHR is enabled in native layer");
14050
+ return response.shr;
14226
14051
  }
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);
14052
+ // Generate SHR in msal js if WAM does not compute it when POP is enabled
14053
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14054
+ const shrParameters = {
14055
+ resourceRequestMethod: request.resourceRequestMethod,
14056
+ resourceRequestUri: request.resourceRequestUri,
14057
+ shrClaims: request.shrClaims,
14058
+ shrNonce: request.shrNonce,
14059
+ };
14060
+ /**
14061
+ * KeyID must be present in the native request from when the PoP key was generated in order for
14062
+ * PopTokenGenerator to query the full key for signing
14063
+ */
14064
+ if (!request.keyId) {
14065
+ throw createClientAuthError(keyIdMissing);
14243
14066
  }
14244
- // Do nothing if method is not Response or HandshakeResponse
14067
+ return popTokenGenerator.signPopToken(response.access_token, request.keyId, shrParameters);
14245
14068
  }
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
- }
14069
+ else {
14070
+ return response.access_token;
14256
14071
  }
14257
14072
  }
14258
14073
  /**
14259
- * Returns the Id for the browser extension this handler is communicating with
14074
+ * Generates authentication result
14075
+ * @param response
14076
+ * @param request
14077
+ * @param idTokenObj
14078
+ * @param accountEntity
14079
+ * @param authority
14080
+ * @param reqTimestamp
14260
14081
  * @returns
14261
14082
  */
14262
- getExtensionId() {
14263
- return this.extensionId;
14083
+ async generateAuthenticationResult(response, request, idTokenClaims, accountEntity, authority, reqTimestamp) {
14084
+ // Add Native Broker fields to Telemetry
14085
+ const mats = this.addTelemetryFromNativeResponse(response);
14086
+ // If scopes not returned in server response, use request scopes
14087
+ const responseScopes = response.scope
14088
+ ? ScopeSet.fromString(response.scope)
14089
+ : ScopeSet.fromString(request.scope);
14090
+ const accountProperties = response.account.properties || {};
14091
+ const uid = accountProperties["UID"] ||
14092
+ idTokenClaims.oid ||
14093
+ idTokenClaims.sub ||
14094
+ Constants.EMPTY_STRING;
14095
+ const tid = accountProperties["TenantId"] ||
14096
+ idTokenClaims.tid ||
14097
+ Constants.EMPTY_STRING;
14098
+ const accountInfo = updateAccountTenantProfileData(accountEntity.getAccountInfo(), undefined, // tenantProfile optional
14099
+ idTokenClaims, response.id_token);
14100
+ /**
14101
+ * In pairwise broker flows, this check prevents the broker's native account id
14102
+ * from being returned over the embedded app's account id.
14103
+ */
14104
+ if (accountInfo.nativeAccountId !== response.account.id) {
14105
+ accountInfo.nativeAccountId = response.account.id;
14106
+ }
14107
+ // generate PoP token as needed
14108
+ const responseAccessToken = await this.generatePopAccessToken(response, request);
14109
+ const tokenType = request.tokenType === AuthenticationScheme.POP
14110
+ ? AuthenticationScheme.POP
14111
+ : AuthenticationScheme.BEARER;
14112
+ const result = {
14113
+ authority: authority,
14114
+ uniqueId: uid,
14115
+ tenantId: tid,
14116
+ scopes: responseScopes.asArray(),
14117
+ account: accountInfo,
14118
+ idToken: response.id_token,
14119
+ idTokenClaims: idTokenClaims,
14120
+ accessToken: responseAccessToken,
14121
+ fromCache: mats ? this.isResponseFromCache(mats) : false,
14122
+ // Request timestamp and NativeResponse expires_in are in seconds, converting to Date for AuthenticationResult
14123
+ expiresOn: toDateFromSeconds(reqTimestamp + response.expires_in),
14124
+ tokenType: tokenType,
14125
+ correlationId: this.correlationId,
14126
+ state: response.state,
14127
+ fromNativeBroker: true,
14128
+ };
14129
+ return result;
14264
14130
  }
14265
14131
  /**
14266
- * Returns the version for the browser extension this handler is communicating with
14267
- * @returns
14132
+ * cache the account entity in browser storage
14133
+ * @param accountEntity
14268
14134
  */
14269
- getExtensionVersion() {
14270
- return this.extensionVersion;
14135
+ async cacheAccount(accountEntity) {
14136
+ // Store the account info and hence `nativeAccountId` in browser cache
14137
+ await this.browserStorage.setAccount(accountEntity, this.correlationId);
14138
+ // Remove any existing cached tokens for this account in browser storage
14139
+ this.browserStorage.removeAccountContext(accountEntity).catch((e) => {
14140
+ this.logger.error(`Error occurred while removing account context from browser storage. ${e}`);
14141
+ });
14271
14142
  }
14272
14143
  /**
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
14144
+ * Stores the access_token and id_token in inmemory storage
14145
+ * @param response
14146
+ * @param request
14147
+ * @param homeAccountIdentifier
14148
+ * @param idTokenObj
14149
+ * @param responseAccessToken
14150
+ * @param tenantId
14151
+ * @param reqTimestamp
14278
14152
  */
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;
14153
+ cacheNativeTokens(response, request, homeAccountIdentifier, idTokenClaims, responseAccessToken, tenantId, reqTimestamp) {
14154
+ const cachedIdToken = createIdTokenEntity(homeAccountIdentifier, request.authority, response.id_token || "", request.clientId, idTokenClaims.tid || "");
14155
+ // cache accessToken in inmemory storage
14156
+ const expiresIn = request.tokenType === AuthenticationScheme.POP
14157
+ ? Constants.SHR_NONCE_VALIDITY
14158
+ : (typeof response.expires_in === "string"
14159
+ ? parseInt(response.expires_in, 10)
14160
+ : response.expires_in) || 0;
14161
+ const tokenExpirationSeconds = reqTimestamp + expiresIn;
14162
+ const responseScopes = this.generateScopes(response, request);
14163
+ const cachedAccessToken = createAccessTokenEntity(homeAccountIdentifier, request.authority, responseAccessToken, request.clientId, idTokenClaims.tid || tenantId, responseScopes.printScopes(), tokenExpirationSeconds, 0, base64Decode, undefined, request.tokenType, undefined, request.keyId);
14164
+ const nativeCacheRecord = {
14165
+ idToken: cachedIdToken,
14166
+ accessToken: cachedAccessToken,
14167
+ };
14168
+ return this.nativeStorageManager.saveCacheRecord(nativeCacheRecord, this.correlationId, request.storeInCache);
14169
+ }
14170
+ addTelemetryFromNativeResponse(response) {
14171
+ const mats = this.getMATSFromResponse(response);
14172
+ if (!mats) {
14173
+ return null;
14285
14174
  }
14286
- if (!nativeExtensionProvider) {
14287
- logger.trace("isPlatformBrokerAvailable: Platform extension provider is not initialized, returning false");
14288
- // Extension is not available
14289
- return false;
14175
+ this.performanceClient.addFields({
14176
+ extensionId: this.nativeMessageHandler.getExtensionId(),
14177
+ extensionVersion: this.nativeMessageHandler.getExtensionVersion(),
14178
+ matsBrokerVersion: mats.broker_version,
14179
+ matsAccountJoinOnStart: mats.account_join_on_start,
14180
+ matsAccountJoinOnEnd: mats.account_join_on_end,
14181
+ matsDeviceJoin: mats.device_join,
14182
+ matsPromptBehavior: mats.prompt_behavior,
14183
+ matsApiErrorCode: mats.api_error_code,
14184
+ matsUiVisible: mats.ui_visible,
14185
+ matsSilentCode: mats.silent_code,
14186
+ matsSilentBiSubCode: mats.silent_bi_sub_code,
14187
+ matsSilentMessage: mats.silent_message,
14188
+ matsSilentStatus: mats.silent_status,
14189
+ matsHttpStatus: mats.http_status,
14190
+ matsHttpEventCount: mats.http_event_count,
14191
+ }, this.correlationId);
14192
+ return mats;
14193
+ }
14194
+ /**
14195
+ * Validates native platform response before processing
14196
+ * @param response
14197
+ */
14198
+ validateNativeResponse(response) {
14199
+ if (response.hasOwnProperty("access_token") &&
14200
+ response.hasOwnProperty("id_token") &&
14201
+ response.hasOwnProperty("client_info") &&
14202
+ response.hasOwnProperty("account") &&
14203
+ response.hasOwnProperty("scope") &&
14204
+ response.hasOwnProperty("expires_in")) {
14205
+ return response;
14290
14206
  }
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
- }
14207
+ else {
14208
+ throw createAuthError(unexpectedError, "Response missing expected properties.");
14301
14209
  }
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
14210
  }
14321
14211
  /**
14322
- * Function to handle response parameters from hash.
14323
- * @param locationHash
14212
+ * Gets MATS telemetry from native response
14213
+ * @param response
14214
+ * @returns
14324
14215
  */
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);
14216
+ getMATSFromResponse(response) {
14217
+ if (response.properties.MATS) {
14218
+ try {
14219
+ return JSON.parse(response.properties.MATS);
14336
14220
  }
14337
- else {
14338
- throw e;
14221
+ catch (e) {
14222
+ this.logger.error("NativeInteractionClient - Error parsing MATS telemetry, returning null instead");
14339
14223
  }
14340
14224
  }
14341
- return invokeAsync(this.handleCodeResponseFromServer.bind(this), PerformanceEvents.HandleCodeResponseFromServer, this.logger, this.performanceClient, request.correlationId)(authCodeResponse, request);
14225
+ return null;
14342
14226
  }
14343
14227
  /**
14344
- * Process auth code response from AAD
14345
- * @param authCodeResponse
14346
- * @param state
14347
- * @param authority
14348
- * @param networkModule
14228
+ * Returns whether or not response came from native cache
14229
+ * @param response
14349
14230
  * @returns
14350
14231
  */
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
- }
14232
+ isResponseFromCache(mats) {
14233
+ if (typeof mats.is_cached === "undefined") {
14234
+ this.logger.verbose("NativeInteractionClient - MATS telemetry does not contain field indicating if response was served from cache. Returning false.");
14235
+ return false;
14375
14236
  }
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;
14237
+ return !!mats.is_cached;
14379
14238
  }
14380
14239
  /**
14381
- * Build ccs creds if available
14240
+ * Translates developer provided request object into NativeRequest object
14241
+ * @param request
14382
14242
  */
14383
- createCcsCredentials(request) {
14243
+ async initializeNativeRequest(request) {
14244
+ this.logger.trace("NativeInteractionClient - initializeNativeRequest called");
14245
+ const requestAuthority = request.authority || this.config.auth.authority;
14384
14246
  if (request.account) {
14385
- return {
14386
- credential: request.account.homeAccountId,
14387
- type: CcsCredentialType.HOME_ACCOUNT_ID,
14388
- };
14247
+ // validate authority
14248
+ await this.getDiscoveredAuthority({
14249
+ requestAuthority,
14250
+ requestAzureCloudOptions: request.azureCloudOptions,
14251
+ account: request.account,
14252
+ });
14389
14253
  }
14390
- else if (request.loginHint) {
14391
- return {
14392
- credential: request.loginHint,
14393
- type: CcsCredentialType.UPN,
14254
+ const canonicalAuthority = new UrlString(requestAuthority);
14255
+ canonicalAuthority.validateAsUri();
14256
+ // 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.
14257
+ const { scopes, ...remainingProperties } = request;
14258
+ const scopeSet = new ScopeSet(scopes || []);
14259
+ scopeSet.appendScopes(OIDC_DEFAULT_SCOPES);
14260
+ const getPrompt = () => {
14261
+ // If request is silent, prompt is always none
14262
+ switch (this.apiId) {
14263
+ case ApiId.ssoSilent:
14264
+ case ApiId.acquireTokenSilent_silentFlow:
14265
+ this.logger.trace("initializeNativeRequest: silent request sets prompt to none");
14266
+ return PromptValue.NONE;
14267
+ }
14268
+ // Prompt not provided, request may proceed and native broker decides if it needs to prompt
14269
+ if (!request.prompt) {
14270
+ this.logger.trace("initializeNativeRequest: prompt was not provided");
14271
+ return undefined;
14272
+ }
14273
+ // If request is interactive, check if prompt provided is allowed to go directly to native broker
14274
+ switch (request.prompt) {
14275
+ case PromptValue.NONE:
14276
+ case PromptValue.CONSENT:
14277
+ case PromptValue.LOGIN:
14278
+ this.logger.trace("initializeNativeRequest: prompt is compatible with native flow");
14279
+ return request.prompt;
14280
+ default:
14281
+ this.logger.trace(`initializeNativeRequest: prompt = ${request.prompt} is not compatible with native flow`);
14282
+ throw createBrowserAuthError(nativePromptNotSupported);
14283
+ }
14284
+ };
14285
+ const validatedRequest = {
14286
+ ...remainingProperties,
14287
+ accountId: this.accountId,
14288
+ clientId: this.config.auth.clientId,
14289
+ authority: canonicalAuthority.urlString,
14290
+ scope: scopeSet.printScopes(),
14291
+ redirectUri: this.getRedirectUri(request.redirectUri),
14292
+ prompt: getPrompt(),
14293
+ correlationId: this.correlationId,
14294
+ tokenType: request.authenticationScheme,
14295
+ windowTitleSubstring: document.title,
14296
+ extraParameters: {
14297
+ ...request.extraQueryParameters,
14298
+ ...request.tokenQueryParameters,
14299
+ },
14300
+ extendedExpiryToken: false,
14301
+ keyId: request.popKid,
14302
+ };
14303
+ // Check for PoP token requests: signPopToken should only be set to true if popKid is not set
14304
+ if (validatedRequest.signPopToken && !!request.popKid) {
14305
+ throw createBrowserAuthError(invalidPopTokenRequest);
14306
+ }
14307
+ this.handleExtraBrokerParams(validatedRequest);
14308
+ validatedRequest.extraParameters =
14309
+ validatedRequest.extraParameters || {};
14310
+ validatedRequest.extraParameters.telemetry =
14311
+ NativeConstants.MATS_TELEMETRY;
14312
+ if (request.authenticationScheme === AuthenticationScheme.POP) {
14313
+ // add POP request type
14314
+ const shrParameters = {
14315
+ resourceRequestUri: request.resourceRequestUri,
14316
+ resourceRequestMethod: request.resourceRequestMethod,
14317
+ shrClaims: request.shrClaims,
14318
+ shrNonce: request.shrNonce,
14394
14319
  };
14320
+ const popTokenGenerator = new PopTokenGenerator(this.browserCrypto);
14321
+ // generate reqCnf if not provided in the request
14322
+ let reqCnfData;
14323
+ if (!validatedRequest.keyId) {
14324
+ const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, this.logger, this.performanceClient, request.correlationId)(shrParameters, this.logger);
14325
+ reqCnfData = generatedReqCnfData.reqCnfString;
14326
+ validatedRequest.keyId = generatedReqCnfData.kid;
14327
+ validatedRequest.signPopToken = true;
14328
+ }
14329
+ else {
14330
+ reqCnfData = this.browserCrypto.base64UrlEncode(JSON.stringify({ kid: validatedRequest.keyId }));
14331
+ validatedRequest.signPopToken = false;
14332
+ }
14333
+ // SPAs require whole string to be passed to broker
14334
+ validatedRequest.reqCnf = reqCnfData;
14395
14335
  }
14396
- return null;
14336
+ this.addRequestSKUs(validatedRequest);
14337
+ return validatedRequest;
14397
14338
  }
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);
14339
+ /**
14340
+ * Handles extra broker request parameters
14341
+ * @param request {NativeTokenRequest}
14342
+ * @private
14343
+ */
14344
+ handleExtraBrokerParams(request) {
14345
+ const hasExtraBrokerParams = request.extraParameters &&
14346
+ request.extraParameters.hasOwnProperty(BROKER_CLIENT_ID) &&
14347
+ request.extraParameters.hasOwnProperty(BROKER_REDIRECT_URI) &&
14348
+ request.extraParameters.hasOwnProperty(CLIENT_ID);
14349
+ if (!request.embeddedClientId && !hasExtraBrokerParams) {
14350
+ return;
14412
14351
  }
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);
14352
+ let child_client_id = "";
14353
+ const child_redirect_uri = request.redirectUri;
14354
+ if (request.embeddedClientId) {
14355
+ request.redirectUri = this.config.auth.redirectUri;
14356
+ child_client_id = request.embeddedClientId;
14417
14357
  }
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);
14358
+ else if (request.extraParameters) {
14359
+ request.redirectUri =
14360
+ request.extraParameters[BROKER_REDIRECT_URI];
14361
+ child_client_id =
14362
+ request.extraParameters[CLIENT_ID];
14363
+ }
14364
+ request.extraParameters = {
14365
+ child_client_id,
14366
+ child_redirect_uri,
14367
+ };
14368
+ this.performanceClient?.addFields({
14369
+ embeddedClientId: child_client_id,
14370
+ embeddedRedirectUri: child_redirect_uri,
14371
+ }, request.correlationId);
14434
14372
  }
14435
14373
  }
14436
14374
 
@@ -14494,9 +14432,147 @@
14494
14432
  throw createClientConfigurationError(pkceParamsMissing);
14495
14433
  }
14496
14434
  const parameters = await invokeAsync(getStandardParameters, PerformanceEvents.GetStandardParams, logger, performanceClient, request.correlationId)(config, authority, request, logger, performanceClient);
14497
- addResponseTypeCode(parameters);
14435
+ addResponseType(parameters, OAuthResponseType.CODE);
14498
14436
  addCodeChallengeParams(parameters, request.codeChallenge, Constants.S256_CODE_CHALLENGE_METHOD);
14437
+ addExtraQueryParameters(parameters, request.extraQueryParameters || {});
14499
14438
  return getAuthorizeUrl(authority, parameters);
14439
+ }
14440
+ /**
14441
+ * Gets the form that will be posted to /authorize with request parameters when using EAR
14442
+ */
14443
+ async function getEARForm(frame, config, authority, request, logger, performanceClient) {
14444
+ if (!request.earJwk) {
14445
+ throw createBrowserAuthError(earJwkEmpty);
14446
+ }
14447
+ const parameters = await getStandardParameters(config, authority, request, logger, performanceClient);
14448
+ addResponseType(parameters, OAuthResponseType.IDTOKEN_TOKEN_REFRESHTOKEN);
14449
+ addEARParameters(parameters, request.earJwk);
14450
+ const queryParams = new Map();
14451
+ addExtraQueryParameters(queryParams, request.extraQueryParameters || {});
14452
+ const url = getAuthorizeUrl(authority, queryParams);
14453
+ return createForm(frame, url, parameters);
14454
+ }
14455
+ /**
14456
+ * Creates form element in the provided document with auth parameters in the post body
14457
+ * @param frame
14458
+ * @param authorizeUrl
14459
+ * @param parameters
14460
+ * @returns
14461
+ */
14462
+ function createForm(frame, authorizeUrl, parameters) {
14463
+ const form = frame.createElement("form");
14464
+ form.method = "post";
14465
+ form.action = authorizeUrl;
14466
+ parameters.forEach((value, key) => {
14467
+ const param = frame.createElement("input");
14468
+ param.hidden = true;
14469
+ param.name = key;
14470
+ param.value = value;
14471
+ form.appendChild(param);
14472
+ });
14473
+ frame.body.appendChild(form);
14474
+ return form;
14475
+ }
14476
+ /**
14477
+ * Response handler when server returns accountId on the /authorize request
14478
+ * @param request
14479
+ * @param accountId
14480
+ * @param apiId
14481
+ * @param config
14482
+ * @param browserStorage
14483
+ * @param nativeStorage
14484
+ * @param eventHandler
14485
+ * @param logger
14486
+ * @param performanceClient
14487
+ * @param nativeMessageHandler
14488
+ * @returns
14489
+ */
14490
+ async function handleResponsePlatformBroker(request, accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14491
+ if (!nativeMessageHandler) {
14492
+ throw createBrowserAuthError(nativeConnectionNotEstablished);
14493
+ }
14494
+ const browserCrypto = new CryptoOps(logger, performanceClient);
14495
+ const nativeInteractionClient = new NativeInteractionClient(config, browserStorage, browserCrypto, logger, eventHandler, config.system.navigationClient, apiId, performanceClient, nativeMessageHandler, accountId, nativeStorage, request.correlationId);
14496
+ const { userRequestState } = ProtocolUtils.parseRequestState(browserCrypto, request.state);
14497
+ return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), PerformanceEvents.NativeInteractionClientAcquireToken, logger, performanceClient, request.correlationId)({
14498
+ ...request,
14499
+ state: userRequestState,
14500
+ prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
14501
+ });
14502
+ }
14503
+ /**
14504
+ * Response handler when server returns code on the /authorize request
14505
+ * @param request
14506
+ * @param response
14507
+ * @param codeVerifier
14508
+ * @param authClient
14509
+ * @param browserStorage
14510
+ * @param logger
14511
+ * @param performanceClient
14512
+ * @returns
14513
+ */
14514
+ async function handleResponseCode(request, response, codeVerifier, apiId, config, authClient, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14515
+ // Remove throttle if it exists
14516
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14517
+ if (response.accountId) {
14518
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, response.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14519
+ }
14520
+ const authCodeRequest = {
14521
+ ...request,
14522
+ code: response.code || "",
14523
+ codeVerifier: codeVerifier,
14524
+ };
14525
+ // Create popup interaction handler.
14526
+ const interactionHandler = new InteractionHandler(authClient, browserStorage, authCodeRequest, logger, performanceClient);
14527
+ // Handle response from hash string.
14528
+ const result = await invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), PerformanceEvents.HandleCodeResponse, logger, performanceClient, request.correlationId)(response, request);
14529
+ return result;
14530
+ }
14531
+ /**
14532
+ * Response handler when server returns ear_jwe on the /authorize request
14533
+ * @param request
14534
+ * @param response
14535
+ * @param apiId
14536
+ * @param config
14537
+ * @param authority
14538
+ * @param browserStorage
14539
+ * @param nativeStorage
14540
+ * @param eventHandler
14541
+ * @param logger
14542
+ * @param performanceClient
14543
+ * @param nativeMessageHandler
14544
+ * @returns
14545
+ */
14546
+ async function handleResponseEAR(request, response, apiId, config, authority, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler) {
14547
+ // Remove throttle if it exists
14548
+ ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
14549
+ // Validate state & check response for errors
14550
+ validateAuthorizationResponse(response, request.state);
14551
+ if (!response.ear_jwe) {
14552
+ throw createBrowserAuthError(earJweEmpty);
14553
+ }
14554
+ if (!request.earJwk) {
14555
+ throw createBrowserAuthError(earJwkEmpty);
14556
+ }
14557
+ const decryptedData = JSON.parse(await invokeAsync(decryptEarResponse, PerformanceEvents.DecryptEarResponse, logger, performanceClient, request.correlationId)(request.earJwk, response.ear_jwe));
14558
+ if (decryptedData.accountId) {
14559
+ return invokeAsync(handleResponsePlatformBroker, PerformanceEvents.HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, decryptedData.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, nativeMessageHandler);
14560
+ }
14561
+ const responseHandler = new ResponseHandler(config.auth.clientId, browserStorage, new CryptoOps(logger, performanceClient), logger, null, null, performanceClient);
14562
+ // Validate response. This function throws a server error if an error is returned by the server.
14563
+ responseHandler.validateTokenResponse(decryptedData);
14564
+ // Temporary until response handler is refactored to be more flow agnostic.
14565
+ const additionalData = {
14566
+ code: "",
14567
+ state: request.state,
14568
+ nonce: request.nonce,
14569
+ client_info: decryptedData.client_info,
14570
+ cloud_graph_host_name: decryptedData.cloud_graph_host_name,
14571
+ cloud_instance_host_name: decryptedData.cloud_instance_host_name,
14572
+ cloud_instance_name: decryptedData.cloud_instance_name,
14573
+ msgraph_host: decryptedData.msgraph_host,
14574
+ };
14575
+ return (await invokeAsync(responseHandler.handleServerTokenResponse.bind(responseHandler), PerformanceEvents.HandleServerTokenResponse, logger, performanceClient, request.correlationId)(decryptedData, authority, nowSeconds(), request, additionalData, undefined, undefined, undefined, undefined));
14500
14576
  }
14501
14577
 
14502
14578
  /*
@@ -14564,6 +14640,7 @@
14564
14640
  // Properly sets this reference for the unload event.
14565
14641
  this.unloadWindow = this.unloadWindow.bind(this);
14566
14642
  this.nativeStorage = nativeStorageImpl;
14643
+ this.eventHandler = eventHandler;
14567
14644
  }
14568
14645
  /**
14569
14646
  * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
@@ -14639,11 +14716,7 @@
14639
14716
  */
14640
14717
  async acquireTokenPopupAsync(request, popupParams, pkceCodes) {
14641
14718
  this.logger.verbose("acquireTokenPopupAsync called");
14642
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14643
14719
  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
14720
  /*
14648
14721
  * Skip pre-connect for async popups to reduce time between user interaction and popup window creation to avoid
14649
14722
  * popup from being blocked by browsers with shorter popup timers
@@ -14651,65 +14724,49 @@
14651
14724
  if (popupParams.popup) {
14652
14725
  preconnect(validRequest.authority);
14653
14726
  }
14727
+ const isPlatformBroker = NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
14728
+ validRequest.platformBroker = isPlatformBroker;
14729
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
14730
+ return this.executeEarFlow(validRequest, popupParams);
14731
+ }
14732
+ else {
14733
+ return this.executeCodeFlow(validRequest, popupParams, pkceCodes);
14734
+ }
14735
+ }
14736
+ /**
14737
+ * Executes auth code + PKCE flow
14738
+ * @param request
14739
+ * @param popupParams
14740
+ * @param pkceCodes
14741
+ * @returns
14742
+ */
14743
+ async executeCodeFlow(request, popupParams, pkceCodes) {
14744
+ const correlationId = request.correlationId;
14745
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
14746
+ const pkce = pkceCodes ||
14747
+ (await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId));
14748
+ const popupRequest = {
14749
+ ...request,
14750
+ codeChallenge: pkce.challenge,
14751
+ };
14654
14752
  try {
14655
14753
  // Initialize the client
14656
- const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
14754
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, correlationId)({
14657
14755
  serverTelemetryManager,
14658
- requestAuthority: validRequest.authority,
14659
- requestAzureCloudOptions: validRequest.azureCloudOptions,
14660
- requestExtraQueryParameters: validRequest.extraQueryParameters,
14661
- account: validRequest.account,
14756
+ requestAuthority: popupRequest.authority,
14757
+ requestAzureCloudOptions: popupRequest.azureCloudOptions,
14758
+ requestExtraQueryParameters: popupRequest.extraQueryParameters,
14759
+ account: popupRequest.account,
14662
14760
  });
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
14761
  // 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);
14762
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, popupRequest, this.logger, this.performanceClient);
14675
14763
  // Show the UI once the url has been created. Get the window handle for the popup.
14676
14764
  const popupWindow = this.initiateAuthRequest(navigateUrl, popupParams);
14677
14765
  this.eventHandler.emitEvent(EventType.POPUP_OPENED, exports.InteractionType.Popup, { popupWindow }, null);
14678
14766
  // 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
14767
  const responseString = await this.monitorPopupForHash(popupWindow, popupParams.popupWindowParent);
14680
14768
  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;
14769
+ 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
14770
  }
14714
14771
  catch (e) {
14715
14772
  // Close the synchronous popup if an error is thrown before the window unload event is registered
@@ -14721,6 +14778,32 @@
14721
14778
  throw e;
14722
14779
  }
14723
14780
  }
14781
+ /**
14782
+ * Executes EAR flow
14783
+ * @param request
14784
+ */
14785
+ async executeEarFlow(request, popupParams) {
14786
+ const correlationId = request.correlationId;
14787
+ // Get the frame handle for the silent request
14788
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
14789
+ requestAuthority: request.authority,
14790
+ requestAzureCloudOptions: request.azureCloudOptions,
14791
+ requestExtraQueryParameters: request.extraQueryParameters,
14792
+ account: request.account,
14793
+ });
14794
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
14795
+ const popupRequest = {
14796
+ ...request,
14797
+ earJwk: earJwk,
14798
+ };
14799
+ const popupWindow = popupParams.popup || this.openPopup("about:blank", popupParams);
14800
+ const form = await getEARForm(popupWindow.document, this.config, discoveredAuthority, popupRequest, this.logger, this.performanceClient);
14801
+ form.submit();
14802
+ // Monitor the popup for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
14803
+ const responseString = await invokeAsync(this.monitorPopupForHash.bind(this), PerformanceEvents.SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(popupWindow, popupParams.popupWindowParent);
14804
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId)(responseString, this.config.auth.OIDCOptions.serverResponseType, this.logger);
14805
+ 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);
14806
+ }
14724
14807
  /**
14725
14808
  *
14726
14809
  * @param validRequest
@@ -14796,7 +14879,6 @@
14796
14879
  e.setCorrelationId(this.correlationId);
14797
14880
  serverTelemetryManager.cacheFailedRequest(e);
14798
14881
  }
14799
- this.browserStorage.setInteractionInProgress(false);
14800
14882
  this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, exports.InteractionType.Popup, null, e);
14801
14883
  this.eventHandler.emitEvent(EventType.LOGOUT_END, exports.InteractionType.Popup);
14802
14884
  throw e;
@@ -14908,7 +14990,6 @@
14908
14990
  }
14909
14991
  catch (e) {
14910
14992
  this.logger.error("error opening popup " + e.message);
14911
- this.browserStorage.setInteractionInProgress(false);
14912
14993
  throw createBrowserAuthError(popupWindowError);
14913
14994
  }
14914
14995
  }
@@ -14966,7 +15047,6 @@
14966
15047
  * Event callback to unload main window.
14967
15048
  */
14968
15049
  unloadWindow(e) {
14969
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Popup);
14970
15050
  if (this.currentWindow) {
14971
15051
  this.currentWindow.close();
14972
15052
  }
@@ -14982,8 +15062,6 @@
14982
15062
  popupWindow.close();
14983
15063
  // Remove window unload function
14984
15064
  popupWindowParent.removeEventListener("beforeunload", this.unloadWindow);
14985
- // Interaction is completed - remove interaction status.
14986
- this.browserStorage.setInteractionInProgress(false);
14987
15065
  }
14988
15066
  /**
14989
15067
  * Generates the name for the popup based on the client id and request
@@ -15004,141 +15082,6 @@
15004
15082
  }
15005
15083
  }
15006
15084
 
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
15085
  /*
15143
15086
  * Copyright (c) Microsoft Corporation. All rights reserved.
15144
15087
  * Licensed under the MIT License.
@@ -15166,62 +15109,96 @@
15166
15109
  */
15167
15110
  async acquireToken(request) {
15168
15111
  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);
15112
+ validRequest.platformBroker =
15113
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
15173
15114
  const handleBackButton = (event) => {
15174
15115
  // Clear temporary cache if the back button is clicked during the redirect flow.
15175
15116
  if (event.persisted) {
15176
15117
  this.logger.verbose("Page was restored from back/forward cache. Clearing temporary cache.");
15177
- this.browserStorage.cleanRequestByState(validRequest.state);
15118
+ this.browserStorage.resetRequestCache();
15178
15119
  this.eventHandler.emitEvent(EventType.RESTORE_FROM_BFCACHE, exports.InteractionType.Redirect);
15179
15120
  }
15180
15121
  };
15122
+ const redirectStartPage = this.getRedirectStartPage(request.redirectStartPage);
15123
+ this.logger.verbosePii(`Redirect start page: ${redirectStartPage}`);
15124
+ // Cache start page, returns to this page after redirectUri if navigateToLoginRequestUrl is true
15125
+ this.browserStorage.setTemporaryCache(TemporaryCacheKeys.ORIGIN_URI, redirectStartPage, true);
15126
+ // Clear temporary cache if the back button is clicked during the redirect flow.
15127
+ window.addEventListener("pageshow", handleBackButton);
15128
+ try {
15129
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15130
+ await this.executeEarFlow(validRequest);
15131
+ }
15132
+ else {
15133
+ await this.executeCodeFlow(validRequest, request.onRedirectNavigate);
15134
+ }
15135
+ }
15136
+ catch (e) {
15137
+ if (e instanceof AuthError) {
15138
+ e.setCorrelationId(this.correlationId);
15139
+ }
15140
+ window.removeEventListener("pageshow", handleBackButton);
15141
+ throw e;
15142
+ }
15143
+ }
15144
+ /**
15145
+ * Executes auth code + PKCE flow
15146
+ * @param request
15147
+ * @returns
15148
+ */
15149
+ async executeCodeFlow(request, onRedirectNavigate) {
15150
+ const correlationId = request.correlationId;
15151
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenRedirect);
15152
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15153
+ const redirectRequest = {
15154
+ ...request,
15155
+ codeChallenge: pkceCodes.challenge,
15156
+ };
15157
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest, pkceCodes.verifier);
15181
15158
  try {
15182
15159
  // Initialize the client
15183
15160
  const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
15184
15161
  serverTelemetryManager,
15185
- requestAuthority: validRequest.authority,
15186
- requestAzureCloudOptions: validRequest.azureCloudOptions,
15187
- requestExtraQueryParameters: validRequest.extraQueryParameters,
15188
- account: validRequest.account,
15162
+ requestAuthority: redirectRequest.authority,
15163
+ requestAzureCloudOptions: redirectRequest.azureCloudOptions,
15164
+ requestExtraQueryParameters: redirectRequest.extraQueryParameters,
15165
+ account: redirectRequest.account,
15189
15166
  });
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
15167
  // 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);
15168
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, request.correlationId)(this.config, authClient.authority, redirectRequest, this.logger, this.performanceClient);
15206
15169
  // 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
- });
15170
+ return await this.initiateAuthRequest(navigateUrl, onRedirectNavigate);
15214
15171
  }
15215
15172
  catch (e) {
15216
15173
  if (e instanceof AuthError) {
15217
15174
  e.setCorrelationId(this.correlationId);
15218
15175
  serverTelemetryManager.cacheFailedRequest(e);
15219
15176
  }
15220
- window.removeEventListener("pageshow", handleBackButton);
15221
- this.browserStorage.cleanRequestByState(validRequest.state);
15222
15177
  throw e;
15223
15178
  }
15224
15179
  }
15180
+ /**
15181
+ * Executes EAR flow
15182
+ * @param request
15183
+ */
15184
+ async executeEarFlow(request) {
15185
+ const correlationId = request.correlationId;
15186
+ // Get the frame handle for the silent request
15187
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15188
+ requestAuthority: request.authority,
15189
+ requestAzureCloudOptions: request.azureCloudOptions,
15190
+ requestExtraQueryParameters: request.extraQueryParameters,
15191
+ account: request.account,
15192
+ });
15193
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15194
+ const redirectRequest = {
15195
+ ...request,
15196
+ earJwk: earJwk,
15197
+ };
15198
+ this.browserStorage.cacheAuthorizeRequest(redirectRequest);
15199
+ const form = await getEARForm(document, this.config, discoveredAuthority, redirectRequest, this.logger, this.performanceClient);
15200
+ form.submit();
15201
+ }
15225
15202
  /**
15226
15203
  * Checks if navigateToLoginRequestUrl is set, and:
15227
15204
  * - if true, performs logic to cache and navigate
@@ -15229,18 +15206,14 @@
15229
15206
  * @param hash {string} url hash
15230
15207
  * @param parentMeasurement {InProgressPerformanceEvent} parent measurement
15231
15208
  */
15232
- async handleRedirectPromise(hash = "", parentMeasurement) {
15209
+ async handleRedirectPromise(hash = "", request, pkceVerifier, parentMeasurement) {
15233
15210
  const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.handleRedirectPromise);
15234
15211
  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
15212
  const [serverParams, responseString] = this.getRedirectResponse(hash || "");
15240
15213
  if (!serverParams) {
15241
15214
  // Not a recognized server response hash or hash not associated with a redirect request
15242
15215
  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);
15216
+ this.browserStorage.resetRequestCache();
15244
15217
  // Do not instrument "no_server_response" if user clicked back button
15245
15218
  if (getNavigationType() !== "back_forward") {
15246
15219
  parentMeasurement.event.errorCode = "no_server_response";
@@ -15262,12 +15235,12 @@
15262
15235
  // Replace current hash with non-msal hash, if present
15263
15236
  replaceHash(loginRequestUrl);
15264
15237
  }
15265
- const handleHashResult = await this.handleResponse(serverParams, serverTelemetryManager);
15238
+ const handleHashResult = await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15266
15239
  return handleHashResult;
15267
15240
  }
15268
15241
  else if (!this.config.auth.navigateToLoginRequestUrl) {
15269
15242
  this.logger.verbose("NavigateToLoginRequestUrl set to false, handling response");
15270
- return await this.handleResponse(serverParams, serverTelemetryManager);
15243
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15271
15244
  }
15272
15245
  else if (!isInIframe() ||
15273
15246
  this.config.system.allowRedirectInIframe) {
@@ -15303,7 +15276,7 @@
15303
15276
  }
15304
15277
  // If navigateInternal implementation returns false, handle the hash now
15305
15278
  if (!processHashOnRedirect) {
15306
- return await this.handleResponse(serverParams, serverTelemetryManager);
15279
+ return await this.handleResponse(serverParams, request, pkceVerifier, serverTelemetryManager);
15307
15280
  }
15308
15281
  }
15309
15282
  return null;
@@ -15313,7 +15286,6 @@
15313
15286
  e.setCorrelationId(this.correlationId);
15314
15287
  serverTelemetryManager.cacheFailedRequest(e);
15315
15288
  }
15316
- this.browserStorage.cleanRequestByInteractionType(exports.InteractionType.Redirect);
15317
15289
  throw e;
15318
15290
  }
15319
15291
  }
@@ -15366,39 +15338,67 @@
15366
15338
  * @param hash
15367
15339
  * @param state
15368
15340
  */
15369
- async handleResponse(serverParams, serverTelemetryManager) {
15341
+ async handleResponse(serverParams, request, codeVerifier, serverTelemetryManager) {
15370
15342
  const state = serverParams.state;
15371
15343
  if (!state) {
15372
15344
  throw createBrowserAuthError(noStateInHash);
15373
15345
  }
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);
15346
+ if (serverParams.ear_jwe) {
15347
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, request.correlationId)({
15348
+ requestAuthority: request.authority,
15349
+ requestAzureCloudOptions: request.azureCloudOptions,
15350
+ requestExtraQueryParameters: request.extraQueryParameters,
15351
+ account: request.account,
15391
15352
  });
15353
+ 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);
15354
+ }
15355
+ const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({ serverTelemetryManager, requestAuthority: request.authority });
15356
+ 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);
15357
+ }
15358
+ /**
15359
+ * Redirects window to given URL.
15360
+ * @param urlNavigate
15361
+ * @param onRedirectNavigateRequest - onRedirectNavigate callback provided on the request
15362
+ */
15363
+ async initiateAuthRequest(requestUrl, onRedirectNavigateRequest) {
15364
+ this.logger.verbose("RedirectHandler.initiateAuthRequest called");
15365
+ // Navigate if valid URL
15366
+ if (requestUrl) {
15367
+ this.logger.infoPii(`RedirectHandler.initiateAuthRequest: Navigate to: ${requestUrl}`);
15368
+ const navigationOptions = {
15369
+ apiId: ApiId.acquireTokenRedirect,
15370
+ timeout: this.config.system.redirectNavigationTimeout,
15371
+ noHistory: false,
15372
+ };
15373
+ const onRedirectNavigate = onRedirectNavigateRequest ||
15374
+ this.config.auth.onRedirectNavigate;
15375
+ // If onRedirectNavigate is implemented, invoke it and provide requestUrl
15376
+ if (typeof onRedirectNavigate === "function") {
15377
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Invoking onRedirectNavigate callback");
15378
+ const navigate = onRedirectNavigate(requestUrl);
15379
+ // Returning false from onRedirectNavigate will stop navigation
15380
+ if (navigate !== false) {
15381
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate did not return false, navigating");
15382
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15383
+ return;
15384
+ }
15385
+ else {
15386
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: onRedirectNavigate returned false, stopping navigation");
15387
+ return;
15388
+ }
15389
+ }
15390
+ else {
15391
+ // Navigate window to request URL
15392
+ this.logger.verbose("RedirectHandler.initiateAuthRequest: Navigating window to navigate url");
15393
+ await this.navigationClient.navigateExternal(requestUrl, navigationOptions);
15394
+ return;
15395
+ }
15392
15396
  }
15393
- // Hash contains known properties - handle and return in callback
15394
- const currentAuthority = this.browserStorage.getCachedAuthority(state);
15395
- if (!currentAuthority) {
15396
- throw createBrowserAuthError(noCachedAuthorityError);
15397
+ else {
15398
+ // Throw error if request URL is empty.
15399
+ this.logger.info("RedirectHandler.initiateAuthRequest: Navigate url is empty");
15400
+ throw createBrowserAuthError(emptyNavigateUri);
15397
15401
  }
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
15402
  }
15403
15403
  /**
15404
15404
  * Use to log out the current user, and redirect the user to the postLogoutRedirectUri.
@@ -15497,7 +15497,7 @@
15497
15497
  * @param urlNavigate
15498
15498
  * @param userRequestScopes
15499
15499
  */
15500
- async function initiateAuthRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15500
+ async function initiateCodeRequest(requestUrl, performanceClient, logger, correlationId, navigateFrameWait) {
15501
15501
  performanceClient.addQueueMeasurement(PerformanceEvents.SilentHandlerInitiateAuthRequest, correlationId);
15502
15502
  if (!requestUrl) {
15503
15503
  // Throw error if request URL is empty.
@@ -15509,6 +15509,15 @@
15509
15509
  }
15510
15510
  return invoke(loadFrameSync, PerformanceEvents.SilentHandlerLoadFrameSync, logger, performanceClient, correlationId)(requestUrl);
15511
15511
  }
15512
+ async function initiateEarRequest(config, authority, request, logger, performanceClient) {
15513
+ const frame = createHiddenIframe();
15514
+ if (!frame.contentDocument) {
15515
+ throw "No document associated with iframe!";
15516
+ }
15517
+ const form = await getEARForm(frame.contentDocument, config, authority, request, logger, performanceClient);
15518
+ form.submit();
15519
+ return frame;
15520
+ }
15512
15521
  /**
15513
15522
  * Monitors an iframe content window until it loads a url with a known hash, or hits a specified timeout.
15514
15523
  * @param iframe
@@ -15659,19 +15668,34 @@
15659
15668
  }
15660
15669
  // Create silent request
15661
15670
  const silentRequest = await invokeAsync(this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, request.correlationId)(inputRequest, exports.InteractionType.Silent);
15671
+ silentRequest.platformBroker =
15672
+ NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeMessageHandler, silentRequest.authenticationScheme);
15662
15673
  preconnect(silentRequest.authority);
15663
- const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15674
+ if (this.config.auth.protocolMode === ProtocolMode.EAR) {
15675
+ return this.executeEarFlow(silentRequest);
15676
+ }
15677
+ else {
15678
+ return this.executeCodeFlow(silentRequest);
15679
+ }
15680
+ }
15681
+ /**
15682
+ * Executes auth code + PKCE flow
15683
+ * @param request
15684
+ * @returns
15685
+ */
15686
+ async executeCodeFlow(request) {
15664
15687
  let authClient;
15688
+ const serverTelemetryManager = this.initializeServerTelemetryManager(this.apiId);
15665
15689
  try {
15666
15690
  // Initialize the client
15667
15691
  authClient = await invokeAsync(this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, request.correlationId)({
15668
15692
  serverTelemetryManager,
15669
- requestAuthority: silentRequest.authority,
15670
- requestAzureCloudOptions: silentRequest.azureCloudOptions,
15671
- requestExtraQueryParameters: silentRequest.extraQueryParameters,
15672
- account: silentRequest.account,
15693
+ requestAuthority: request.authority,
15694
+ requestAzureCloudOptions: request.azureCloudOptions,
15695
+ requestExtraQueryParameters: request.extraQueryParameters,
15696
+ account: request.account,
15673
15697
  });
15674
- return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, silentRequest);
15698
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, request);
15675
15699
  }
15676
15700
  catch (e) {
15677
15701
  if (e instanceof AuthError) {
@@ -15686,10 +15710,33 @@
15686
15710
  this.performanceClient.addFields({
15687
15711
  retryError: e.errorCode,
15688
15712
  }, 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);
15713
+ return await invokeAsync(this.silentTokenHelper.bind(this), PerformanceEvents.SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, request);
15691
15714
  }
15692
15715
  }
15716
+ /**
15717
+ * Executes EAR flow
15718
+ * @param request
15719
+ */
15720
+ async executeEarFlow(request) {
15721
+ const correlationId = request.correlationId;
15722
+ const discoveredAuthority = await invokeAsync(this.getDiscoveredAuthority.bind(this), PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)({
15723
+ requestAuthority: request.authority,
15724
+ requestAzureCloudOptions: request.azureCloudOptions,
15725
+ requestExtraQueryParameters: request.extraQueryParameters,
15726
+ account: request.account,
15727
+ });
15728
+ const earJwk = await invokeAsync(generateEarKey, PerformanceEvents.GenerateEarKey, this.logger, this.performanceClient, correlationId)();
15729
+ const silentRequest = {
15730
+ ...request,
15731
+ earJwk: earJwk,
15732
+ };
15733
+ const msalFrame = await invokeAsync(initiateEarRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(this.config, discoveredAuthority, silentRequest, this.logger, this.performanceClient);
15734
+ const responseType = this.config.auth.OIDCOptions.serverResponseType;
15735
+ // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
15736
+ 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);
15737
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15738
+ 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);
15739
+ }
15693
15740
  /**
15694
15741
  * Currently Unsupported
15695
15742
  */
@@ -15706,44 +15753,20 @@
15706
15753
  async silentTokenHelper(authClient, request) {
15707
15754
  const correlationId = request.correlationId;
15708
15755
  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);
15756
+ const pkceCodes = await invokeAsync(generatePkceCodes, PerformanceEvents.GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
15710
15757
  const silentRequest = {
15711
15758
  ...request,
15712
15759
  codeChallenge: pkceCodes.challenge,
15713
15760
  };
15714
15761
  // 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);
15762
+ const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, silentRequest, this.logger, this.performanceClient);
15719
15763
  // 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);
15764
+ const msalFrame = await invokeAsync(initiateCodeRequest, PerformanceEvents.SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId, this.config.system.navigateFrameWait);
15721
15765
  const responseType = this.config.auth.OIDCOptions.serverResponseType;
15722
15766
  // 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
15767
  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);
15768
+ const serverParams = invoke(deserializeResponse, PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger);
15769
+ 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
15770
  }
15748
15771
  }
15749
15772
 
@@ -16272,31 +16295,38 @@
16272
16295
  * @returns
16273
16296
  */
16274
16297
  async handleRedirectPromiseInternal(hash) {
16298
+ if (!this.browserStorage.isInteractionInProgress(true)) {
16299
+ this.logger.info("handleRedirectPromise called but there is no interaction in progress, returning null.");
16300
+ return null;
16301
+ }
16275
16302
  const loggedInAccounts = this.getAllAccounts();
16276
- const request = this.browserStorage.getCachedNativeRequest();
16277
- const useNative = request &&
16303
+ const platformBrokerRequest = this.browserStorage.getCachedNativeRequest();
16304
+ const useNative = platformBrokerRequest &&
16278
16305
  NativeMessageHandler.isPlatformBrokerAvailable(this.config, this.logger, this.nativeExtensionProvider) &&
16279
16306
  this.nativeExtensionProvider &&
16280
16307
  !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);
16308
+ let rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, platformBrokerRequest?.correlationId || "");
16285
16309
  this.eventHandler.emitEvent(EventType.HANDLE_REDIRECT_START, exports.InteractionType.Redirect);
16286
16310
  let redirectResponse;
16287
16311
  if (useNative && this.nativeExtensionProvider) {
16288
16312
  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);
16313
+ 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
16314
  redirectResponse = invokeAsync(nativeClient.handleRedirectPromise.bind(nativeClient), PerformanceEvents.HandleNativeRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(this.performanceClient, rootMeasurement.event.correlationId);
16291
16315
  }
16292
16316
  else {
16317
+ const [standardRequest, codeVerifier] = this.browserStorage.getCachedRequest();
16318
+ const correlationId = standardRequest.correlationId;
16319
+ // Reset rootMeasurement now that we have correlationId
16320
+ rootMeasurement.discard();
16321
+ rootMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.AcquireTokenRedirect, correlationId);
16293
16322
  this.logger.trace("handleRedirectPromise - acquiring token from web flow");
16294
16323
  const redirectClient = this.createRedirectClient(correlationId);
16295
- redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, rootMeasurement);
16324
+ redirectResponse = invokeAsync(redirectClient.handleRedirectPromise.bind(redirectClient), PerformanceEvents.HandleRedirectPromiseMeasurement, this.logger, this.performanceClient, rootMeasurement.event.correlationId)(hash, standardRequest, codeVerifier, rootMeasurement);
16296
16325
  }
16297
16326
  return redirectResponse
16298
16327
  .then((result) => {
16299
16328
  if (result) {
16329
+ this.browserStorage.resetRequestCache();
16300
16330
  // Emit login event if number of accounts change
16301
16331
  const isLoggingIn = loggedInAccounts.length < this.getAllAccounts().length;
16302
16332
  if (isLoggingIn) {
@@ -16328,6 +16358,7 @@
16328
16358
  return result;
16329
16359
  })
16330
16360
  .catch((e) => {
16361
+ this.browserStorage.resetRequestCache();
16331
16362
  const eventError = e;
16332
16363
  // Emit login event if there is an account
16333
16364
  if (loggedInAccounts.length > 0) {
@@ -16421,7 +16452,6 @@
16421
16452
  const redirectClient = this.createRedirectClient(correlationId);
16422
16453
  return redirectClient.acquireToken(request);
16423
16454
  }
16424
- this.browserStorage.setInteractionInProgress(false);
16425
16455
  throw e;
16426
16456
  });
16427
16457
  }
@@ -16432,6 +16462,7 @@
16432
16462
  return await result;
16433
16463
  }
16434
16464
  catch (e) {
16465
+ this.browserStorage.resetRequestCache();
16435
16466
  atrMeasurement.end({ success: false }, e);
16436
16467
  if (isLoggedIn) {
16437
16468
  this.eventHandler.emitEvent(EventType.ACQUIRE_TOKEN_FAILURE, exports.InteractionType.Redirect, null, e);
@@ -16483,7 +16514,6 @@
16483
16514
  correlationId,
16484
16515
  }, ApiId.acquireTokenPopup)
16485
16516
  .then((response) => {
16486
- this.browserStorage.setInteractionInProgress(false);
16487
16517
  atPopupMeasurement.end({
16488
16518
  success: true,
16489
16519
  isNativeBroker: true,
@@ -16503,7 +16533,6 @@
16503
16533
  const popupClient = this.createPopupClient(correlationId);
16504
16534
  return popupClient.acquireToken(request, pkce);
16505
16535
  }
16506
- this.browserStorage.setInteractionInProgress(false);
16507
16536
  throw e;
16508
16537
  });
16509
16538
  }
@@ -16544,8 +16573,12 @@
16544
16573
  // Since this function is syncronous we need to reject
16545
16574
  return Promise.reject(e);
16546
16575
  })
16547
- .finally(() => this.config.system.asyncPopups &&
16548
- this.preGeneratePkceCodes(correlationId));
16576
+ .finally(async () => {
16577
+ this.browserStorage.setInteractionInProgress(false);
16578
+ if (this.config.system.asyncPopups) {
16579
+ await this.preGeneratePkceCodes(correlationId);
16580
+ }
16581
+ });
16549
16582
  }
16550
16583
  trackPageVisibilityWithMeasurement() {
16551
16584
  const measurement = this.ssoSilentMeasurement ||
@@ -16848,7 +16881,9 @@
16848
16881
  preflightCheck$1(this.initialized);
16849
16882
  this.browserStorage.setInteractionInProgress(true);
16850
16883
  const popupClient = this.createPopupClient(correlationId);
16851
- return popupClient.logout(logoutRequest);
16884
+ return popupClient.logout(logoutRequest).finally(() => {
16885
+ this.browserStorage.setInteractionInProgress(false);
16886
+ });
16852
16887
  }
16853
16888
  catch (e) {
16854
16889
  // Since this function is syncronous we need to reject
@@ -17264,9 +17299,7 @@
17264
17299
  async acquireTokenSilentDeduped(request, account, correlationId) {
17265
17300
  const thumbprint = getRequestThumbprint(this.config.auth.clientId, {
17266
17301
  ...request,
17267
- authority: request.authority || this.config.auth.authority,
17268
- correlationId: correlationId,
17269
- }, account.homeAccountId);
17302
+ authority: request.authority || this.config.auth.authority}, account.homeAccountId);
17270
17303
  const silentRequestKey = JSON.stringify(thumbprint);
17271
17304
  const inProgressRequest = this.activeSilentTokenRequests.get(silentRequestKey);
17272
17305
  if (typeof inProgressRequest === "undefined") {