@azure/msal-browser 3.5.1-beta.1 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/dist/app/IPublicClientApplication.d.ts +51 -51
  2. package/dist/app/IPublicClientApplication.mjs +102 -102
  3. package/dist/app/IPublicClientApplication.mjs.map +1 -1
  4. package/dist/app/PublicClientApplication.d.ts +269 -268
  5. package/dist/app/PublicClientApplication.d.ts.map +1 -1
  6. package/dist/app/PublicClientApplication.mjs +333 -333
  7. package/dist/app/PublicClientApplication.mjs.map +1 -1
  8. package/dist/app/PublicClientNext.d.ts +273 -273
  9. package/dist/app/PublicClientNext.mjs +351 -351
  10. package/dist/app/PublicClientNext.mjs.map +1 -1
  11. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts +62 -62
  12. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +254 -254
  13. package/dist/broker/nativeBroker/NativeMessageHandler.mjs.map +1 -1
  14. package/dist/broker/nativeBroker/NativeRequest.d.ts +44 -44
  15. package/dist/broker/nativeBroker/NativeResponse.d.ts +48 -48
  16. package/dist/broker/nativeBroker/NativeStatusCodes.d.ts +7 -7
  17. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +11 -11
  18. package/dist/broker/nativeBroker/NativeStatusCodes.mjs.map +1 -1
  19. package/dist/cache/AsyncMemoryStorage.d.ts +51 -51
  20. package/dist/cache/AsyncMemoryStorage.mjs +132 -132
  21. package/dist/cache/AsyncMemoryStorage.mjs.map +1 -1
  22. package/dist/cache/BrowserCacheManager.d.ts +395 -383
  23. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  24. package/dist/cache/BrowserCacheManager.mjs +1283 -1263
  25. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  26. package/dist/cache/BrowserStorage.d.ts +11 -11
  27. package/dist/cache/BrowserStorage.mjs +32 -32
  28. package/dist/cache/BrowserStorage.mjs.map +1 -1
  29. package/dist/cache/CryptoKeyStore.d.ts +18 -18
  30. package/dist/cache/CryptoKeyStore.mjs +40 -40
  31. package/dist/cache/CryptoKeyStore.mjs.map +1 -1
  32. package/dist/cache/DatabaseStorage.d.ts +56 -56
  33. package/dist/cache/DatabaseStorage.mjs +190 -190
  34. package/dist/cache/DatabaseStorage.mjs.map +1 -1
  35. package/dist/cache/IAsyncMemoryStorage.d.ts +27 -27
  36. package/dist/cache/ITokenCache.d.ts +11 -11
  37. package/dist/cache/IWindowStorage.d.ts +27 -27
  38. package/dist/cache/MemoryStorage.d.ts +11 -11
  39. package/dist/cache/MemoryStorage.mjs +31 -31
  40. package/dist/cache/MemoryStorage.mjs.map +1 -1
  41. package/dist/cache/TokenCache.d.ts +77 -77
  42. package/dist/cache/TokenCache.d.ts.map +1 -1
  43. package/dist/cache/TokenCache.mjs +223 -225
  44. package/dist/cache/TokenCache.mjs.map +1 -1
  45. package/dist/config/Configuration.d.ts +206 -206
  46. package/dist/config/Configuration.d.ts.map +1 -1
  47. package/dist/config/Configuration.mjs +128 -128
  48. package/dist/config/Configuration.mjs.map +1 -1
  49. package/dist/controllers/ControllerFactory.d.ts +4 -4
  50. package/dist/controllers/ControllerFactory.d.ts.map +1 -1
  51. package/dist/controllers/ControllerFactory.mjs +29 -29
  52. package/dist/controllers/ControllerFactory.mjs.map +1 -1
  53. package/dist/controllers/IController.d.ts +61 -61
  54. package/dist/controllers/NestedAppAuthController.d.ts +95 -95
  55. package/dist/controllers/NestedAppAuthController.mjs +334 -334
  56. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  57. package/dist/controllers/StandardController.d.ts +407 -407
  58. package/dist/controllers/StandardController.d.ts.map +1 -1
  59. package/dist/controllers/StandardController.mjs +1307 -1294
  60. package/dist/controllers/StandardController.mjs.map +1 -1
  61. package/dist/controllers/UnknownOperatingContextController.d.ts +87 -87
  62. package/dist/controllers/UnknownOperatingContextController.mjs +264 -264
  63. package/dist/controllers/UnknownOperatingContextController.mjs.map +1 -1
  64. package/dist/crypto/BrowserCrypto.d.ts +44 -44
  65. package/dist/crypto/BrowserCrypto.mjs +93 -93
  66. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  67. package/dist/crypto/CryptoOps.d.ts +63 -63
  68. package/dist/crypto/CryptoOps.mjs +145 -145
  69. package/dist/crypto/CryptoOps.mjs.map +1 -1
  70. package/dist/crypto/PkceGenerator.d.ts +8 -8
  71. package/dist/crypto/PkceGenerator.mjs +54 -54
  72. package/dist/crypto/PkceGenerator.mjs.map +1 -1
  73. package/dist/crypto/SignedHttpRequest.d.ts +30 -30
  74. package/dist/crypto/SignedHttpRequest.mjs +39 -39
  75. package/dist/crypto/SignedHttpRequest.mjs.map +1 -1
  76. package/dist/encode/Base64Decode.d.ts +9 -9
  77. package/dist/encode/Base64Decode.mjs +36 -36
  78. package/dist/encode/Base64Decode.mjs.map +1 -1
  79. package/dist/encode/Base64Encode.d.ts +19 -19
  80. package/dist/encode/Base64Encode.mjs +43 -43
  81. package/dist/encode/Base64Encode.mjs.map +1 -1
  82. package/dist/error/BrowserAuthError.d.ts +250 -250
  83. package/dist/error/BrowserAuthError.mjs +258 -258
  84. package/dist/error/BrowserAuthError.mjs.map +1 -1
  85. package/dist/error/BrowserAuthErrorCodes.d.ts +45 -45
  86. package/dist/error/BrowserAuthErrorCodes.mjs +49 -49
  87. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -1
  88. package/dist/error/BrowserConfigurationAuthError.d.ts +33 -33
  89. package/dist/error/BrowserConfigurationAuthError.mjs +40 -40
  90. package/dist/error/BrowserConfigurationAuthError.mjs.map +1 -1
  91. package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts +3 -3
  92. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +7 -7
  93. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs.map +1 -1
  94. package/dist/error/NativeAuthError.d.ts +29 -29
  95. package/dist/error/NativeAuthError.mjs +60 -60
  96. package/dist/error/NativeAuthError.mjs.map +1 -1
  97. package/dist/error/NativeAuthErrorCodes.d.ts +2 -2
  98. package/dist/error/NativeAuthErrorCodes.mjs +6 -6
  99. package/dist/error/NativeAuthErrorCodes.mjs.map +1 -1
  100. package/dist/error/NestedAppAuthError.d.ts +14 -14
  101. package/dist/error/NestedAppAuthError.mjs +23 -23
  102. package/dist/error/NestedAppAuthError.mjs.map +1 -1
  103. package/dist/event/EventHandler.d.ts +41 -41
  104. package/dist/event/EventHandler.mjs +119 -119
  105. package/dist/event/EventHandler.mjs.map +1 -1
  106. package/dist/event/EventMessage.d.ts +25 -25
  107. package/dist/event/EventMessage.mjs +68 -68
  108. package/dist/event/EventMessage.mjs.map +1 -1
  109. package/dist/event/EventType.d.ts +28 -28
  110. package/dist/event/EventType.mjs +31 -31
  111. package/dist/event/EventType.mjs.map +1 -1
  112. package/dist/index.d.ts +39 -39
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.mjs +1 -1
  115. package/dist/interaction_client/BaseInteractionClient.d.ts +54 -54
  116. package/dist/interaction_client/BaseInteractionClient.mjs +152 -152
  117. package/dist/interaction_client/BaseInteractionClient.mjs.map +1 -1
  118. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.d.ts +4 -4
  119. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +10 -10
  120. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs.map +1 -1
  121. package/dist/interaction_client/NativeInteractionClient.d.ts +140 -140
  122. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  123. package/dist/interaction_client/NativeInteractionClient.mjs +541 -549
  124. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  125. package/dist/interaction_client/PopupClient.d.ts +105 -105
  126. package/dist/interaction_client/PopupClient.mjs +432 -432
  127. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  128. package/dist/interaction_client/RedirectClient.d.ts +49 -49
  129. package/dist/interaction_client/RedirectClient.mjs +311 -311
  130. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  131. package/dist/interaction_client/SilentAuthCodeClient.d.ts +23 -23
  132. package/dist/interaction_client/SilentAuthCodeClient.mjs +57 -57
  133. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  134. package/dist/interaction_client/SilentCacheClient.d.ts +24 -24
  135. package/dist/interaction_client/SilentCacheClient.mjs +61 -61
  136. package/dist/interaction_client/SilentCacheClient.mjs.map +1 -1
  137. package/dist/interaction_client/SilentIframeClient.d.ts +32 -32
  138. package/dist/interaction_client/SilentIframeClient.mjs +96 -96
  139. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  140. package/dist/interaction_client/SilentRefreshClient.d.ts +20 -20
  141. package/dist/interaction_client/SilentRefreshClient.d.ts.map +1 -1
  142. package/dist/interaction_client/SilentRefreshClient.mjs +47 -43
  143. package/dist/interaction_client/SilentRefreshClient.mjs.map +1 -1
  144. package/dist/interaction_client/StandardInteractionClient.d.ts +54 -54
  145. package/dist/interaction_client/StandardInteractionClient.mjs +222 -222
  146. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  147. package/dist/interaction_handler/InteractionHandler.d.ts +33 -33
  148. package/dist/interaction_handler/InteractionHandler.mjs +93 -93
  149. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  150. package/dist/interaction_handler/RedirectHandler.d.ts +32 -32
  151. package/dist/interaction_handler/RedirectHandler.mjs +134 -134
  152. package/dist/interaction_handler/RedirectHandler.mjs.map +1 -1
  153. package/dist/interaction_handler/SilentHandler.d.ts +13 -13
  154. package/dist/interaction_handler/SilentHandler.mjs +133 -133
  155. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  156. package/dist/naa/AccountInfo.d.ts +11 -11
  157. package/dist/naa/AuthBridge.d.ts +8 -5
  158. package/dist/naa/AuthBridge.d.ts.map +1 -1
  159. package/dist/naa/AuthResult.d.ts +7 -0
  160. package/dist/naa/AuthResult.d.ts.map +1 -0
  161. package/dist/naa/BridgeCapabilities.d.ts +3 -3
  162. package/dist/naa/BridgeError.d.ts +9 -9
  163. package/dist/naa/BridgeError.mjs +7 -7
  164. package/dist/naa/BridgeError.mjs.map +1 -1
  165. package/dist/naa/BridgeProxy.d.ts +68 -72
  166. package/dist/naa/BridgeProxy.d.ts.map +1 -1
  167. package/dist/naa/BridgeProxy.mjs +155 -152
  168. package/dist/naa/BridgeProxy.mjs.map +1 -1
  169. package/dist/naa/BridgeRequest.d.ts +7 -6
  170. package/dist/naa/BridgeRequest.d.ts.map +1 -1
  171. package/dist/naa/BridgeRequestEnvelope.d.ts +12 -13
  172. package/dist/naa/BridgeRequestEnvelope.d.ts.map +1 -1
  173. package/dist/naa/BridgeResponseEnvelope.d.ts +13 -10
  174. package/dist/naa/BridgeResponseEnvelope.d.ts.map +1 -1
  175. package/dist/naa/BridgeStatusCode.d.ts +11 -10
  176. package/dist/naa/BridgeStatusCode.d.ts.map +1 -1
  177. package/dist/naa/BridgeStatusCode.mjs +15 -16
  178. package/dist/naa/BridgeStatusCode.mjs.map +1 -1
  179. package/dist/naa/IBridgeProxy.d.ts +10 -12
  180. package/dist/naa/IBridgeProxy.d.ts.map +1 -1
  181. package/dist/naa/{InitializeBridgeResponse.d.ts → InitContext.d.ts} +7 -7
  182. package/dist/naa/InitContext.d.ts.map +1 -0
  183. package/dist/naa/TokenRequest.d.ts +18 -19
  184. package/dist/naa/TokenRequest.d.ts.map +1 -1
  185. package/dist/naa/TokenResponse.d.ts +13 -15
  186. package/dist/naa/TokenResponse.d.ts.map +1 -1
  187. package/dist/naa/mapping/NestedAppAuthAdapter.d.ts +23 -23
  188. package/dist/naa/mapping/NestedAppAuthAdapter.d.ts.map +1 -1
  189. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +141 -141
  190. package/dist/naa/mapping/NestedAppAuthAdapter.mjs.map +1 -1
  191. package/dist/navigation/INavigationClient.d.ts +16 -16
  192. package/dist/navigation/NavigationClient.d.ts +22 -22
  193. package/dist/navigation/NavigationClient.mjs +40 -40
  194. package/dist/navigation/NavigationClient.mjs.map +1 -1
  195. package/dist/navigation/NavigationOptions.d.ts +12 -12
  196. package/dist/network/FetchClient.d.ts +26 -26
  197. package/dist/network/FetchClient.mjs +99 -99
  198. package/dist/network/FetchClient.mjs.map +1 -1
  199. package/dist/operatingcontext/BaseOperatingContext.d.ts +41 -40
  200. package/dist/operatingcontext/BaseOperatingContext.d.ts.map +1 -1
  201. package/dist/operatingcontext/BaseOperatingContext.mjs +96 -45
  202. package/dist/operatingcontext/BaseOperatingContext.mjs.map +1 -1
  203. package/dist/operatingcontext/StandardOperatingContext.d.ts +25 -25
  204. package/dist/operatingcontext/StandardOperatingContext.mjs +43 -43
  205. package/dist/operatingcontext/StandardOperatingContext.mjs.map +1 -1
  206. package/dist/operatingcontext/TeamsAppOperatingContext.d.ts +32 -32
  207. package/dist/operatingcontext/TeamsAppOperatingContext.mjs +85 -85
  208. package/dist/operatingcontext/TeamsAppOperatingContext.mjs.map +1 -1
  209. package/dist/operatingcontext/UnknownOperatingContext.d.ts +25 -25
  210. package/dist/operatingcontext/UnknownOperatingContext.mjs +42 -42
  211. package/dist/operatingcontext/UnknownOperatingContext.mjs.map +1 -1
  212. package/dist/packageMetadata.d.ts +2 -2
  213. package/dist/packageMetadata.d.ts.map +1 -1
  214. package/dist/packageMetadata.mjs +4 -4
  215. package/dist/packageMetadata.mjs.map +1 -1
  216. package/dist/request/AuthorizationCodeRequest.d.ts +8 -8
  217. package/dist/request/AuthorizationUrlRequest.d.ts +8 -8
  218. package/dist/request/ClearCacheRequest.d.ts +10 -10
  219. package/dist/request/EndSessionPopupRequest.d.ts +18 -18
  220. package/dist/request/EndSessionRequest.d.ts +15 -15
  221. package/dist/request/PopupRequest.d.ts +34 -34
  222. package/dist/request/PopupWindowAttributes.d.ts +15 -15
  223. package/dist/request/RedirectRequest.d.ts +35 -35
  224. package/dist/request/SilentRequest.d.ts +32 -32
  225. package/dist/request/SsoSilentRequest.d.ts +29 -29
  226. package/dist/response/AuthenticationResult.d.ts +4 -4
  227. package/dist/response/ResponseHandler.d.ts +7 -7
  228. package/dist/response/ResponseHandler.mjs +36 -36
  229. package/dist/response/ResponseHandler.mjs.map +1 -1
  230. package/dist/telemetry/BrowserPerformanceClient.d.ts +34 -36
  231. package/dist/telemetry/BrowserPerformanceClient.d.ts.map +1 -1
  232. package/dist/telemetry/BrowserPerformanceClient.mjs +163 -126
  233. package/dist/telemetry/BrowserPerformanceClient.mjs.map +1 -1
  234. package/dist/telemetry/BrowserPerformanceMeasurement.d.ts +21 -21
  235. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +92 -92
  236. package/dist/telemetry/BrowserPerformanceMeasurement.mjs.map +1 -1
  237. package/dist/utils/BrowserConstants.d.ts +180 -176
  238. package/dist/utils/BrowserConstants.d.ts.map +1 -1
  239. package/dist/utils/BrowserConstants.mjs +213 -205
  240. package/dist/utils/BrowserConstants.mjs.map +1 -1
  241. package/dist/utils/BrowserProtocolUtils.d.ts +11 -11
  242. package/dist/utils/BrowserProtocolUtils.mjs +21 -21
  243. package/dist/utils/BrowserProtocolUtils.mjs.map +1 -1
  244. package/dist/utils/BrowserUtils.d.ts +56 -56
  245. package/dist/utils/BrowserUtils.mjs +125 -125
  246. package/dist/utils/BrowserUtils.mjs.map +1 -1
  247. package/lib/msal-browser.cjs +18304 -17929
  248. package/lib/msal-browser.cjs.map +1 -1
  249. package/lib/msal-browser.js +18304 -17929
  250. package/lib/msal-browser.js.map +1 -1
  251. package/lib/msal-browser.min.js +66 -68
  252. package/package.json +103 -102
  253. package/src/app/PublicClientApplication.ts +2 -1
  254. package/src/cache/BrowserCacheManager.ts +38 -23
  255. package/src/cache/DatabaseStorage.ts +1 -1
  256. package/src/cache/TokenCache.ts +34 -29
  257. package/src/config/Configuration.ts +1 -1
  258. package/src/controllers/ControllerFactory.ts +3 -5
  259. package/src/controllers/StandardController.ts +53 -26
  260. package/src/crypto/CryptoOps.ts +1 -1
  261. package/src/crypto/SignedHttpRequest.ts +1 -1
  262. package/src/index.ts +1 -0
  263. package/src/interaction_client/BaseInteractionClient.ts +2 -2
  264. package/src/interaction_client/NativeInteractionClient.ts +24 -20
  265. package/src/interaction_client/PopupClient.ts +1 -1
  266. package/src/interaction_client/RedirectClient.ts +3 -3
  267. package/src/interaction_client/SilentAuthCodeClient.ts +1 -1
  268. package/src/interaction_client/SilentRefreshClient.ts +8 -0
  269. package/src/interaction_client/StandardInteractionClient.ts +1 -1
  270. package/src/interaction_handler/SilentHandler.ts +2 -2
  271. package/src/naa/AuthBridge.ts +3 -2
  272. package/src/naa/AuthResult.ts +12 -0
  273. package/src/naa/BridgeProxy.ts +64 -71
  274. package/src/naa/BridgeRequest.ts +6 -2
  275. package/src/naa/BridgeRequestEnvelope.ts +1 -13
  276. package/src/naa/BridgeResponseEnvelope.ts +5 -7
  277. package/src/naa/BridgeStatusCode.ts +12 -10
  278. package/src/naa/IBridgeProxy.ts +3 -14
  279. package/src/naa/{InitializeBridgeResponse.ts → InitContext.ts} +1 -1
  280. package/src/naa/TokenRequest.ts +1 -2
  281. package/src/naa/TokenResponse.ts +1 -4
  282. package/src/naa/mapping/NestedAppAuthAdapter.ts +19 -19
  283. package/src/operatingcontext/BaseOperatingContext.ts +64 -7
  284. package/src/packageMetadata.ts +1 -1
  285. package/src/telemetry/BrowserPerformanceClient.ts +95 -42
  286. package/src/utils/BrowserConstants.ts +11 -0
  287. package/dist/naa/AccountRequests.d.ts +0 -10
  288. package/dist/naa/AccountRequests.d.ts.map +0 -1
  289. package/dist/naa/InitializeBridgeResponse.d.ts.map +0 -1
  290. package/src/naa/AccountRequests.ts +0 -16
