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