@azure/msal-browser 3.0.0-alpha.1 → 3.0.0-alpha.2

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