@@ -1,6 +1,6 @@
1
- /*! @azure/msal-browser v3.5.1-beta.1 2023-11-14 */
1
+ /*! @azure/msal-browser v3.7.0 2024-01-09 */
2
2
  'use strict';
3
- import { CacheManager, CredentialType, CacheHelpers, AccountEntity, createClientAuthError, ClientAuthErrorCodes, AppMetadataEntity, AuthorityMetadataEntity, Constants, PersistentCacheKeys, ThrottlingEntity, PerformanceEvents, StringUtils, ProtocolUtils, AuthToken, CacheRecord, DEFAULT_CRYPTO_IMPLEMENTATION, CcsCredentialType } from '@azure/msal-common';
3
+ import { CacheManager, CredentialType, CacheHelpers, AccountEntity, createClientAuthError, ClientAuthErrorCodes, Constants, PersistentCacheKeys, PerformanceEvents, StringUtils, ProtocolUtils, AuthToken, CacheRecord, DEFAULT_CRYPTO_IMPLEMENTATION, CcsCredentialType } from '@azure/msal-common';
4
4
  import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
5
5
  import { BrowserCacheLocation, StaticCacheKeys, InMemoryCacheKeys, TemporaryCacheKeys } from '../utils/BrowserConstants.mjs';
6
6
  import { BrowserStorage } from './BrowserStorage.mjs';
@@ -10,1267 +10,1287 @@ import { base64Decode } from '../encode/Base64Decode.mjs';
10
10
  import { base64Encode } from '../encode/Base64Encode.mjs';
11
11
  import { noTokenRequestCacheError, unableToParseTokenRequestCacheError, noCachedAuthorityError, interactionInProgress } from '../error/BrowserAuthErrorCodes.mjs';
12
12
 
