@azure/msal-browser 3.0.0-alpha.1 → 3.0.0-beta.0

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