13
- /*
14
- * Copyright (c) Microsoft Corporation. All rights reserved.
15
- * Licensed under the MIT License.
16
- */
17
- /**
18
- * This class implements the cache storage interface for MSAL through browser local or session storage.
19
- * Cookies are only used if storeAuthStateInCookie is true, and are only used for
20
- * parameters such as state and nonce, generally.
21
- */
22
- class BrowserCacheManager extends CacheManager {
23
- constructor(clientId, cacheConfig, cryptoImpl, logger, staticAuthorityOptions) {
24
- super(clientId, cryptoImpl, logger, staticAuthorityOptions);
25
- // Cookie life calculation (hours * minutes * seconds * ms)
26
- this.COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
27
- this.cacheConfig = cacheConfig;
28
- this.logger = logger;
29
- this.internalStorage = new MemoryStorage();
30
- this.browserStorage = this.setupBrowserStorage(this.cacheConfig.cacheLocation);
31
- this.temporaryCacheStorage = this.setupTemporaryCacheStorage(this.cacheConfig.temporaryCacheLocation, this.cacheConfig.cacheLocation);
32
- // Migrate cache entries from older versions of MSAL.
33
- if (cacheConfig.cacheMigrationEnabled) {
34
- this.migrateCacheEntries();
35
- this.createKeyMaps();
36
- }
37
- }
38
- /**
39
- * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured cacheLocation.
40
- * @param cacheLocation
41
- */
42
- setupBrowserStorage(cacheLocation) {
43
- switch (cacheLocation) {
44
- case BrowserCacheLocation.LocalStorage:
45
- case BrowserCacheLocation.SessionStorage:
46
- try {
47
- return new BrowserStorage(cacheLocation);
48
- }
49
- catch (e) {
50
- this.logger.verbose(e);
51
- break;
52
- }
53
- }
54
- this.cacheConfig.cacheLocation = BrowserCacheLocation.MemoryStorage;
55
- return new MemoryStorage();
56
- }
57
- /**
58
- * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured temporaryCacheLocation.
59
- * @param temporaryCacheLocation
60
- * @param cacheLocation
61
- */
62
- setupTemporaryCacheStorage(temporaryCacheLocation, cacheLocation) {
63
- switch (cacheLocation) {
64
- case BrowserCacheLocation.LocalStorage:
65
- case BrowserCacheLocation.SessionStorage:
66
- try {
67
- // Temporary cache items will always be stored in session storage to mitigate problems caused by multiple tabs
68
- return new BrowserStorage(temporaryCacheLocation ||
69
- BrowserCacheLocation.SessionStorage);
70
- }
71
- catch (e) {
72
- this.logger.verbose(e);
73
- return this.internalStorage;
74
- }
75
- case BrowserCacheLocation.MemoryStorage:
76
- default:
77
- return this.internalStorage;
78
- }
79
- }
80
- /**
81
- * Migrate all old cache entries to new schema. No rollback supported.
82
- * @param storeAuthStateInCookie
83
- */
84
- migrateCacheEntries() {
85
- const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
86
- const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
87
- const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
88
- const errorDescKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR_DESC}`;
89
- const idTokenValue = this.browserStorage.getItem(idTokenKey);
90
- const clientInfoValue = this.browserStorage.getItem(clientInfoKey);
91
- const errorValue = this.browserStorage.getItem(errorKey);
92
- const errorDescValue = this.browserStorage.getItem(errorDescKey);
93
- const values = [
94
- idTokenValue,
95
- clientInfoValue,
96
- errorValue,
97
- errorDescValue,
98
- ];
99
- const keysToMigrate = [
100
- PersistentCacheKeys.ID_TOKEN,
101
- PersistentCacheKeys.CLIENT_INFO,
102
- PersistentCacheKeys.ERROR,
103
- PersistentCacheKeys.ERROR_DESC,
104
- ];
105
- keysToMigrate.forEach((cacheKey, index) => this.migrateCacheEntry(cacheKey, values[index]));
106
- }
107
- /**
108
- * Utility function to help with migration.
109
- * @param newKey
110
- * @param value
111
- * @param storeAuthStateInCookie
112
- */
113
- migrateCacheEntry(newKey, value) {
114
- if (value) {
115
- this.setTemporaryCache(newKey, value, true);
116
- }
117
- }
118
- /**
119
- * Searches all cache entries for MSAL accounts and creates the account key map
120
- * This is used to migrate users from older versions of MSAL which did not create the map.
121
- * @returns
122
- */
123
- createKeyMaps() {
124
- this.logger.trace("BrowserCacheManager - createKeyMaps called.");
125
- const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
126
- const tokenKeys = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
127
- if (accountKeys && tokenKeys) {
128
- this.logger.verbose("BrowserCacheManager:createKeyMaps - account and token key maps already exist, skipping migration.");
129
- // Key maps already exist, no need to iterate through cache
130
- return;
131
- }
132
- const allKeys = this.browserStorage.getKeys();
133
- allKeys.forEach((key) => {
134
- if (this.isCredentialKey(key)) {
135
- // Get item, parse, validate and write key to map
136
- const value = this.getItem(key);
137
- if (value) {
138
- const credObj = this.validateAndParseJson(value);
139
- if (credObj && credObj.hasOwnProperty("credentialType")) {
140
- switch (credObj["credentialType"]) {
141
- case CredentialType.ID_TOKEN:
142
- if (CacheHelpers.isIdTokenEntity(credObj)) {
143
- this.logger.trace("BrowserCacheManager:createKeyMaps - idToken found, saving key to token key map");
144
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - idToken with key: ${key} found, saving key to token key map`);
145
- const idTokenEntity = credObj;
146
- const newKey = this.updateCredentialCacheKey(key, idTokenEntity);
147
- this.addTokenKey(newKey, CredentialType.ID_TOKEN);
148
- return;
149
- }
150
- else {
151
- this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching idToken schema with value containing idToken credentialType field but value failed IdTokenEntity validation, skipping.");
152
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed idToken validation on key: ${key}`);
153
- }
154
- break;
155
- case CredentialType.ACCESS_TOKEN:
156
- case CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME:
157
- if (CacheHelpers.isAccessTokenEntity(credObj)) {
158
- this.logger.trace("BrowserCacheManager:createKeyMaps - accessToken found, saving key to token key map");
159
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - accessToken with key: ${key} found, saving key to token key map`);
160
- const accessTokenEntity = credObj;
161
- const newKey = this.updateCredentialCacheKey(key, accessTokenEntity);
162
- this.addTokenKey(newKey, CredentialType.ACCESS_TOKEN);
163
- return;
164
- }
165
- else {
166
- this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching accessToken schema with value containing accessToken credentialType field but value failed AccessTokenEntity validation, skipping.");
167
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed accessToken validation on key: ${key}`);
168
- }
169
- break;
170
- case CredentialType.REFRESH_TOKEN:
171
- if (CacheHelpers.isRefreshTokenEntity(credObj)) {
172
- this.logger.trace("BrowserCacheManager:createKeyMaps - refreshToken found, saving key to token key map");
173
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - refreshToken with key: ${key} found, saving key to token key map`);
174
- const refreshTokenEntity = credObj;
175
- const newKey = this.updateCredentialCacheKey(key, refreshTokenEntity);
176
- this.addTokenKey(newKey, CredentialType.REFRESH_TOKEN);
177
- return;
178
- }
179
- else {
180
- this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching refreshToken schema with value containing refreshToken credentialType field but value failed RefreshTokenEntity validation, skipping.");
181
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed refreshToken validation on key: ${key}`);
182
- }
183
- break;
184
- // If credentialType isn't one of our predefined ones, it may not be an MSAL cache value. Ignore.
185
- }
186
- }
187
- }
188
- }
189
- if (this.isAccountKey(key)) {
190
- const value = this.getItem(key);
191
- if (value) {
192
- const accountObj = this.validateAndParseJson(value);
193
- if (accountObj &&
194
- AccountEntity.isAccountEntity(accountObj)) {
195
- this.logger.trace("BrowserCacheManager:createKeyMaps - account found, saving key to account key map");
196
- this.logger.tracePii(`BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`);
197
- this.addAccountKeyToMap(key);
198
- }
199
- }
200
- }
201
- });
202
- }
203
- /**
204
- * Parses passed value as JSON object, JSON.parse() will throw an error.
205
- * @param input
206
- */
207
- validateAndParseJson(jsonValue) {
208
- try {
209
- const parsedJson = JSON.parse(jsonValue);
210
- /**
211
- * There are edge cases in which JSON.parse will successfully parse a non-valid JSON object
212
- * (e.g. JSON.parse will parse an escaped string into an unescaped string), so adding a type check
213
- * of the parsed value is necessary in order to be certain that the string represents a valid JSON object.
214
- *
215
- */
216
- return parsedJson && typeof parsedJson === "object"
217
- ? parsedJson
218
- : null;
219
- }
220
- catch (error) {
221
- return null;
222
- }
223
- }
224
- /**
225
- * fetches the entry from the browser storage based off the key
226
- * @param key
227
- */
228
- getItem(key) {
229
- return this.browserStorage.getItem(key);
230
- }
231
- /**
232
- * sets the entry in the browser storage
233
- * @param key
234
- * @param value
235
- */
236
- setItem(key, value) {
237
- this.browserStorage.setItem(key, value);
238
- }
239
- /**
240
- * fetch the account entity from the platform cache
241
- * @param accountKey
242
- */
243
- getAccount(accountKey) {
244
- this.logger.trace("BrowserCacheManager.getAccount called");
245
- const account = this.getItem(accountKey);
246
- if (!account) {
247
- this.removeAccountKeyFromMap(accountKey);
248
- return null;
249
- }
250
- const parsedAccount = this.validateAndParseJson(account);
251
- if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
252
- this.removeAccountKeyFromMap(accountKey);
253
- return null;
254
- }
255
- return CacheManager.toObject(new AccountEntity(), parsedAccount);
256
- }
257
- /**
258
- * set account entity in the platform cache
259
- * @param account
260
- */
261
- setAccount(account) {
262
- this.logger.trace("BrowserCacheManager.setAccount called");
263
- const key = account.generateAccountKey();
264
- this.setItem(key, JSON.stringify(account));
265
- this.addAccountKeyToMap(key);
266
- }
267
- /**
268
- * Returns the array of account keys currently cached
269
- * @returns
270
- */
271
- getAccountKeys() {
272
- this.logger.trace("BrowserCacheManager.getAccountKeys called");
273
- const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
274
- if (accountKeys) {
275
- return JSON.parse(accountKeys);
276
- }
277
- this.logger.verbose("BrowserCacheManager.getAccountKeys - No account keys found");
278
- return [];
279
- }
280
- /**
281
- * Add a new account to the key map
282
- * @param key
283
- */
284
- addAccountKeyToMap(key) {
285
- this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
286
- this.logger.tracePii(`BrowserCacheManager.addAccountKeyToMap called with key: ${key}`);
287
- const accountKeys = this.getAccountKeys();
288
- if (accountKeys.indexOf(key) === -1) {
289
- // Only add key if it does not already exist in the map
290
- accountKeys.push(key);
291
- this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
292
- this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key added");
293
- }
294
- else {
295
- this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key already exists in map");
296
- }
297
- }
298
- /**
299
- * Remove an account from the key map
300
- * @param key
301
- */
302
- removeAccountKeyFromMap(key) {
303
- this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
304
- this.logger.tracePii(`BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`);
305
- const accountKeys = this.getAccountKeys();
306
- const removalIndex = accountKeys.indexOf(key);
307
- if (removalIndex > -1) {
308
- accountKeys.splice(removalIndex, 1);
309
- this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
310
- this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap account key removed");
311
- }
312
- else {
313
- this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap key not found in existing map");
314
- }
315
- }
316
- /**
317
- * Extends inherited removeAccount function to include removal of the account key from the map
318
- * @param key
319
- */
320
- async removeAccount(key) {
321
- void super.removeAccount(key);
322
- this.removeAccountKeyFromMap(key);
323
- }
324
- /**
325
- * Removes given idToken from the cache and from the key map
326
- * @param key
327
- */
328
- removeIdToken(key) {
329
- super.removeIdToken(key);
330
- this.removeTokenKey(key, CredentialType.ID_TOKEN);
331
- }
332
- /**
333
- * Removes given accessToken from the cache and from the key map
334
- * @param key
335
- */
336
- async removeAccessToken(key) {
337
- void super.removeAccessToken(key);
338
- this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
339
- }
340
- /**
341
- * Removes given refreshToken from the cache and from the key map
342
- * @param key
343
- */
344
- removeRefreshToken(key) {
345
- super.removeRefreshToken(key);
346
- this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
347
- }
348
- /**
349
- * Gets the keys for the cached tokens associated with this clientId
350
- * @returns
351
- */
352
- getTokenKeys() {
353
- this.logger.trace("BrowserCacheManager.getTokenKeys called");
354
- const item = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
355
- if (item) {
356
- const tokenKeys = this.validateAndParseJson(item);
357
- if (tokenKeys &&
358
- tokenKeys.hasOwnProperty("idToken") &&
359
- tokenKeys.hasOwnProperty("accessToken") &&
360
- tokenKeys.hasOwnProperty("refreshToken")) {
361
- return tokenKeys;
362
- }
363
- else {
364
- this.logger.error("BrowserCacheManager.getTokenKeys - Token keys found but in an unknown format. Returning empty key map.");
365
- }
366
- }
367
- else {
368
- this.logger.verbose("BrowserCacheManager.getTokenKeys - No token keys found");
369
- }
370
- return {
371
- idToken: [],
372
- accessToken: [],
373
- refreshToken: [],
374
- };
375
- }
376
- /**
377
- * Adds the given key to the token key map
378
- * @param key
379
- * @param type
380
- */
381
- addTokenKey(key, type) {
382
- this.logger.trace("BrowserCacheManager addTokenKey called");
383
- const tokenKeys = this.getTokenKeys();
384
- switch (type) {
385
- case CredentialType.ID_TOKEN:
386
- if (tokenKeys.idToken.indexOf(key) === -1) {
387
- this.logger.info("BrowserCacheManager: addTokenKey - idToken added to map");
388
- tokenKeys.idToken.push(key);
389
- }
390
- break;
391
- case CredentialType.ACCESS_TOKEN:
392
- if (tokenKeys.accessToken.indexOf(key) === -1) {
393
- this.logger.info("BrowserCacheManager: addTokenKey - accessToken added to map");
394
- tokenKeys.accessToken.push(key);
395
- }
396
- break;
397
- case CredentialType.REFRESH_TOKEN:
398
- if (tokenKeys.refreshToken.indexOf(key) === -1) {
399
- this.logger.info("BrowserCacheManager: addTokenKey - refreshToken added to map");
400
- tokenKeys.refreshToken.push(key);
401
- }
402
- break;
403
- default:
404
- this.logger.error(`BrowserCacheManager:addTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
405
- throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
406
- }
407
- this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
408
- }
409
- /**
410
- * Removes the given key from the token key map
411
- * @param key
412
- * @param type
413
- */
414
- removeTokenKey(key, type) {
415
- this.logger.trace("BrowserCacheManager removeTokenKey called");
416
- const tokenKeys = this.getTokenKeys();
417
- switch (type) {
418
- case CredentialType.ID_TOKEN:
419
- this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove idToken with key: ${key} from map`);
420
- const idRemoval = tokenKeys.idToken.indexOf(key);
421
- if (idRemoval > -1) {
422
- this.logger.info("BrowserCacheManager: removeTokenKey - idToken removed from map");
423
- tokenKeys.idToken.splice(idRemoval, 1);
424
- }
425
- else {
426
- this.logger.info("BrowserCacheManager: removeTokenKey - idToken does not exist in map. Either it was previously removed or it was never added.");
427
- }
428
- break;
429
- case CredentialType.ACCESS_TOKEN:
430
- this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove accessToken with key: ${key} from map`);
431
- const accessRemoval = tokenKeys.accessToken.indexOf(key);
432
- if (accessRemoval > -1) {
433
- this.logger.info("BrowserCacheManager: removeTokenKey - accessToken removed from map");
434
- tokenKeys.accessToken.splice(accessRemoval, 1);
435
- }
436
- else {
437
- this.logger.info("BrowserCacheManager: removeTokenKey - accessToken does not exist in map. Either it was previously removed or it was never added.");
438
- }
439
- break;
440
- case CredentialType.REFRESH_TOKEN:
441
- this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove refreshToken with key: ${key} from map`);
442
- const refreshRemoval = tokenKeys.refreshToken.indexOf(key);
443
- if (refreshRemoval > -1) {
444
- this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken removed from map");
445
- tokenKeys.refreshToken.splice(refreshRemoval, 1);
446
- }
447
- else {
448
- this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken does not exist in map. Either it was previously removed or it was never added.");
449
- }
450
- break;
451
- default:
452
- this.logger.error(`BrowserCacheManager:removeTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
453
- throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
454
- }
455
- this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
456
- }
457
- /**
458
- * generates idToken entity from a string
459
- * @param idTokenKey
460
- */
461
- getIdTokenCredential(idTokenKey) {
462
- const value = this.getItem(idTokenKey);
463
- if (!value) {
464
- this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
465
- this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
466
- return null;
467
- }
468
- const parsedIdToken = this.validateAndParseJson(value);
469
- if (!parsedIdToken || !CacheHelpers.isIdTokenEntity(parsedIdToken)) {
470
- this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
471
- this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
472
- return null;
473
- }
474
- this.logger.trace("BrowserCacheManager.getIdTokenCredential: cache hit");
475
- return parsedIdToken;
476
- }
477
- /**
478
- * set IdToken credential to the platform cache
479
- * @param idToken
480
- */
481
- setIdTokenCredential(idToken) {
482
- this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
483
- const idTokenKey = CacheHelpers.generateCredentialKey(idToken);
484
- this.setItem(idTokenKey, JSON.stringify(idToken));
485
- this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
486
- }
487
- /**
488
- * generates accessToken entity from a string
489
- * @param key
490
- */
491
- getAccessTokenCredential(accessTokenKey) {
492
- const value = this.getItem(accessTokenKey);
493
- if (!value) {
494
- this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
495
- this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
496
- return null;
497
- }
498
- const parsedAccessToken = this.validateAndParseJson(value);
499
- if (!parsedAccessToken ||
500
- !CacheHelpers.isAccessTokenEntity(parsedAccessToken)) {
501
- this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
502
- this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
503
- return null;
504
- }
505
- this.logger.trace("BrowserCacheManager.getAccessTokenCredential: cache hit");
506
- return parsedAccessToken;
507
- }
508
- /**
509
- * set accessToken credential to the platform cache
510
- * @param accessToken
511
- */
512
- setAccessTokenCredential(accessToken) {
513
- this.logger.trace("BrowserCacheManager.setAccessTokenCredential called");
514
- const accessTokenKey = CacheHelpers.generateCredentialKey(accessToken);
515
- this.setItem(accessTokenKey, JSON.stringify(accessToken));
516
- this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
517
- }
518
- /**
519
- * generates refreshToken entity from a string
520
- * @param refreshTokenKey
521
- */
522
- getRefreshTokenCredential(refreshTokenKey) {
523
- const value = this.getItem(refreshTokenKey);
524
- if (!value) {
525
- this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
526
- this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
527
- return null;
528
- }
529
- const parsedRefreshToken = this.validateAndParseJson(value);
530
- if (!parsedRefreshToken ||
531
- !CacheHelpers.isRefreshTokenEntity(parsedRefreshToken)) {
532
- this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
533
- this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
534
- return null;
535
- }
536
- this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: cache hit");
537
- return parsedRefreshToken;
538
- }
539
- /**
540
- * set refreshToken credential to the platform cache
541
- * @param refreshToken
542
- */
543
- setRefreshTokenCredential(refreshToken) {
544
- this.logger.trace("BrowserCacheManager.setRefreshTokenCredential called");
545
- const refreshTokenKey = CacheHelpers.generateCredentialKey(refreshToken);
546
- this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
547
- this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
548
- }
549
- /**
550
- * fetch appMetadata entity from the platform cache
551
- * @param appMetadataKey
552
- */
553
- getAppMetadata(appMetadataKey) {
554
- const value = this.getItem(appMetadataKey);
555
- if (!value) {
556
- this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
557
- return null;
558
- }
559
- const parsedMetadata = this.validateAndParseJson(value);
560
- if (!parsedMetadata ||
561
- !AppMetadataEntity.isAppMetadataEntity(appMetadataKey, parsedMetadata)) {
562
- this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
563
- return null;
564
- }
565
- this.logger.trace("BrowserCacheManager.getAppMetadata: cache hit");
566
- return CacheManager.toObject(new AppMetadataEntity(), parsedMetadata);
567
- }
568
- /**
569
- * set appMetadata entity to the platform cache
570
- * @param appMetadata
571
- */
572
- setAppMetadata(appMetadata) {
573
- this.logger.trace("BrowserCacheManager.setAppMetadata called");
574
- const appMetadataKey = appMetadata.generateAppMetadataKey();
575
- this.setItem(appMetadataKey, JSON.stringify(appMetadata));
576
- }
577
- /**
578
- * fetch server telemetry entity from the platform cache
579
- * @param serverTelemetryKey
580
- */
581
- getServerTelemetry(serverTelemetryKey) {
582
- const value = this.getItem(serverTelemetryKey);
583
- if (!value) {
584
- this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
585
- return null;
586
- }
587
- const parsedEntity = this.validateAndParseJson(value);
588
- if (!parsedEntity ||
589
- !CacheHelpers.isServerTelemetryEntity(serverTelemetryKey, parsedEntity)) {
590
- this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
591
- return null;
592
- }
593
- this.logger.trace("BrowserCacheManager.getServerTelemetry: cache hit");
594
- return parsedEntity;
595
- }
596
- /**
597
- * set server telemetry entity to the platform cache
598
- * @param serverTelemetryKey
599
- * @param serverTelemetry
600
- */
601
- setServerTelemetry(serverTelemetryKey, serverTelemetry) {
602
- this.logger.trace("BrowserCacheManager.setServerTelemetry called");
603
- this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
604
- }
605
- /**
606
- *
607
- */
608
- getAuthorityMetadata(key) {
609
- const value = this.internalStorage.getItem(key);
610
- if (!value) {
611
- this.logger.trace("BrowserCacheManager.getAuthorityMetadata: called, no cache hit");
612
- return null;
613
- }
614
- const parsedMetadata = this.validateAndParseJson(value);
615
- if (parsedMetadata &&
616
- AuthorityMetadataEntity.isAuthorityMetadataEntity(key, parsedMetadata)) {
617
- this.logger.trace("BrowserCacheManager.getAuthorityMetadata: cache hit");
618
- return CacheManager.toObject(new AuthorityMetadataEntity(), parsedMetadata);
619
- }
620
- return null;
621
- }
622
- /**
623
- *
624
- */
625
- getAuthorityMetadataKeys() {
626
- const allKeys = this.internalStorage.getKeys();
627
- return allKeys.filter((key) => {
628
- return this.isAuthorityMetadata(key);
629
- });
630
- }
631
- /**
632
- * Sets wrapper metadata in memory
633
- * @param wrapperSKU
634
- * @param wrapperVersion
635
- */
636
- setWrapperMetadata(wrapperSKU, wrapperVersion) {
637
- this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_SKU, wrapperSKU);
638
- this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_VER, wrapperVersion);
639
- }
640
- /**
641
- * Returns wrapper metadata from in-memory storage
642
- */
643
- getWrapperMetadata() {
644
- const sku = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_SKU) ||
645
- Constants.EMPTY_STRING;
646
- const version = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_VER) ||
647
- Constants.EMPTY_STRING;
648
- return [sku, version];
649
- }
650
- /**
651
- *
652
- * @param entity
653
- */
654
- setAuthorityMetadata(key, entity) {
655
- this.logger.trace("BrowserCacheManager.setAuthorityMetadata called");
656
- this.internalStorage.setItem(key, JSON.stringify(entity));
657
- }
658
- /**
659
- * Gets the active account
660
- */
661
- getActiveAccount() {
662
- const activeAccountKeyFilters = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
663
- const activeAccountValueFilters = this.getItem(activeAccountKeyFilters);
664
- if (!activeAccountValueFilters) {
665
- // if new active account cache type isn't found, it's an old version, so look for that instead
666
- this.logger.trace("BrowserCacheManager.getActiveAccount: No active account filters cache schema found, looking for legacy schema");
667
- const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
668
- const activeAccountValueLocal = this.getItem(activeAccountKeyLocal);
669
- if (!activeAccountValueLocal) {
670
- this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
671
- return null;
672
- }
673
- const activeAccount = this.getAccountInfoFilteredBy({
674
- localAccountId: activeAccountValueLocal,
675
- });
676
- if (activeAccount) {
677
- this.logger.trace("BrowserCacheManager.getActiveAccount: Legacy active account cache schema found");
678
- this.logger.trace("BrowserCacheManager.getActiveAccount: Adding active account filters cache schema");
679
- this.setActiveAccount(activeAccount);
680
- return activeAccount;
681
- }
682
- return null;
683
- }
684
- const activeAccountValueObj = this.validateAndParseJson(activeAccountValueFilters);
685
- if (activeAccountValueObj) {
686
- this.logger.trace("BrowserCacheManager.getActiveAccount: Active account filters schema found");
687
- return this.getAccountInfoFilteredBy({
688
- homeAccountId: activeAccountValueObj.homeAccountId,
689
- localAccountId: activeAccountValueObj.localAccountId,
690
- });
691
- }
692
- this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
693
- return null;
694
- }
695
- /**
696
- * Sets the active account's localAccountId in cache
697
- * @param account
698
- */
699
- setActiveAccount(account) {
700
- const activeAccountKey = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
701
- const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
702
- if (account) {
703
- this.logger.verbose("setActiveAccount: Active account set");
704
- const activeAccountValue = {
705
- homeAccountId: account.homeAccountId,
706
- localAccountId: account.localAccountId,
707
- };
708
- this.browserStorage.setItem(activeAccountKey, JSON.stringify(activeAccountValue));
709
- this.browserStorage.setItem(activeAccountKeyLocal, account.localAccountId);
710
- }
711
- else {
712
- this.logger.verbose("setActiveAccount: No account passed, active account not set");
713
- this.browserStorage.removeItem(activeAccountKey);
714
- this.browserStorage.removeItem(activeAccountKeyLocal);
715
- }
716
- }
717
- /**
718
- * fetch throttling entity from the platform cache
719
- * @param throttlingCacheKey
720
- */
721
- getThrottlingCache(throttlingCacheKey) {
722
- const value = this.getItem(throttlingCacheKey);
723
- if (!value) {
724
- this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
725
- return null;
726
- }
727
- const parsedThrottlingCache = this.validateAndParseJson(value);
728
- if (!parsedThrottlingCache ||
729
- !ThrottlingEntity.isThrottlingEntity(throttlingCacheKey, parsedThrottlingCache)) {
730
- this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
731
- return null;
732
- }
733
- this.logger.trace("BrowserCacheManager.getThrottlingCache: cache hit");
734
- return CacheManager.toObject(new ThrottlingEntity(), parsedThrottlingCache);
735
- }
736
- /**
737
- * set throttling entity to the platform cache
738
- * @param throttlingCacheKey
739
- * @param throttlingCache
740
- */
741
- setThrottlingCache(throttlingCacheKey, throttlingCache) {
742
- this.logger.trace("BrowserCacheManager.setThrottlingCache called");
743
- this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
744
- }
745
- /**
746
- * Gets cache item with given key.
747
- * Will retrieve from cookies if storeAuthStateInCookie is set to true.
748
- * @param key
749
- */
750
- getTemporaryCache(cacheKey, generateKey) {
751
- const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
752
- if (this.cacheConfig.storeAuthStateInCookie) {
753
- const itemCookie = this.getItemCookie(key);
754
- if (itemCookie) {
755
- this.logger.trace("BrowserCacheManager.getTemporaryCache: storeAuthStateInCookies set to true, retrieving from cookies");
756
- return itemCookie;
757
- }
758
- }
759
- const value = this.temporaryCacheStorage.getItem(key);
760
- if (!value) {
761
- // If temp cache item not found in session/memory, check local storage for items set by old versions
762
- if (this.cacheConfig.cacheLocation ===
763
- BrowserCacheLocation.LocalStorage) {
764
- const item = this.browserStorage.getItem(key);
765
- if (item) {
766
- this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item found in local storage");
767
- return item;
768
- }
769
- }
770
- this.logger.trace("BrowserCacheManager.getTemporaryCache: No cache item found in local storage");
771
- return null;
772
- }
773
- this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item returned");
774
- return value;
775
- }
776
- /**
777
- * Sets the cache item with the key and value given.
778
- * Stores in cookie if storeAuthStateInCookie is set to true.
779
- * This can cause cookie overflow if used incorrectly.
780
- * @param key
781
- * @param value
782
- */
783
- setTemporaryCache(cacheKey, value, generateKey) {
784
- const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
785
- this.temporaryCacheStorage.setItem(key, value);
786
- if (this.cacheConfig.storeAuthStateInCookie) {
787
- this.logger.trace("BrowserCacheManager.setTemporaryCache: storeAuthStateInCookie set to true, setting item cookie");
788
- this.setItemCookie(key, value);
789
- }
790
- }
791
- /**
792
- * Removes the cache item with the given key.
793
- * Will also clear the cookie item if storeAuthStateInCookie is set to true.
794
- * @param key
795
- */
796
- removeItem(key) {
797
- this.browserStorage.removeItem(key);
798
- this.temporaryCacheStorage.removeItem(key);
799
- if (this.cacheConfig.storeAuthStateInCookie) {
800
- this.logger.trace("BrowserCacheManager.removeItem: storeAuthStateInCookie is true, clearing item cookie");
801
- this.clearItemCookie(key);
802
- }
803
- }
804
- /**
805
- * Checks whether key is in cache.
806
- * @param key
807
- */
808
- containsKey(key) {
809
- return (this.browserStorage.containsKey(key) ||
810
- this.temporaryCacheStorage.containsKey(key));
811
- }
812
- /**
813
- * Gets all keys in window.
814
- */
815
- getKeys() {
816
- return [
817
- ...this.browserStorage.getKeys(),
818
- ...this.temporaryCacheStorage.getKeys(),
819
- ];
820
- }
821
- /**
822
- * Clears all cache entries created by MSAL.
823
- */
824
- async clear() {
825
- // Removes all accounts and their credentials
826
- await this.removeAllAccounts();
827
- this.removeAppMetadata();
828
- // Removes all remaining MSAL cache items
829
- this.getKeys().forEach((cacheKey) => {
830
- // Check if key contains msal prefix; For now, we are clearing all the cache items created by MSAL.js
831
- if ((this.browserStorage.containsKey(cacheKey) ||
832
- this.temporaryCacheStorage.containsKey(cacheKey)) &&
833
- (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
834
- cacheKey.indexOf(this.clientId) !== -1)) {
835
- this.removeItem(cacheKey);
836
- }
837
- });
838
- this.internalStorage.clear();
839
- }
840
- /**
841
- * Clears all access tokes that have claims prior to saving the current one
842
- * @param performanceClient {IPerformanceClient}
843
- * @returns
844
- */
845
- async clearTokensAndKeysWithClaims(performanceClient) {
846
- performanceClient.addQueueMeasurement(PerformanceEvents.ClearTokensAndKeysWithClaims);
847
- const tokenKeys = this.getTokenKeys();
848
- const removedAccessTokens = [];
849
- tokenKeys.accessToken.forEach((key) => {
850
- // if the access token has claims in its key, remove the token key and the token
851
- const credential = this.getAccessTokenCredential(key);
852
- if (credential?.requestedClaimsHash &&
853
- key.includes(credential.requestedClaimsHash.toLowerCase())) {
854
- removedAccessTokens.push(this.removeAccessToken(key));
855
- }
856
- });
857
- await Promise.all(removedAccessTokens);
858
- // warn if any access tokens are removed
859
- if (removedAccessTokens.length > 0) {
860
- this.logger.warning(`${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`);
861
- }
862
- }
863
- /**
864
- * Add value to cookies
865
- * @param cookieName
866
- * @param cookieValue
867
- * @param expires
868
- */
869
- setItemCookie(cookieName, cookieValue, expires) {
870
- let cookieStr = `${encodeURIComponent(cookieName)}=${encodeURIComponent(cookieValue)};path=/;SameSite=Lax;`;
871
- if (expires) {
872
- const expireTime = this.getCookieExpirationTime(expires);
873
- cookieStr += `expires=${expireTime};`;
874
- }
875
- if (this.cacheConfig.secureCookies) {
876
- cookieStr += "Secure;";
877
- }
878
- document.cookie = cookieStr;
879
- }
880
- /**
881
- * Get one item by key from cookies
882
- * @param cookieName
883
- */
884
- getItemCookie(cookieName) {
885
- const name = `${encodeURIComponent(cookieName)}=`;
886
- const cookieList = document.cookie.split(";");
887
- for (let i = 0; i < cookieList.length; i++) {
888
- let cookie = cookieList[i];
889
- while (cookie.charAt(0) === " ") {
890
- cookie = cookie.substring(1);
891
- }
892
- if (cookie.indexOf(name) === 0) {
893
- return decodeURIComponent(cookie.substring(name.length, cookie.length));
894
- }
895
- }
896
- return Constants.EMPTY_STRING;
897
- }
898
- /**
899
- * Clear all msal-related cookies currently set in the browser. Should only be used to clear temporary cache items.
900
- */
901
- clearMsalCookies() {
902
- const cookiePrefix = `${Constants.CACHE_PREFIX}.${this.clientId}`;
903
- const cookieList = document.cookie.split(";");
904
- cookieList.forEach((cookie) => {
905
- while (cookie.charAt(0) === " ") {
906
- // eslint-disable-next-line no-param-reassign
907
- cookie = cookie.substring(1);
908
- }
909
- if (cookie.indexOf(cookiePrefix) === 0) {
910
- const cookieKey = cookie.split("=")[0];
911
- this.clearItemCookie(cookieKey);
912
- }
913
- });
914
- }
915
- /**
916
- * Clear an item in the cookies by key
917
- * @param cookieName
918
- */
919
- clearItemCookie(cookieName) {
920
- this.setItemCookie(cookieName, Constants.EMPTY_STRING, -1);
921
- }
922
- /**
923
- * Get cookie expiration time
924
- * @param cookieLifeDays
925
- */
926
- getCookieExpirationTime(cookieLifeDays) {
927
- const today = new Date();
928
- const expr = new Date(today.getTime() + cookieLifeDays * this.COOKIE_LIFE_MULTIPLIER);
929
- return expr.toUTCString();
930
- }
931
- /**
932
- * Gets the cache object referenced by the browser
933
- */
934
- getCache() {
935
- return this.browserStorage;
936
- }
937
- /**
938
- * interface compat, we cannot overwrite browser cache; Functionality is supported by individual entities in browser
939
- */
940
- setCache() {
941
- // sets nothing
942
- }
943
- /**
944
- * Prepend msal.<client-id> to each key; Skip for any JSON object as Key (defined schemas do not need the key appended: AccessToken Keys or the upcoming schema)
945
- * @param key
946
- * @param addInstanceId
947
- */
948
- generateCacheKey(key) {
949
- const generatedKey = this.validateAndParseJson(key);
950
- if (!generatedKey) {
951
- if (StringUtils.startsWith(key, Constants.CACHE_PREFIX) ||
952
- StringUtils.startsWith(key, PersistentCacheKeys.ADAL_ID_TOKEN)) {
953
- return key;
954
- }
955
- return `${Constants.CACHE_PREFIX}.${this.clientId}.${key}`;
956
- }
957
- return JSON.stringify(key);
958
- }
959
- /**
960
- * Create authorityKey to cache authority
961
- * @param state
962
- */
963
- generateAuthorityKey(stateString) {
964
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
965
- return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
966
- }
967
- /**
968
- * Create Nonce key to cache nonce
969
- * @param state
970
- */
971
- generateNonceKey(stateString) {
972
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
973
- return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
974
- }
975
- /**
976
- * Creates full cache key for the request state
977
- * @param stateString State string for the request
978
- */
979
- generateStateKey(stateString) {
980
- // Use the library state id to key temp storage for uniqueness for multiple concurrent requests
981
- const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
982
- return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
983
- }
984
- /**
985
- * Gets the cached authority based on the cached state. Returns empty if no cached state found.
986
- */
987
- getCachedAuthority(cachedState) {
988
- const stateCacheKey = this.generateStateKey(cachedState);
989
- const state = this.getTemporaryCache(stateCacheKey);
990
- if (!state) {
991
- return null;
992
- }
993
- const authorityCacheKey = this.generateAuthorityKey(state);
994
- return this.getTemporaryCache(authorityCacheKey);
995
- }
996
- /**
997
- * Updates account, authority, and state in cache
998
- * @param serverAuthenticationRequest
999
- * @param account
1000
- */
1001
- updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
1002
- this.logger.trace("BrowserCacheManager.updateCacheEntries called");
1003
- // Cache the request state
1004
- const stateCacheKey = this.generateStateKey(state);
1005
- this.setTemporaryCache(stateCacheKey, state, false);
1006
- // Cache the nonce
1007
- const nonceCacheKey = this.generateNonceKey(state);
1008
- this.setTemporaryCache(nonceCacheKey, nonce, false);
1009
- // Cache authorityKey
1010
- const authorityCacheKey = this.generateAuthorityKey(state);
1011
- this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
1012
- if (account) {
1013
- const ccsCredential = {
1014
- credential: account.homeAccountId,
1015
- type: CcsCredentialType.HOME_ACCOUNT_ID,
1016
- };
1017
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
1018
- }
1019
- else if (loginHint) {
1020
- const ccsCredential = {
1021
- credential: loginHint,
1022
- type: CcsCredentialType.UPN,
1023
- };
1024
- this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
1025
- }
1026
- }
1027
- /**
1028
- * Reset all temporary cache items
1029
- * @param state
1030
- */
1031
- resetRequestCache(state) {
1032
- this.logger.trace("BrowserCacheManager.resetRequestCache called");
1033
- // check state and remove associated cache items
1034
- if (state) {
1035
- this.getKeys().forEach((key) => {
1036
- if (key.indexOf(state) !== -1) {
1037
- this.removeItem(key);
1038
- }
1039
- });
1040
- // delete generic interactive request parameters
1041
- this.removeItem(this.generateStateKey(state));
1042
- this.removeItem(this.generateNonceKey(state));
1043
- this.removeItem(this.generateAuthorityKey(state));
1044
- }
1045
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
1046
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
1047
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
1048
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
1049
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
1050
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
1051
- this.setInteractionInProgress(false);
1052
- }
1053
- /**
1054
- * Removes temporary cache for the provided state
1055
- * @param stateString
1056
- */
1057
- cleanRequestByState(stateString) {
1058
- this.logger.trace("BrowserCacheManager.cleanRequestByState called");
1059
- // Interaction is completed - remove interaction status.
1060
- if (stateString) {
1061
- const stateKey = this.generateStateKey(stateString);
1062
- const cachedState = this.temporaryCacheStorage.getItem(stateKey);
1063
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
1064
- this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
1065
- }
1066
- this.clearMsalCookies();
1067
- }
1068
- /**
1069
- * Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
1070
- * Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
1071
- * @param interactionType
1072
- */
1073
- cleanRequestByInteractionType(interactionType) {
1074
- this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
1075
- // Loop through all keys to find state key
1076
- this.getKeys().forEach((key) => {
1077
- // If this key is not the state key, move on
1078
- if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
1079
- return;
1080
- }
1081
- // Retrieve state value, return if not a valid value
1082
- const stateValue = this.temporaryCacheStorage.getItem(key);
1083
- if (!stateValue) {
1084
- return;
1085
- }
1086
- // Extract state and ensure it matches given InteractionType, then clean request cache
1087
- const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
1088
- if (parsedState &&
1089
- parsedState.interactionType === interactionType) {
1090
- this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
1091
- this.resetRequestCache(stateValue);
1092
- }
1093
- });
1094
- this.clearMsalCookies();
1095
- this.setInteractionInProgress(false);
1096
- }
1097
- cacheCodeRequest(authCodeRequest) {
1098
- this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
1099
- const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
1100
- this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
1101
- }
1102
- /**
1103
- * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
1104
- */
1105
- getCachedRequest(state) {
1106
- this.logger.trace("BrowserCacheManager.getCachedRequest called");
1107
- // Get token request from cache and parse as TokenExchangeParameters.
1108
- const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
1109
- if (!encodedTokenRequest) {
1110
- throw createBrowserAuthError(noTokenRequestCacheError);
1111
- }
1112
- let parsedRequest;
1113
- try {
1114
- parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
1115
- }
1116
- catch (e) {
1117
- this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
1118
- this.logger.error(`Parsing cached token request threw with error: ${e}`);
1119
- throw createBrowserAuthError(unableToParseTokenRequestCacheError);
1120
- }
1121
- this.removeItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
1122
- // Get cached authority and use if no authority is cached with request.
1123
- if (!parsedRequest.authority) {
1124
- const authorityCacheKey = this.generateAuthorityKey(state);
1125
- const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
1126
- if (!cachedAuthority) {
1127
- throw createBrowserAuthError(noCachedAuthorityError);
1128
- }
1129
- parsedRequest.authority = cachedAuthority;
1130
- }
1131
- return parsedRequest;
1132
- }
1133
- /**
1134
- * Gets cached native request for redirect flows
1135
- */
1136
- getCachedNativeRequest() {
1137
- this.logger.trace("BrowserCacheManager.getCachedNativeRequest called");
1138
- const cachedRequest = this.getTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, true);
1139
- if (!cachedRequest) {
1140
- this.logger.trace("BrowserCacheManager.getCachedNativeRequest: No cached native request found");
1141
- return null;
1142
- }
1143
- const parsedRequest = this.validateAndParseJson(cachedRequest);
1144
- if (!parsedRequest) {
1145
- this.logger.error("BrowserCacheManager.getCachedNativeRequest: Unable to parse native request");
1146
- return null;
1147
- }
1148
- return parsedRequest;
1149
- }
1150
- isInteractionInProgress(matchClientId) {
1151
- const clientId = this.getInteractionInProgress();
1152
- if (matchClientId) {
1153
- return clientId === this.clientId;
1154
- }
1155
- else {
1156
- return !!clientId;
1157
- }
1158
- }
1159
- getInteractionInProgress() {
1160
- const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1161
- return this.getTemporaryCache(key, false);
1162
- }
1163
- setInteractionInProgress(inProgress) {
1164
- // Ensure we don't overwrite interaction in progress for a different clientId
1165
- const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1166
- if (inProgress) {
1167
- if (this.getInteractionInProgress()) {
1168
- throw createBrowserAuthError(interactionInProgress);
1169
- }
1170
- else {
1171
- // No interaction is in progress
1172
- this.setTemporaryCache(key, this.clientId, false);
1173
- }
1174
- }
1175
- else if (!inProgress &&
1176
- this.getInteractionInProgress() === this.clientId) {
1177
- this.removeItem(key);
1178
- }
1179
- }
1180
- /**
1181
- * Returns username retrieved from ADAL or MSAL v1 idToken
1182
- * @deprecated
1183
- */
1184
- getLegacyLoginHint() {
1185
- // Only check for adal/msal token if no SSO params are being used
1186
- const adalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ADAL_ID_TOKEN);
1187
- if (adalIdTokenString) {
1188
- this.browserStorage.removeItem(PersistentCacheKeys.ADAL_ID_TOKEN);
1189
- this.logger.verbose("Cached ADAL id token retrieved.");
1190
- }
1191
- // Check for cached MSAL v1 id token
1192
- const msalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ID_TOKEN, true);
1193
- if (msalIdTokenString) {
1194
- this.removeItem(this.generateCacheKey(PersistentCacheKeys.ID_TOKEN));
1195
- this.logger.verbose("Cached MSAL.js v1 id token retrieved");
1196
- }
1197
- const cachedIdTokenString = msalIdTokenString || adalIdTokenString;
1198
- if (cachedIdTokenString) {
1199
- const idTokenClaims = AuthToken.extractTokenClaims(cachedIdTokenString, base64Decode);
1200
- if (idTokenClaims.preferred_username) {
1201
- this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 preferred_username as loginHint");
1202
- return idTokenClaims.preferred_username;
1203
- }
1204
- else if (idTokenClaims.upn) {
1205
- this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 upn as loginHint");
1206
- return idTokenClaims.upn;
1207
- }
1208
- else {
1209
- this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, however, no account hint claim found. Enable preferred_username or upn id token claim to get SSO.");
1210
- }
1211
- }
1212
- return null;
1213
- }
1214
- /**
1215
- * Updates a credential's cache key if the current cache key is outdated
1216
- */
1217
- updateCredentialCacheKey(currentCacheKey, credential) {
1218
- const updatedCacheKey = CacheHelpers.generateCredentialKey(credential);
1219
- if (currentCacheKey !== updatedCacheKey) {
1220
- const cacheItem = this.getItem(currentCacheKey);
1221
- if (cacheItem) {
1222
- this.removeItem(currentCacheKey);
1223
- this.setItem(updatedCacheKey, cacheItem);
1224
- this.logger.verbose(`Updated an outdated ${credential.credentialType} cache key`);
1225
- return updatedCacheKey;
1226
- }
1227
- else {
1228
- this.logger.error(`Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`);
1229
- }
1230
- }
1231
- return currentCacheKey;
1232
- }
1233
- /**
1234
- * Returns application id as redirect context during AcquireTokenRedirect flow.
1235
- */
1236
- getRedirectRequestContext() {
1237
- return this.getTemporaryCache(TemporaryCacheKeys.REDIRECT_CONTEXT, true);
1238
- }
1239
- /**
1240
- * Sets application id as the redirect context during AcquireTokenRedirect flow.
1241
- * @param value
1242
- */
1243
- setRedirectRequestContext(value) {
1244
- this.setTemporaryCache(TemporaryCacheKeys.REDIRECT_CONTEXT, value, true);
1245
- }
1246
- /**
1247
- * Builds credential entities from AuthenticationResult object and saves the resulting credentials to the cache
1248
- * @param result
1249
- * @param request
1250
- */
1251
- async hydrateCache(result, request) {
1252
- const idTokenEntity = CacheHelpers.createIdTokenEntity(result.account?.homeAccountId, result.account?.environment, result.idToken, this.clientId, result.tenantId);
1253
- let claimsHash;
1254
- if (request.claims) {
1255
- claimsHash = await this.cryptoImpl.hashString(request.claims);
1256
- }
1257
- const accessTokenEntity = CacheHelpers.createAccessTokenEntity(result.account?.homeAccountId, result.account.environment, result.accessToken, this.clientId, result.tenantId, result.scopes.join(" "), result.expiresOn?.getTime() || 0, result.extExpiresOn?.getTime() || 0, base64Decode, undefined, // refreshOn
1258
- result.tokenType, undefined, // userAssertionHash
1259
- request.sshKid, request.claims, claimsHash);
1260
- const cacheRecord = new CacheRecord(undefined, idTokenEntity, accessTokenEntity);
1261
- return this.saveCacheRecord(cacheRecord);
1262
- }
1263
- }
1264
- const DEFAULT_BROWSER_CACHE_MANAGER = (clientId, logger) => {
1265
- const cacheOptions = {
1266
- cacheLocation: BrowserCacheLocation.MemoryStorage,
1267
- temporaryCacheLocation: BrowserCacheLocation.MemoryStorage,
1268
- storeAuthStateInCookie: false,
1269
- secureCookies: false,
1270
- cacheMigrationEnabled: false,
1271
- claimsBasedCachingEnabled: false,
1272
- };
1273
- return new BrowserCacheManager(clientId, cacheOptions, DEFAULT_CRYPTO_IMPLEMENTATION, logger);
13
+ /*
14
+ * Copyright (c) Microsoft Corporation. All rights reserved.
15
+ * Licensed under the MIT License.
16
+ */
17
+ /**
18
+ * This class implements the cache storage interface for MSAL through browser local or session storage.
19
+ * Cookies are only used if storeAuthStateInCookie is true, and are only used for
20
+ * parameters such as state and nonce, generally.
21
+ */
22
+ class BrowserCacheManager extends CacheManager {
23
+ constructor(clientId, cacheConfig, cryptoImpl, logger, staticAuthorityOptions) {
24
+ super(clientId, cryptoImpl, logger, staticAuthorityOptions);
25
+ // Cookie life calculation (hours * minutes * seconds * ms)
26
+ this.COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
27
+ this.cacheConfig = cacheConfig;
28
+ this.logger = logger;
29
+ this.internalStorage = new MemoryStorage();
30
+ this.browserStorage = this.setupBrowserStorage(this.cacheConfig.cacheLocation);
31
+ this.temporaryCacheStorage = this.setupTemporaryCacheStorage(this.cacheConfig.temporaryCacheLocation, this.cacheConfig.cacheLocation);
32
+ // Migrate cache entries from older versions of MSAL.
33
+ if (cacheConfig.cacheMigrationEnabled) {
34
+ this.migrateCacheEntries();
35
+ this.createKeyMaps();
36
+ }
37
+ }
38
+ /**
39
+ * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured cacheLocation.
40
+ * @param cacheLocation
41
+ */
42
+ setupBrowserStorage(cacheLocation) {
43
+ switch (cacheLocation) {
44
+ case BrowserCacheLocation.LocalStorage:
45
+ case BrowserCacheLocation.SessionStorage:
46
+ try {
47
+ return new BrowserStorage(cacheLocation);
48
+ }
49
+ catch (e) {
50
+ this.logger.verbose(e);
51
+ break;
52
+ }
53
+ }
54
+ this.cacheConfig.cacheLocation = BrowserCacheLocation.MemoryStorage;
55
+ return new MemoryStorage();
56
+ }
57
+ /**
58
+ * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured temporaryCacheLocation.
59
+ * @param temporaryCacheLocation
60
+ * @param cacheLocation
61
+ */
62
+ setupTemporaryCacheStorage(temporaryCacheLocation, cacheLocation) {
63
+ switch (cacheLocation) {
64
+ case BrowserCacheLocation.LocalStorage:
65
+ case BrowserCacheLocation.SessionStorage:
66
+ try {
67
+ // Temporary cache items will always be stored in session storage to mitigate problems caused by multiple tabs
68
+ return new BrowserStorage(temporaryCacheLocation ||
69
+ BrowserCacheLocation.SessionStorage);
70
+ }
71
+ catch (e) {
72
+ this.logger.verbose(e);
73
+ return this.internalStorage;
74
+ }
75
+ case BrowserCacheLocation.MemoryStorage:
76
+ default:
77
+ return this.internalStorage;
78
+ }
79
+ }
80
+ /**
81
+ * Migrate all old cache entries to new schema. No rollback supported.
82
+ * @param storeAuthStateInCookie
83
+ */
84
+ migrateCacheEntries() {
85
+ const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
86
+ const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
87
+ const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
88
+ const errorDescKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR_DESC}`;
89
+ const idTokenValue = this.browserStorage.getItem(idTokenKey);
90
+ const clientInfoValue = this.browserStorage.getItem(clientInfoKey);
91
+ const errorValue = this.browserStorage.getItem(errorKey);
92
+ const errorDescValue = this.browserStorage.getItem(errorDescKey);
93
+ const values = [
94
+ idTokenValue,
95
+ clientInfoValue,
96
+ errorValue,
97
+ errorDescValue,
98
+ ];
99
+ const keysToMigrate = [
100
+ PersistentCacheKeys.ID_TOKEN,
101
+ PersistentCacheKeys.CLIENT_INFO,
102
+ PersistentCacheKeys.ERROR,
103
+ PersistentCacheKeys.ERROR_DESC,
104
+ ];
105
+ keysToMigrate.forEach((cacheKey, index) => this.migrateCacheEntry(cacheKey, values[index]));
106
+ }
107
+ /**
108
+ * Utility function to help with migration.
109
+ * @param newKey
110
+ * @param value
111
+ * @param storeAuthStateInCookie
112
+ */
113
+ migrateCacheEntry(newKey, value) {
114
+ if (value) {
115
+ this.setTemporaryCache(newKey, value, true);
116
+ }
117
+ }
118
+ /**
119
+ * Searches all cache entries for MSAL accounts and creates the account key map
120
+ * This is used to migrate users from older versions of MSAL which did not create the map.
121
+ * @returns
122
+ */
123
+ createKeyMaps() {
124
+ this.logger.trace("BrowserCacheManager - createKeyMaps called.");
125
+ const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
126
+ const tokenKeys = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
127
+ if (accountKeys && tokenKeys) {
128
+ this.logger.verbose("BrowserCacheManager:createKeyMaps - account and token key maps already exist, skipping migration.");
129
+ // Key maps already exist, no need to iterate through cache
130
+ return;
131
+ }
132
+ const allKeys = this.browserStorage.getKeys();
133
+ allKeys.forEach((key) => {
134
+ if (this.isCredentialKey(key)) {
135
+ // Get item, parse, validate and write key to map
136
+ const value = this.getItem(key);
137
+ if (value) {
138
+ const credObj = this.validateAndParseJson(value);
139
+ if (credObj && credObj.hasOwnProperty("credentialType")) {
140
+ switch (credObj["credentialType"]) {
141
+ case CredentialType.ID_TOKEN:
142
+ if (CacheHelpers.isIdTokenEntity(credObj)) {
143
+ this.logger.trace("BrowserCacheManager:createKeyMaps - idToken found, saving key to token key map");
144
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - idToken with key: ${key} found, saving key to token key map`);
145
+ const idTokenEntity = credObj;
146
+ const newKey = this.updateCredentialCacheKey(key, idTokenEntity);
147
+ this.addTokenKey(newKey, CredentialType.ID_TOKEN);
148
+ return;
149
+ }
150
+ else {
151
+ this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching idToken schema with value containing idToken credentialType field but value failed IdTokenEntity validation, skipping.");
152
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed idToken validation on key: ${key}`);
153
+ }
154
+ break;
155
+ case CredentialType.ACCESS_TOKEN:
156
+ case CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME:
157
+ if (CacheHelpers.isAccessTokenEntity(credObj)) {
158
+ this.logger.trace("BrowserCacheManager:createKeyMaps - accessToken found, saving key to token key map");
159
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - accessToken with key: ${key} found, saving key to token key map`);
160
+ const accessTokenEntity = credObj;
161
+ const newKey = this.updateCredentialCacheKey(key, accessTokenEntity);
162
+ this.addTokenKey(newKey, CredentialType.ACCESS_TOKEN);
163
+ return;
164
+ }
165
+ else {
166
+ this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching accessToken schema with value containing accessToken credentialType field but value failed AccessTokenEntity validation, skipping.");
167
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed accessToken validation on key: ${key}`);
168
+ }
169
+ break;
170
+ case CredentialType.REFRESH_TOKEN:
171
+ if (CacheHelpers.isRefreshTokenEntity(credObj)) {
172
+ this.logger.trace("BrowserCacheManager:createKeyMaps - refreshToken found, saving key to token key map");
173
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - refreshToken with key: ${key} found, saving key to token key map`);
174
+ const refreshTokenEntity = credObj;
175
+ const newKey = this.updateCredentialCacheKey(key, refreshTokenEntity);
176
+ this.addTokenKey(newKey, CredentialType.REFRESH_TOKEN);
177
+ return;
178
+ }
179
+ else {
180
+ this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching refreshToken schema with value containing refreshToken credentialType field but value failed RefreshTokenEntity validation, skipping.");
181
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed refreshToken validation on key: ${key}`);
182
+ }
183
+ break;
184
+ // If credentialType isn't one of our predefined ones, it may not be an MSAL cache value. Ignore.
185
+ }
186
+ }
187
+ }
188
+ }
189
+ if (this.isAccountKey(key)) {
190
+ const value = this.getItem(key);
191
+ if (value) {
192
+ const accountObj = this.validateAndParseJson(value);
193
+ if (accountObj &&
194
+ AccountEntity.isAccountEntity(accountObj)) {
195
+ this.logger.trace("BrowserCacheManager:createKeyMaps - account found, saving key to account key map");
196
+ this.logger.tracePii(`BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`);
197
+ this.addAccountKeyToMap(key);
198
+ }
199
+ }
200
+ }
201
+ });
202
+ }
203
+ /**
204
+ * Parses passed value as JSON object, JSON.parse() will throw an error.
205
+ * @param input
206
+ */
207
+ validateAndParseJson(jsonValue) {
208
+ try {
209
+ const parsedJson = JSON.parse(jsonValue);
210
+ /**
211
+ * There are edge cases in which JSON.parse will successfully parse a non-valid JSON object
212
+ * (e.g. JSON.parse will parse an escaped string into an unescaped string), so adding a type check
213
+ * of the parsed value is necessary in order to be certain that the string represents a valid JSON object.
214
+ *
215
+ */
216
+ return parsedJson && typeof parsedJson === "object"
217
+ ? parsedJson
218
+ : null;
219
+ }
220
+ catch (error) {
221
+ return null;
222
+ }
223
+ }
224
+ /**
225
+ * fetches the entry from the browser storage based off the key
226
+ * @param key
227
+ */
228
+ getItem(key) {
229
+ return this.browserStorage.getItem(key);
230
+ }
231
+ /**
232
+ * sets the entry in the browser storage
233
+ * @param key
234
+ * @param value
235
+ */
236
+ setItem(key, value) {
237
+ this.browserStorage.setItem(key, value);
238
+ }
239
+ /**
240
+ * fetch the account entity from the platform cache
241
+ * @param accountKey
242
+ */
243
+ getAccount(accountKey, logger) {
244
+ this.logger.trace("BrowserCacheManager.getAccount called");
245
+ const accountEntity = this.getCachedAccountEntity(accountKey);
246
+ return this.updateOutdatedCachedAccount(accountKey, accountEntity, logger);
247
+ }
248
+ /**
249
+ * Reads account from cache, deserializes it into an account entity and returns it.
250
+ * If account is not found from the key, returns null and removes key from map.
251
+ * @param accountKey
252
+ * @returns
253
+ */
254
+ getCachedAccountEntity(accountKey) {
255
+ const serializedAccount = this.getItem(accountKey);
256
+ if (!serializedAccount) {
257
+ this.removeAccountKeyFromMap(accountKey);
258
+ return null;
259
+ }
260
+ const parsedAccount = this.validateAndParseJson(serializedAccount);
261
+ if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
262
+ this.removeAccountKeyFromMap(accountKey);
263
+ return null;
264
+ }
265
+ return CacheManager.toObject(new AccountEntity(), parsedAccount);
266
+ }
267
+ /**
268
+ * set account entity in the platform cache
269
+ * @param account
270
+ */
271
+ setAccount(account) {
272
+ this.logger.trace("BrowserCacheManager.setAccount called");
273
+ const key = account.generateAccountKey();
274
+ this.setItem(key, JSON.stringify(account));
275
+ this.addAccountKeyToMap(key);
276
+ }
277
+ /**
278
+ * Returns the array of account keys currently cached
279
+ * @returns
280
+ */
281
+ getAccountKeys() {
282
+ this.logger.trace("BrowserCacheManager.getAccountKeys called");
283
+ const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
284
+ if (accountKeys) {
285
+ return JSON.parse(accountKeys);
286
+ }
287
+ this.logger.verbose("BrowserCacheManager.getAccountKeys - No account keys found");
288
+ return [];
289
+ }
290
+ /**
291
+ * Add a new account to the key map
292
+ * @param key
293
+ */
294
+ addAccountKeyToMap(key) {
295
+ this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
296
+ this.logger.tracePii(`BrowserCacheManager.addAccountKeyToMap called with key: ${key}`);
297
+ const accountKeys = this.getAccountKeys();
298
+ if (accountKeys.indexOf(key) === -1) {
299
+ // Only add key if it does not already exist in the map
300
+ accountKeys.push(key);
301
+ this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
302
+ this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key added");
303
+ }
304
+ else {
305
+ this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key already exists in map");
306
+ }
307
+ }
308
+ /**
309
+ * Remove an account from the key map
310
+ * @param key
311
+ */
312
+ removeAccountKeyFromMap(key) {
313
+ this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
314
+ this.logger.tracePii(`BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`);
315
+ const accountKeys = this.getAccountKeys();
316
+ const removalIndex = accountKeys.indexOf(key);
317
+ if (removalIndex > -1) {
318
+ accountKeys.splice(removalIndex, 1);
319
+ this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
320
+ this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap account key removed");
321
+ }
322
+ else {
323
+ this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap key not found in existing map");
324
+ }
325
+ }
326
+ /**
327
+ * Extends inherited removeAccount function to include removal of the account key from the map
328
+ * @param key
329
+ */
330
+ async removeAccount(key) {
331
+ void super.removeAccount(key);
332
+ this.removeAccountKeyFromMap(key);
333
+ }
334
+ /**
335
+ * Remove account entity from the platform cache if it's outdated
336
+ * @param accountKey
337
+ */
338
+ removeOutdatedAccount(accountKey) {
339
+ this.removeItem(accountKey);
340
+ this.removeAccountKeyFromMap(accountKey);
341
+ }
342
+ /**
343
+ * Removes given idToken from the cache and from the key map
344
+ * @param key
345
+ */
346
+ removeIdToken(key) {
347
+ super.removeIdToken(key);
348
+ this.removeTokenKey(key, CredentialType.ID_TOKEN);
349
+ }
350
+ /**
351
+ * Removes given accessToken from the cache and from the key map
352
+ * @param key
353
+ */
354
+ async removeAccessToken(key) {
355
+ void super.removeAccessToken(key);
356
+ this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
357
+ }
358
+ /**
359
+ * Removes given refreshToken from the cache and from the key map
360
+ * @param key
361
+ */
362
+ removeRefreshToken(key) {
363
+ super.removeRefreshToken(key);
364
+ this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
365
+ }
366
+ /**
367
+ * Gets the keys for the cached tokens associated with this clientId
368
+ * @returns
369
+ */
370
+ getTokenKeys() {
371
+ this.logger.trace("BrowserCacheManager.getTokenKeys called");
372
+ const item = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
373
+ if (item) {
374
+ const tokenKeys = this.validateAndParseJson(item);
375
+ if (tokenKeys &&
376
+ tokenKeys.hasOwnProperty("idToken") &&
377
+ tokenKeys.hasOwnProperty("accessToken") &&
378
+ tokenKeys.hasOwnProperty("refreshToken")) {
379
+ return tokenKeys;
380
+ }
381
+ else {
382
+ this.logger.error("BrowserCacheManager.getTokenKeys - Token keys found but in an unknown format. Returning empty key map.");
383
+ }
384
+ }
385
+ else {
386
+ this.logger.verbose("BrowserCacheManager.getTokenKeys - No token keys found");
387
+ }
388
+ return {
389
+ idToken: [],
390
+ accessToken: [],
391
+ refreshToken: [],
392
+ };
393
+ }
394
+ /**
395
+ * Adds the given key to the token key map
396
+ * @param key
397
+ * @param type
398
+ */
399
+ addTokenKey(key, type) {
400
+ this.logger.trace("BrowserCacheManager addTokenKey called");
401
+ const tokenKeys = this.getTokenKeys();
402
+ switch (type) {
403
+ case CredentialType.ID_TOKEN:
404
+ if (tokenKeys.idToken.indexOf(key) === -1) {
405
+ this.logger.info("BrowserCacheManager: addTokenKey - idToken added to map");
406
+ tokenKeys.idToken.push(key);
407
+ }
408
+ break;
409
+ case CredentialType.ACCESS_TOKEN:
410
+ if (tokenKeys.accessToken.indexOf(key) === -1) {
411
+ this.logger.info("BrowserCacheManager: addTokenKey - accessToken added to map");
412
+ tokenKeys.accessToken.push(key);
413
+ }
414
+ break;
415
+ case CredentialType.REFRESH_TOKEN:
416
+ if (tokenKeys.refreshToken.indexOf(key) === -1) {
417
+ this.logger.info("BrowserCacheManager: addTokenKey - refreshToken added to map");
418
+ tokenKeys.refreshToken.push(key);
419
+ }
420
+ break;
421
+ default:
422
+ this.logger.error(`BrowserCacheManager:addTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
423
+ throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
424
+ }
425
+ this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
426
+ }
427
+ /**
428
+ * Removes the given key from the token key map
429
+ * @param key
430
+ * @param type
431
+ */
432
+ removeTokenKey(key, type) {
433
+ this.logger.trace("BrowserCacheManager removeTokenKey called");
434
+ const tokenKeys = this.getTokenKeys();
435
+ switch (type) {
436
+ case CredentialType.ID_TOKEN:
437
+ this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove idToken with key: ${key} from map`);
438
+ const idRemoval = tokenKeys.idToken.indexOf(key);
439
+ if (idRemoval > -1) {
440
+ this.logger.info("BrowserCacheManager: removeTokenKey - idToken removed from map");
441
+ tokenKeys.idToken.splice(idRemoval, 1);
442
+ }
443
+ else {
444
+ this.logger.info("BrowserCacheManager: removeTokenKey - idToken does not exist in map. Either it was previously removed or it was never added.");
445
+ }
446
+ break;
447
+ case CredentialType.ACCESS_TOKEN:
448
+ this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove accessToken with key: ${key} from map`);
449
+ const accessRemoval = tokenKeys.accessToken.indexOf(key);
450
+ if (accessRemoval > -1) {
451
+ this.logger.info("BrowserCacheManager: removeTokenKey - accessToken removed from map");
452
+ tokenKeys.accessToken.splice(accessRemoval, 1);
453
+ }
454
+ else {
455
+ this.logger.info("BrowserCacheManager: removeTokenKey - accessToken does not exist in map. Either it was previously removed or it was never added.");
456
+ }
457
+ break;
458
+ case CredentialType.REFRESH_TOKEN:
459
+ this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove refreshToken with key: ${key} from map`);
460
+ const refreshRemoval = tokenKeys.refreshToken.indexOf(key);
461
+ if (refreshRemoval > -1) {
462
+ this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken removed from map");
463
+ tokenKeys.refreshToken.splice(refreshRemoval, 1);
464
+ }
465
+ else {
466
+ this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken does not exist in map. Either it was previously removed or it was never added.");
467
+ }
468
+ break;
469
+ default:
470
+ this.logger.error(`BrowserCacheManager:removeTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
471
+ throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
472
+ }
473
+ this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
474
+ }
475
+ /**
476
+ * generates idToken entity from a string
477
+ * @param idTokenKey
478
+ */
479
+ getIdTokenCredential(idTokenKey) {
480
+ const value = this.getItem(idTokenKey);
481
+ if (!value) {
482
+ this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
483
+ this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
484
+ return null;
485
+ }
486
+ const parsedIdToken = this.validateAndParseJson(value);
487
+ if (!parsedIdToken || !CacheHelpers.isIdTokenEntity(parsedIdToken)) {
488
+ this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
489
+ this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
490
+ return null;
491
+ }
492
+ this.logger.trace("BrowserCacheManager.getIdTokenCredential: cache hit");
493
+ return parsedIdToken;
494
+ }
495
+ /**
496
+ * set IdToken credential to the platform cache
497
+ * @param idToken
498
+ */
499
+ setIdTokenCredential(idToken) {
500
+ this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
501
+ const idTokenKey = CacheHelpers.generateCredentialKey(idToken);
502
+ this.setItem(idTokenKey, JSON.stringify(idToken));
503
+ this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
504
+ }
505
+ /**
506
+ * generates accessToken entity from a string
507
+ * @param key
508
+ */
509
+ getAccessTokenCredential(accessTokenKey) {
510
+ const value = this.getItem(accessTokenKey);
511
+ if (!value) {
512
+ this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
513
+ this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
514
+ return null;
515
+ }
516
+ const parsedAccessToken = this.validateAndParseJson(value);
517
+ if (!parsedAccessToken ||
518
+ !CacheHelpers.isAccessTokenEntity(parsedAccessToken)) {
519
+ this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
520
+ this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
521
+ return null;
522
+ }
523
+ this.logger.trace("BrowserCacheManager.getAccessTokenCredential: cache hit");
524
+ return parsedAccessToken;
525
+ }
526
+ /**
527
+ * set accessToken credential to the platform cache
528
+ * @param accessToken
529
+ */
530
+ setAccessTokenCredential(accessToken) {
531
+ this.logger.trace("BrowserCacheManager.setAccessTokenCredential called");
532
+ const accessTokenKey = CacheHelpers.generateCredentialKey(accessToken);
533
+ this.setItem(accessTokenKey, JSON.stringify(accessToken));
534
+ this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
535
+ }
536
+ /**
537
+ * generates refreshToken entity from a string
538
+ * @param refreshTokenKey
539
+ */
540
+ getRefreshTokenCredential(refreshTokenKey) {
541
+ const value = this.getItem(refreshTokenKey);
542
+ if (!value) {
543
+ this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
544
+ this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
545
+ return null;
546
+ }
547
+ const parsedRefreshToken = this.validateAndParseJson(value);
548
+ if (!parsedRefreshToken ||
549
+ !CacheHelpers.isRefreshTokenEntity(parsedRefreshToken)) {
550
+ this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
551
+ this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
552
+ return null;
553
+ }
554
+ this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: cache hit");
555
+ return parsedRefreshToken;
556
+ }
557
+ /**
558
+ * set refreshToken credential to the platform cache
559
+ * @param refreshToken
560
+ */
561
+ setRefreshTokenCredential(refreshToken) {
562
+ this.logger.trace("BrowserCacheManager.setRefreshTokenCredential called");
563
+ const refreshTokenKey = CacheHelpers.generateCredentialKey(refreshToken);
564
+ this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
565
+ this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
566
+ }
567
+ /**
568
+ * fetch appMetadata entity from the platform cache
569
+ * @param appMetadataKey
570
+ */
571
+ getAppMetadata(appMetadataKey) {
572
+ const value = this.getItem(appMetadataKey);
573
+ if (!value) {
574
+ this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
575
+ return null;
576
+ }
577
+ const parsedMetadata = this.validateAndParseJson(value);
578
+ if (!parsedMetadata ||
579
+ !CacheHelpers.isAppMetadataEntity(appMetadataKey, parsedMetadata)) {
580
+ this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
581
+ return null;
582
+ }
583
+ this.logger.trace("BrowserCacheManager.getAppMetadata: cache hit");
584
+ return parsedMetadata;
585
+ }
586
+ /**
587
+ * set appMetadata entity to the platform cache
588
+ * @param appMetadata
589
+ */
590
+ setAppMetadata(appMetadata) {
591
+ this.logger.trace("BrowserCacheManager.setAppMetadata called");
592
+ const appMetadataKey = CacheHelpers.generateAppMetadataKey(appMetadata);
593
+ this.setItem(appMetadataKey, JSON.stringify(appMetadata));
594
+ }
595
+ /**
596
+ * fetch server telemetry entity from the platform cache
597
+ * @param serverTelemetryKey
598
+ */
599
+ getServerTelemetry(serverTelemetryKey) {
600
+ const value = this.getItem(serverTelemetryKey);
601
+ if (!value) {
602
+ this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
603
+ return null;
604
+ }
605
+ const parsedEntity = this.validateAndParseJson(value);
606
+ if (!parsedEntity ||
607
+ !CacheHelpers.isServerTelemetryEntity(serverTelemetryKey, parsedEntity)) {
608
+ this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
609
+ return null;
610
+ }
611
+ this.logger.trace("BrowserCacheManager.getServerTelemetry: cache hit");
612
+ return parsedEntity;
613
+ }
614
+ /**
615
+ * set server telemetry entity to the platform cache
616
+ * @param serverTelemetryKey
617
+ * @param serverTelemetry
618
+ */
619
+ setServerTelemetry(serverTelemetryKey, serverTelemetry) {
620
+ this.logger.trace("BrowserCacheManager.setServerTelemetry called");
621
+ this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
622
+ }
623
+ /**
624
+ *
625
+ */
626
+ getAuthorityMetadata(key) {
627
+ const value = this.internalStorage.getItem(key);
628
+ if (!value) {
629
+ this.logger.trace("BrowserCacheManager.getAuthorityMetadata: called, no cache hit");
630
+ return null;
631
+ }
632
+ const parsedMetadata = this.validateAndParseJson(value);
633
+ if (parsedMetadata &&
634
+ CacheHelpers.isAuthorityMetadataEntity(key, parsedMetadata)) {
635
+ this.logger.trace("BrowserCacheManager.getAuthorityMetadata: cache hit");
636
+ return parsedMetadata;
637
+ }
638
+ return null;
639
+ }
640
+ /**
641
+ *
642
+ */
643
+ getAuthorityMetadataKeys() {
644
+ const allKeys = this.internalStorage.getKeys();
645
+ return allKeys.filter((key) => {
646
+ return this.isAuthorityMetadata(key);
647
+ });
648
+ }
649
+ /**
650
+ * Sets wrapper metadata in memory
651
+ * @param wrapperSKU
652
+ * @param wrapperVersion
653
+ */
654
+ setWrapperMetadata(wrapperSKU, wrapperVersion) {
655
+ this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_SKU, wrapperSKU);
656
+ this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_VER, wrapperVersion);
657
+ }
658
+ /**
659
+ * Returns wrapper metadata from in-memory storage
660
+ */
661
+ getWrapperMetadata() {
662
+ const sku = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_SKU) ||
663
+ Constants.EMPTY_STRING;
664
+ const version = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_VER) ||
665
+ Constants.EMPTY_STRING;
666
+ return [sku, version];
667
+ }
668
+ /**
669
+ *
670
+ * @param entity
671
+ */
672
+ setAuthorityMetadata(key, entity) {
673
+ this.logger.trace("BrowserCacheManager.setAuthorityMetadata called");
674
+ this.internalStorage.setItem(key, JSON.stringify(entity));
675
+ }
676
+ /**
677
+ * Gets the active account
678
+ */
679
+ getActiveAccount() {
680
+ const activeAccountKeyFilters = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
681
+ const activeAccountValueFilters = this.getItem(activeAccountKeyFilters);
682
+ if (!activeAccountValueFilters) {
683
+ // if new active account cache type isn't found, it's an old version, so look for that instead
684
+ this.logger.trace("BrowserCacheManager.getActiveAccount: No active account filters cache schema found, looking for legacy schema");
685
+ const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
686
+ const activeAccountValueLocal = this.getItem(activeAccountKeyLocal);
687
+ if (!activeAccountValueLocal) {
688
+ this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
689
+ return null;
690
+ }
691
+ const activeAccount = this.getAccountInfoFilteredBy({
692
+ localAccountId: activeAccountValueLocal,
693
+ });
694
+ if (activeAccount) {
695
+ this.logger.trace("BrowserCacheManager.getActiveAccount: Legacy active account cache schema found");
696
+ this.logger.trace("BrowserCacheManager.getActiveAccount: Adding active account filters cache schema");
697
+ this.setActiveAccount(activeAccount);
698
+ return activeAccount;
699
+ }
700
+ return null;
701
+ }
702
+ const activeAccountValueObj = this.validateAndParseJson(activeAccountValueFilters);
703
+ if (activeAccountValueObj) {
704
+ this.logger.trace("BrowserCacheManager.getActiveAccount: Active account filters schema found");
705
+ return this.getAccountInfoFilteredBy({
706
+ homeAccountId: activeAccountValueObj.homeAccountId,
707
+ localAccountId: activeAccountValueObj.localAccountId,
708
+ tenantId: activeAccountValueObj.tenantId,
709
+ });
710
+ }
711
+ this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
712
+ return null;
713
+ }
714
+ /**
715
+ * Sets the active account's localAccountId in cache
716
+ * @param account
717
+ */
718
+ setActiveAccount(account) {
719
+ const activeAccountKey = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
720
+ const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
721
+ if (account) {
722
+ this.logger.verbose("setActiveAccount: Active account set");
723
+ const activeAccountValue = {
724
+ homeAccountId: account.homeAccountId,
725
+ localAccountId: account.localAccountId,
726
+ tenantId: account.tenantId,
727
+ };
728
+ this.browserStorage.setItem(activeAccountKey, JSON.stringify(activeAccountValue));
729
+ this.browserStorage.setItem(activeAccountKeyLocal, account.localAccountId);
730
+ }
731
+ else {
732
+ this.logger.verbose("setActiveAccount: No account passed, active account not set");
733
+ this.browserStorage.removeItem(activeAccountKey);
734
+ this.browserStorage.removeItem(activeAccountKeyLocal);
735
+ }
736
+ }
737
+ /**
738
+ * fetch throttling entity from the platform cache
739
+ * @param throttlingCacheKey
740
+ */
741
+ getThrottlingCache(throttlingCacheKey) {
742
+ const value = this.getItem(throttlingCacheKey);
743
+ if (!value) {
744
+ this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
745
+ return null;
746
+ }
747
+ const parsedThrottlingCache = this.validateAndParseJson(value);
748
+ if (!parsedThrottlingCache ||
749
+ !CacheHelpers.isThrottlingEntity(throttlingCacheKey, parsedThrottlingCache)) {
750
+ this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
751
+ return null;
752
+ }
753
+ this.logger.trace("BrowserCacheManager.getThrottlingCache: cache hit");
754
+ return parsedThrottlingCache;
755
+ }
756
+ /**
757
+ * set throttling entity to the platform cache
758
+ * @param throttlingCacheKey
759
+ * @param throttlingCache
760
+ */
761
+ setThrottlingCache(throttlingCacheKey, throttlingCache) {
762
+ this.logger.trace("BrowserCacheManager.setThrottlingCache called");
763
+ this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
764
+ }
765
+ /**
766
+ * Gets cache item with given key.
767
+ * Will retrieve from cookies if storeAuthStateInCookie is set to true.
768
+ * @param key
769
+ */
770
+ getTemporaryCache(cacheKey, generateKey) {
771
+ const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
772
+ if (this.cacheConfig.storeAuthStateInCookie) {
773
+ const itemCookie = this.getItemCookie(key);
774
+ if (itemCookie) {
775
+ this.logger.trace("BrowserCacheManager.getTemporaryCache: storeAuthStateInCookies set to true, retrieving from cookies");
776
+ return itemCookie;
777
+ }
778
+ }
779
+ const value = this.temporaryCacheStorage.getItem(key);
780
+ if (!value) {
781
+ // If temp cache item not found in session/memory, check local storage for items set by old versions
782
+ if (this.cacheConfig.cacheLocation ===
783
+ BrowserCacheLocation.LocalStorage) {
784
+ const item = this.browserStorage.getItem(key);
785
+ if (item) {
786
+ this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item found in local storage");
787
+ return item;
788
+ }
789
+ }
790
+ this.logger.trace("BrowserCacheManager.getTemporaryCache: No cache item found in local storage");
791
+ return null;
792
+ }
793
+ this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item returned");
794
+ return value;
795
+ }
796
+ /**
797
+ * Sets the cache item with the key and value given.
798
+ * Stores in cookie if storeAuthStateInCookie is set to true.
799
+ * This can cause cookie overflow if used incorrectly.
800
+ * @param key
801
+ * @param value
802
+ */
803
+ setTemporaryCache(cacheKey, value, generateKey) {
804
+ const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
805
+ this.temporaryCacheStorage.setItem(key, value);
806
+ if (this.cacheConfig.storeAuthStateInCookie) {
807
+ this.logger.trace("BrowserCacheManager.setTemporaryCache: storeAuthStateInCookie set to true, setting item cookie");
808
+ this.setItemCookie(key, value);
809
+ }
810
+ }
811
+ /**
812
+ * Removes the cache item with the given key.
813
+ * Will also clear the cookie item if storeAuthStateInCookie is set to true.
814
+ * @param key
815
+ */
816
+ removeItem(key) {
817
+ this.browserStorage.removeItem(key);
818
+ this.temporaryCacheStorage.removeItem(key);
819
+ if (this.cacheConfig.storeAuthStateInCookie) {
820
+ this.logger.trace("BrowserCacheManager.removeItem: storeAuthStateInCookie is true, clearing item cookie");
821
+ this.clearItemCookie(key);
822
+ }
823
+ }
824
+ /**
825
+ * Checks whether key is in cache.
826
+ * @param key
827
+ */
828
+ containsKey(key) {
829
+ return (this.browserStorage.containsKey(key) ||
830
+ this.temporaryCacheStorage.containsKey(key));
831
+ }
832
+ /**
833
+ * Gets all keys in window.
834
+ */
835
+ getKeys() {
836
+ return [
837
+ ...this.browserStorage.getKeys(),
838
+ ...this.temporaryCacheStorage.getKeys(),
839
+ ];
840
+ }
841
+ /**
842
+ * Clears all cache entries created by MSAL.
843
+ */
844
+ async clear() {
845
+ // Removes all accounts and their credentials
846
+ await this.removeAllAccounts();
847
+ this.removeAppMetadata();
848
+ // Removes all remaining MSAL cache items
849
+ this.getKeys().forEach((cacheKey) => {
850
+ // Check if key contains msal prefix; For now, we are clearing all the cache items created by MSAL.js
851
+ if ((this.browserStorage.containsKey(cacheKey) ||
852
+ this.temporaryCacheStorage.containsKey(cacheKey)) &&
853
+ (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
854
+ cacheKey.indexOf(this.clientId) !== -1)) {
855
+ this.removeItem(cacheKey);
856
+ }
857
+ });
858
+ this.internalStorage.clear();
859
+ }
860
+ /**
861
+ * Clears all access tokes that have claims prior to saving the current one
862
+ * @param performanceClient {IPerformanceClient}
863
+ * @returns
864
+ */
865
+ async clearTokensAndKeysWithClaims(performanceClient) {
866
+ performanceClient.addQueueMeasurement(PerformanceEvents.ClearTokensAndKeysWithClaims);
867
+ const tokenKeys = this.getTokenKeys();
868
+ const removedAccessTokens = [];
869
+ tokenKeys.accessToken.forEach((key) => {
870
+ // if the access token has claims in its key, remove the token key and the token
871
+ const credential = this.getAccessTokenCredential(key);
872
+ if (credential?.requestedClaimsHash &&
873
+ key.includes(credential.requestedClaimsHash.toLowerCase())) {
874
+ removedAccessTokens.push(this.removeAccessToken(key));
875
+ }
876
+ });
877
+ await Promise.all(removedAccessTokens);
878
+ // warn if any access tokens are removed
879
+ if (removedAccessTokens.length > 0) {
880
+ this.logger.warning(`${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`);
881
+ }
882
+ }
883
+ /**
884
+ * Add value to cookies
885
+ * @param cookieName
886
+ * @param cookieValue
887
+ * @param expires
888
+ */
889
+ setItemCookie(cookieName, cookieValue, expires) {
890
+ let cookieStr = `${encodeURIComponent(cookieName)}=${encodeURIComponent(cookieValue)};path=/;SameSite=Lax;`;
891
+ if (expires) {
892
+ const expireTime = this.getCookieExpirationTime(expires);
893
+ cookieStr += `expires=${expireTime};`;
894
+ }
895
+ if (this.cacheConfig.secureCookies) {
896
+ cookieStr += "Secure;";
897
+ }
898
+ document.cookie = cookieStr;
899
+ }
900
+ /**
901
+ * Get one item by key from cookies
902
+ * @param cookieName
903
+ */
904
+ getItemCookie(cookieName) {
905
+ const name = `${encodeURIComponent(cookieName)}=`;
906
+ const cookieList = document.cookie.split(";");
907
+ for (let i = 0; i < cookieList.length; i++) {
908
+ let cookie = cookieList[i];
909
+ while (cookie.charAt(0) === " ") {
910
+ cookie = cookie.substring(1);
911
+ }
912
+ if (cookie.indexOf(name) === 0) {
913
+ return decodeURIComponent(cookie.substring(name.length, cookie.length));
914
+ }
915
+ }
916
+ return Constants.EMPTY_STRING;
917
+ }
918
+ /**
919
+ * Clear all msal-related cookies currently set in the browser. Should only be used to clear temporary cache items.
920
+ */
921
+ clearMsalCookies() {
922
+ const cookiePrefix = `${Constants.CACHE_PREFIX}.${this.clientId}`;
923
+ const cookieList = document.cookie.split(";");
924
+ cookieList.forEach((cookie) => {
925
+ while (cookie.charAt(0) === " ") {
926
+ // eslint-disable-next-line no-param-reassign
927
+ cookie = cookie.substring(1);
928
+ }
929
+ if (cookie.indexOf(cookiePrefix) === 0) {
930
+ const cookieKey = cookie.split("=")[0];
931
+ this.clearItemCookie(cookieKey);
932
+ }
933
+ });
934
+ }
935
+ /**
936
+ * Clear an item in the cookies by key
937
+ * @param cookieName
938
+ */
939
+ clearItemCookie(cookieName) {
940
+ this.setItemCookie(cookieName, Constants.EMPTY_STRING, -1);
941
+ }
942
+ /**
943
+ * Get cookie expiration time
944
+ * @param cookieLifeDays
945
+ */
946
+ getCookieExpirationTime(cookieLifeDays) {
947
+ const today = new Date();
948
+ const expr = new Date(today.getTime() + cookieLifeDays * this.COOKIE_LIFE_MULTIPLIER);
949
+ return expr.toUTCString();
950
+ }
951
+ /**
952
+ * Gets the cache object referenced by the browser
953
+ */
954
+ getCache() {
955
+ return this.browserStorage;
956
+ }
957
+ /**
958
+ * interface compat, we cannot overwrite browser cache; Functionality is supported by individual entities in browser
959
+ */
960
+ setCache() {
961
+ // sets nothing
962
+ }
963
+ /**
964
+ * Prepend msal.<client-id> to each key; Skip for any JSON object as Key (defined schemas do not need the key appended: AccessToken Keys or the upcoming schema)
965
+ * @param key
966
+ * @param addInstanceId
967
+ */
968
+ generateCacheKey(key) {
969
+ const generatedKey = this.validateAndParseJson(key);
970
+ if (!generatedKey) {
971
+ if (StringUtils.startsWith(key, Constants.CACHE_PREFIX) ||
972
+ StringUtils.startsWith(key, PersistentCacheKeys.ADAL_ID_TOKEN)) {
973
+ return key;
974
+ }
975
+ return `${Constants.CACHE_PREFIX}.${this.clientId}.${key}`;
976
+ }
977
+ return JSON.stringify(key);
978
+ }
979
+ /**
980
+ * Create authorityKey to cache authority
981
+ * @param state
982
+ */
983
+ generateAuthorityKey(stateString) {
984
+ const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
985
+ return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
986
+ }
987
+ /**
988
+ * Create Nonce key to cache nonce
989
+ * @param state
990
+ */
991
+ generateNonceKey(stateString) {
992
+ const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
993
+ return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
994
+ }
995
+ /**
996
+ * Creates full cache key for the request state
997
+ * @param stateString State string for the request
998
+ */
999
+ generateStateKey(stateString) {
1000
+ // Use the library state id to key temp storage for uniqueness for multiple concurrent requests
1001
+ const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
1002
+ return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
1003
+ }
1004
+ /**
1005
+ * Gets the cached authority based on the cached state. Returns empty if no cached state found.
1006
+ */
1007
+ getCachedAuthority(cachedState) {
1008
+ const stateCacheKey = this.generateStateKey(cachedState);
1009
+ const state = this.getTemporaryCache(stateCacheKey);
1010
+ if (!state) {
1011
+ return null;
1012
+ }
1013
+ const authorityCacheKey = this.generateAuthorityKey(state);
1014
+ return this.getTemporaryCache(authorityCacheKey);
1015
+ }
1016
+ /**
1017
+ * Updates account, authority, and state in cache
1018
+ * @param serverAuthenticationRequest
1019
+ * @param account
1020
+ */
1021
+ updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
1022
+ this.logger.trace("BrowserCacheManager.updateCacheEntries called");
1023
+ // Cache the request state
1024
+ const stateCacheKey = this.generateStateKey(state);
1025
+ this.setTemporaryCache(stateCacheKey, state, false);
1026
+ // Cache the nonce
1027
+ const nonceCacheKey = this.generateNonceKey(state);
1028
+ this.setTemporaryCache(nonceCacheKey, nonce, false);
1029
+ // Cache authorityKey
1030
+ const authorityCacheKey = this.generateAuthorityKey(state);
1031
+ this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
1032
+ if (account) {
1033
+ const ccsCredential = {
1034
+ credential: account.homeAccountId,
1035
+ type: CcsCredentialType.HOME_ACCOUNT_ID,
1036
+ };
1037
+ this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
1038
+ }
1039
+ else if (loginHint) {
1040
+ const ccsCredential = {
1041
+ credential: loginHint,
1042
+ type: CcsCredentialType.UPN,
1043
+ };
1044
+ this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Reset all temporary cache items
1049
+ * @param state
1050
+ */
1051
+ resetRequestCache(state) {
1052
+ this.logger.trace("BrowserCacheManager.resetRequestCache called");
1053
+ // check state and remove associated cache items
1054
+ if (state) {
1055
+ this.getKeys().forEach((key) => {
1056
+ if (key.indexOf(state) !== -1) {
1057
+ this.removeItem(key);
1058
+ }
1059
+ });
1060
+ // delete generic interactive request parameters
1061
+ this.removeItem(this.generateStateKey(state));
1062
+ this.removeItem(this.generateNonceKey(state));
1063
+ this.removeItem(this.generateAuthorityKey(state));
1064
+ }
1065
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
1066
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
1067
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
1068
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
1069
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
1070
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
1071
+ this.setInteractionInProgress(false);
1072
+ }
1073
+ /**
1074
+ * Removes temporary cache for the provided state
1075
+ * @param stateString
1076
+ */
1077
+ cleanRequestByState(stateString) {
1078
+ this.logger.trace("BrowserCacheManager.cleanRequestByState called");
1079
+ // Interaction is completed - remove interaction status.
1080
+ if (stateString) {
1081
+ const stateKey = this.generateStateKey(stateString);
1082
+ const cachedState = this.temporaryCacheStorage.getItem(stateKey);
1083
+ this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
1084
+ this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
1085
+ }
1086
+ this.clearMsalCookies();
1087
+ }
1088
+ /**
1089
+ * Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
1090
+ * Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
1091
+ * @param interactionType
1092
+ */
1093
+ cleanRequestByInteractionType(interactionType) {
1094
+ this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
1095
+ // Loop through all keys to find state key
1096
+ this.getKeys().forEach((key) => {
1097
+ // If this key is not the state key, move on
1098
+ if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
1099
+ return;
1100
+ }
1101
+ // Retrieve state value, return if not a valid value
1102
+ const stateValue = this.temporaryCacheStorage.getItem(key);
1103
+ if (!stateValue) {
1104
+ return;
1105
+ }
1106
+ // Extract state and ensure it matches given InteractionType, then clean request cache
1107
+ const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
1108
+ if (parsedState &&
1109
+ parsedState.interactionType === interactionType) {
1110
+ this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
1111
+ this.resetRequestCache(stateValue);
1112
+ }
1113
+ });
1114
+ this.clearMsalCookies();
1115
+ this.setInteractionInProgress(false);
1116
+ }
1117
+ cacheCodeRequest(authCodeRequest) {
1118
+ this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
1119
+ const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
1120
+ this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
1121
+ }
1122
+ /**
1123
+ * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
1124
+ */
1125
+ getCachedRequest(state) {
1126
+ this.logger.trace("BrowserCacheManager.getCachedRequest called");
1127
+ // Get token request from cache and parse as TokenExchangeParameters.
1128
+ const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
1129
+ if (!encodedTokenRequest) {
1130
+ throw createBrowserAuthError(noTokenRequestCacheError);
1131
+ }
1132
+ let parsedRequest;
1133
+ try {
1134
+ parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
1135
+ }
1136
+ catch (e) {
1137
+ this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
1138
+ this.logger.error(`Parsing cached token request threw with error: ${e}`);
1139
+ throw createBrowserAuthError(unableToParseTokenRequestCacheError);
1140
+ }
1141
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
1142
+ // Get cached authority and use if no authority is cached with request.
1143
+ if (!parsedRequest.authority) {
1144
+ const authorityCacheKey = this.generateAuthorityKey(state);
1145
+ const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
1146
+ if (!cachedAuthority) {
1147
+ throw createBrowserAuthError(noCachedAuthorityError);
1148
+ }
1149
+ parsedRequest.authority = cachedAuthority;
1150
+ }
1151
+ return parsedRequest;
1152
+ }
1153
+ /**
1154
+ * Gets cached native request for redirect flows
1155
+ */
1156
+ getCachedNativeRequest() {
1157
+ this.logger.trace("BrowserCacheManager.getCachedNativeRequest called");
1158
+ const cachedRequest = this.getTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, true);
1159
+ if (!cachedRequest) {
1160
+ this.logger.trace("BrowserCacheManager.getCachedNativeRequest: No cached native request found");
1161
+ return null;
1162
+ }
1163
+ const parsedRequest = this.validateAndParseJson(cachedRequest);
1164
+ if (!parsedRequest) {
1165
+ this.logger.error("BrowserCacheManager.getCachedNativeRequest: Unable to parse native request");
1166
+ return null;
1167
+ }
1168
+ return parsedRequest;
1169
+ }
1170
+ isInteractionInProgress(matchClientId) {
1171
+ const clientId = this.getInteractionInProgress();
1172
+ if (matchClientId) {
1173
+ return clientId === this.clientId;
1174
+ }
1175
+ else {
1176
+ return !!clientId;
1177
+ }
1178
+ }
1179
+ getInteractionInProgress() {
1180
+ const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1181
+ return this.getTemporaryCache(key, false);
1182
+ }
1183
+ setInteractionInProgress(inProgress) {
1184
+ // Ensure we don't overwrite interaction in progress for a different clientId
1185
+ const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1186
+ if (inProgress) {
1187
+ if (this.getInteractionInProgress()) {
1188
+ throw createBrowserAuthError(interactionInProgress);
1189
+ }
1190
+ else {
1191
+ // No interaction is in progress
1192
+ this.setTemporaryCache(key, this.clientId, false);
1193
+ }
1194
+ }
1195
+ else if (!inProgress &&
1196
+ this.getInteractionInProgress() === this.clientId) {
1197
+ this.removeItem(key);
1198
+ }
1199
+ }
1200
+ /**
1201
+ * Returns username retrieved from ADAL or MSAL v1 idToken
1202
+ * @deprecated
1203
+ */
1204
+ getLegacyLoginHint() {
1205
+ // Only check for adal/msal token if no SSO params are being used
1206
+ const adalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ADAL_ID_TOKEN);
1207
+ if (adalIdTokenString) {
1208
+ this.browserStorage.removeItem(PersistentCacheKeys.ADAL_ID_TOKEN);
1209
+ this.logger.verbose("Cached ADAL id token retrieved.");
1210
+ }
1211
+ // Check for cached MSAL v1 id token
1212
+ const msalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ID_TOKEN, true);
1213
+ if (msalIdTokenString) {
1214
+ this.removeItem(this.generateCacheKey(PersistentCacheKeys.ID_TOKEN));
1215
+ this.logger.verbose("Cached MSAL.js v1 id token retrieved");
1216
+ }
1217
+ const cachedIdTokenString = msalIdTokenString || adalIdTokenString;
1218
+ if (cachedIdTokenString) {
1219
+ const idTokenClaims = AuthToken.extractTokenClaims(cachedIdTokenString, base64Decode);
1220
+ if (idTokenClaims.preferred_username) {
1221
+ this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 preferred_username as loginHint");
1222
+ return idTokenClaims.preferred_username;
1223
+ }
1224
+ else if (idTokenClaims.upn) {
1225
+ this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 upn as loginHint");
1226
+ return idTokenClaims.upn;
1227
+ }
1228
+ else {
1229
+ this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, however, no account hint claim found. Enable preferred_username or upn id token claim to get SSO.");
1230
+ }
1231
+ }
1232
+ return null;
1233
+ }
1234
+ /**
1235
+ * Updates a credential's cache key if the current cache key is outdated
1236
+ */
1237
+ updateCredentialCacheKey(currentCacheKey, credential) {
1238
+ const updatedCacheKey = CacheHelpers.generateCredentialKey(credential);
1239
+ if (currentCacheKey !== updatedCacheKey) {
1240
+ const cacheItem = this.getItem(currentCacheKey);
1241
+ if (cacheItem) {
1242
+ this.removeItem(currentCacheKey);
1243
+ this.setItem(updatedCacheKey, cacheItem);
1244
+ this.logger.verbose(`Updated an outdated ${credential.credentialType} cache key`);
1245
+ return updatedCacheKey;
1246
+ }
1247
+ else {
1248
+ this.logger.error(`Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`);
1249
+ }
1250
+ }
1251
+ return currentCacheKey;
1252
+ }
1253
+ /**
1254
+ * Returns application id as redirect context during AcquireTokenRedirect flow.
1255
+ */
1256
+ getRedirectRequestContext() {
1257
+ return this.getTemporaryCache(TemporaryCacheKeys.REDIRECT_CONTEXT, true);
1258
+ }
1259
+ /**
1260
+ * Sets application id as the redirect context during AcquireTokenRedirect flow.
1261
+ * @param value
1262
+ */
1263
+ setRedirectRequestContext(value) {
1264
+ this.setTemporaryCache(TemporaryCacheKeys.REDIRECT_CONTEXT, value, true);
1265
+ }
1266
+ /**
1267
+ * Builds credential entities from AuthenticationResult object and saves the resulting credentials to the cache
1268
+ * @param result
1269
+ * @param request
1270
+ */
1271
+ async hydrateCache(result, request) {
1272
+ const idTokenEntity = CacheHelpers.createIdTokenEntity(result.account?.homeAccountId, result.account?.environment, result.idToken, this.clientId, result.tenantId);
1273
+ let claimsHash;
1274
+ if (request.claims) {
1275
+ claimsHash = await this.cryptoImpl.hashString(request.claims);
1276
+ }
1277
+ const accessTokenEntity = CacheHelpers.createAccessTokenEntity(result.account?.homeAccountId, result.account.environment, result.accessToken, this.clientId, result.tenantId, result.scopes.join(" "), result.expiresOn?.getTime() || 0, result.extExpiresOn?.getTime() || 0, base64Decode, undefined, // refreshOn
1278
+ result.tokenType, undefined, // userAssertionHash
1279
+ request.sshKid, request.claims, claimsHash);
1280
+ const cacheRecord = new CacheRecord(undefined, idTokenEntity, accessTokenEntity);
1281
+ return this.saveCacheRecord(cacheRecord);
1282
+ }
1283
+ }
1284
+ const DEFAULT_BROWSER_CACHE_MANAGER = (clientId, logger) => {
1285
+ const cacheOptions = {
1286
+ cacheLocation: BrowserCacheLocation.MemoryStorage,
1287
+ temporaryCacheLocation: BrowserCacheLocation.MemoryStorage,
1288
+ storeAuthStateInCookie: false,
1289
+ secureCookies: false,
1290
+ cacheMigrationEnabled: false,
1291
+ claimsBasedCachingEnabled: false,
1292
+ };
1293
+ return new BrowserCacheManager(clientId, cacheOptions, DEFAULT_CRYPTO_IMPLEMENTATION, logger);
1274
1294
  };
1275
1295
 
1276
1296
  export { BrowserCacheManager, DEFAULT_BROWSER_CACHE_MANAGER };