@azure/msal-browser 3.28.1 → 3.30.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 (127) hide show
  1. package/dist/app/IPublicClientApplication.mjs +1 -1
  2. package/dist/app/PublicClientApplication.mjs +1 -1
  3. package/dist/app/PublicClientNext.mjs +1 -1
  4. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +1 -1
  5. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +1 -1
  6. package/dist/cache/AccountManager.d.ts +7 -7
  7. package/dist/cache/AccountManager.d.ts.map +1 -1
  8. package/dist/cache/AccountManager.mjs +17 -15
  9. package/dist/cache/AccountManager.mjs.map +1 -1
  10. package/dist/cache/AsyncMemoryStorage.mjs +1 -1
  11. package/dist/cache/BrowserCacheManager.d.ts +35 -27
  12. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  13. package/dist/cache/BrowserCacheManager.mjs +187 -91
  14. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  15. package/dist/cache/BrowserStorage.mjs +1 -1
  16. package/dist/cache/CookieStorage.mjs +1 -1
  17. package/dist/cache/DatabaseStorage.mjs +1 -1
  18. package/dist/cache/LocalStorage.mjs +1 -1
  19. package/dist/cache/MemoryStorage.mjs +1 -1
  20. package/dist/cache/SessionStorage.mjs +1 -1
  21. package/dist/cache/TokenCache.d.ts.map +1 -1
  22. package/dist/cache/TokenCache.mjs +16 -15
  23. package/dist/cache/TokenCache.mjs.map +1 -1
  24. package/dist/config/Configuration.mjs +1 -1
  25. package/dist/controllers/ControllerFactory.mjs +1 -1
  26. package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
  27. package/dist/controllers/NestedAppAuthController.mjs +28 -18
  28. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  29. package/dist/controllers/StandardController.d.ts.map +1 -1
  30. package/dist/controllers/StandardController.mjs +16 -9
  31. package/dist/controllers/StandardController.mjs.map +1 -1
  32. package/dist/controllers/UnknownOperatingContextController.mjs +1 -1
  33. package/dist/crypto/BrowserCrypto.mjs +1 -1
  34. package/dist/crypto/CryptoOps.mjs +1 -1
  35. package/dist/crypto/PkceGenerator.mjs +1 -1
  36. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  37. package/dist/encode/Base64Decode.mjs +1 -1
  38. package/dist/encode/Base64Encode.mjs +1 -1
  39. package/dist/error/BrowserAuthError.mjs +1 -1
  40. package/dist/error/BrowserAuthErrorCodes.mjs +1 -1
  41. package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
  42. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
  43. package/dist/error/NativeAuthError.mjs +1 -1
  44. package/dist/error/NativeAuthErrorCodes.mjs +1 -1
  45. package/dist/error/NestedAppAuthError.mjs +1 -1
  46. package/dist/event/EventHandler.mjs +1 -1
  47. package/dist/event/EventMessage.mjs +1 -1
  48. package/dist/event/EventType.mjs +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/dist/interaction_client/BaseInteractionClient.d.ts.map +1 -1
  51. package/dist/interaction_client/BaseInteractionClient.mjs +5 -5
  52. package/dist/interaction_client/BaseInteractionClient.mjs.map +1 -1
  53. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  54. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  55. package/dist/interaction_client/NativeInteractionClient.mjs +9 -7
  56. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  57. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  58. package/dist/interaction_client/PopupClient.mjs +2 -2
  59. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  60. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  61. package/dist/interaction_client/RedirectClient.mjs +2 -2
  62. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  63. package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
  64. package/dist/interaction_client/SilentCacheClient.mjs +1 -1
  65. package/dist/interaction_client/SilentIframeClient.mjs +1 -1
  66. package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
  67. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  68. package/dist/interaction_client/StandardInteractionClient.mjs +3 -2
  69. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  70. package/dist/interaction_handler/InteractionHandler.mjs +1 -1
  71. package/dist/interaction_handler/RedirectHandler.mjs +1 -1
  72. package/dist/interaction_handler/SilentHandler.mjs +1 -1
  73. package/dist/naa/BridgeError.mjs +1 -1
  74. package/dist/naa/BridgeProxy.mjs +1 -1
  75. package/dist/naa/BridgeStatusCode.mjs +1 -1
  76. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +1 -1
  77. package/dist/navigation/NavigationClient.mjs +1 -1
  78. package/dist/network/FetchClient.mjs +1 -1
  79. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  80. package/dist/operatingcontext/NestedAppOperatingContext.mjs +1 -1
  81. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  82. package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
  83. package/dist/packageMetadata.d.ts +1 -1
  84. package/dist/packageMetadata.mjs +2 -2
  85. package/dist/request/RequestHelpers.mjs +1 -1
  86. package/dist/response/ResponseHandler.mjs +1 -1
  87. package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
  88. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  89. package/dist/utils/BrowserConstants.d.ts +1 -0
  90. package/dist/utils/BrowserConstants.d.ts.map +1 -1
  91. package/dist/utils/BrowserConstants.mjs +2 -1
  92. package/dist/utils/BrowserConstants.mjs.map +1 -1
  93. package/dist/utils/BrowserProtocolUtils.mjs +1 -1
  94. package/dist/utils/BrowserUtils.mjs +1 -1
  95. package/lib/msal-browser.cjs +464 -343
  96. package/lib/msal-browser.cjs.map +1 -1
  97. package/lib/msal-browser.js +464 -343
  98. package/lib/msal-browser.js.map +1 -1
  99. package/lib/msal-browser.min.js +65 -66
  100. package/lib/types/cache/AccountManager.d.ts +7 -7
  101. package/lib/types/cache/AccountManager.d.ts.map +1 -1
  102. package/lib/types/cache/BrowserCacheManager.d.ts +35 -27
  103. package/lib/types/cache/BrowserCacheManager.d.ts.map +1 -1
  104. package/lib/types/cache/TokenCache.d.ts.map +1 -1
  105. package/lib/types/controllers/NestedAppAuthController.d.ts.map +1 -1
  106. package/lib/types/controllers/StandardController.d.ts.map +1 -1
  107. package/lib/types/interaction_client/BaseInteractionClient.d.ts.map +1 -1
  108. package/lib/types/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  109. package/lib/types/interaction_client/PopupClient.d.ts.map +1 -1
  110. package/lib/types/interaction_client/RedirectClient.d.ts.map +1 -1
  111. package/lib/types/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  112. package/lib/types/packageMetadata.d.ts +1 -1
  113. package/lib/types/utils/BrowserConstants.d.ts +1 -0
  114. package/lib/types/utils/BrowserConstants.d.ts.map +1 -1
  115. package/package.json +3 -3
  116. package/src/cache/AccountManager.ts +40 -20
  117. package/src/cache/BrowserCacheManager.ts +332 -109
  118. package/src/cache/TokenCache.ts +25 -11
  119. package/src/controllers/NestedAppAuthController.ts +51 -18
  120. package/src/controllers/StandardController.ts +26 -7
  121. package/src/interaction_client/BaseInteractionClient.ts +5 -4
  122. package/src/interaction_client/NativeInteractionClient.ts +22 -12
  123. package/src/interaction_client/PopupClient.ts +2 -1
  124. package/src/interaction_client/RedirectClient.ts +2 -1
  125. package/src/interaction_client/StandardInteractionClient.ts +2 -1
  126. package/src/packageMetadata.ts +1 -1
  127. package/src/utils/BrowserConstants.ts +1 -0
@@ -39,6 +39,8 @@ import {
39
39
  CacheHelpers,
40
40
  StoreInCache,
41
41
  CacheError,
42
+ CacheErrorCodes,
43
+ createCacheError,
42
44
  } from "@azure/msal-common/browser";
43
45
  import { CacheOptions } from "../config/Configuration.js";
44
46
  import {
@@ -66,6 +68,7 @@ import { PopupRequest } from "../request/PopupRequest.js";
66
68
  import { base64Decode } from "../encode/Base64Decode.js";
67
69
  import { base64Encode } from "../encode/Base64Encode.js";
68
70
  import { CookieStorage } from "./CookieStorage.js";
71
+ import { version } from "../packageMetadata.js";
69
72
 
70
73
  /**
71
74
  * This class implements the cache storage interface for MSAL through browser local or session storage.
@@ -146,6 +149,19 @@ export class BrowserCacheManager extends CacheManager {
146
149
  * @param storeAuthStateInCookie
147
150
  */
148
151
  protected migrateCacheEntries(): void {
152
+ const previousVersion = this.browserStorage.getItem(
153
+ StaticCacheKeys.VERSION
154
+ );
155
+ if (previousVersion) {
156
+ this.logger.info(
157
+ `MSAL.js was last initialized with version ${previousVersion}`
158
+ );
159
+ }
160
+
161
+ if (previousVersion !== version) {
162
+ this.browserStorage.setItem(StaticCacheKeys.VERSION, version);
163
+ }
164
+
149
165
  const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
150
166
  const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
151
167
  const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
@@ -184,6 +200,7 @@ export class BrowserCacheManager extends CacheManager {
184
200
  */
185
201
  private createKeyMaps(): void {
186
202
  this.logger.trace("BrowserCacheManager - createKeyMaps called.");
203
+ const correlationId = this.cryptoImpl.createNewGuid();
187
204
  const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
188
205
  const tokenKeys = this.getItem(
189
206
  `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`
@@ -218,11 +235,13 @@ export class BrowserCacheManager extends CacheManager {
218
235
  const newKey =
219
236
  this.updateCredentialCacheKey(
220
237
  key,
221
- idTokenEntity
238
+ idTokenEntity,
239
+ correlationId
222
240
  );
223
241
  this.addTokenKey(
224
242
  newKey,
225
- CredentialType.ID_TOKEN
243
+ CredentialType.ID_TOKEN,
244
+ correlationId
226
245
  );
227
246
  return;
228
247
  } else {
@@ -248,11 +267,13 @@ export class BrowserCacheManager extends CacheManager {
248
267
  const newKey =
249
268
  this.updateCredentialCacheKey(
250
269
  key,
251
- accessTokenEntity
270
+ accessTokenEntity,
271
+ correlationId
252
272
  );
253
273
  this.addTokenKey(
254
274
  newKey,
255
- CredentialType.ACCESS_TOKEN
275
+ CredentialType.ACCESS_TOKEN,
276
+ correlationId
256
277
  );
257
278
  return;
258
279
  } else {
@@ -279,11 +300,13 @@ export class BrowserCacheManager extends CacheManager {
279
300
  const newKey =
280
301
  this.updateCredentialCacheKey(
281
302
  key,
282
- refreshTokenEntity
303
+ refreshTokenEntity,
304
+ correlationId
283
305
  );
284
306
  this.addTokenKey(
285
307
  newKey,
286
- CredentialType.REFRESH_TOKEN
308
+ CredentialType.REFRESH_TOKEN,
309
+ correlationId
287
310
  );
288
311
  return;
289
312
  } else {
@@ -316,7 +339,7 @@ export class BrowserCacheManager extends CacheManager {
316
339
  this.logger.tracePii(
317
340
  `BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`
318
341
  );
319
- this.addAccountKeyToMap(key);
342
+ this.addAccountKeyToMap(key, correlationId);
320
343
  }
321
344
  }
322
345
  }
@@ -357,21 +380,77 @@ export class BrowserCacheManager extends CacheManager {
357
380
  * @param key
358
381
  * @param value
359
382
  */
360
- setItem(key: string, value: string): void {
361
- this.browserStorage.setItem(key, value);
383
+ setItem(key: string, value: string, correlationId: string): void {
384
+ let accessTokenKeys: Array<string> = [];
385
+ const maxRetries = 20;
386
+ for (let i = 0; i <= maxRetries; i++) {
387
+ try {
388
+ this.browserStorage.setItem(key, value);
389
+ if (i > 0) {
390
+ // Finally update the token keys array with the tokens removed
391
+ this.removeAccessTokenKeys(
392
+ accessTokenKeys.slice(0, i),
393
+ correlationId
394
+ );
395
+ }
396
+ break; // If setItem succeeds, exit the loop
397
+ } catch (e) {
398
+ const cacheError = createCacheError(e);
399
+ if (
400
+ cacheError.errorCode ===
401
+ CacheErrorCodes.cacheQuotaExceeded &&
402
+ i < maxRetries
403
+ ) {
404
+ if (!accessTokenKeys.length) {
405
+ if (
406
+ key ===
407
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`
408
+ ) {
409
+ // If we are currently trying to set the token keys, use the value we're trying to set
410
+ accessTokenKeys = (JSON.parse(value) as TokenKeys)
411
+ .accessToken;
412
+ } else {
413
+ // If token keys have not been initialized, get them
414
+ accessTokenKeys = this.getTokenKeys().accessToken;
415
+ }
416
+ }
417
+ if (accessTokenKeys.length <= i) {
418
+ // Nothing to remove, rethrow the error
419
+ throw cacheError;
420
+ }
421
+ // When cache quota is exceeded, start removing access tokens until we can successfully set the item
422
+ this.removeAccessToken(
423
+ accessTokenKeys[i],
424
+ correlationId,
425
+ false // Don't save token keys yet, do it at the end
426
+ );
427
+ } else {
428
+ // If the error is not a quota exceeded error, rethrow it
429
+ throw cacheError;
430
+ }
431
+ }
432
+ }
362
433
  }
363
434
 
364
435
  /**
365
436
  * fetch the account entity from the platform cache
366
437
  * @param accountKey
367
438
  */
368
- getAccount(accountKey: string, logger?: Logger): AccountEntity | null {
439
+ getAccount(
440
+ accountKey: string,
441
+ correlationId: string,
442
+ logger?: Logger
443
+ ): AccountEntity | null {
369
444
  this.logger.trace("BrowserCacheManager.getAccount called");
370
- const accountEntity = this.getCachedAccountEntity(accountKey);
445
+ const accountEntity = this.getCachedAccountEntity(
446
+ accountKey,
447
+ correlationId
448
+ );
371
449
 
372
450
  return this.updateOutdatedCachedAccount(
373
451
  accountKey,
374
452
  accountEntity,
453
+ correlationId,
375
454
  logger
376
455
  );
377
456
  }
@@ -382,16 +461,18 @@ export class BrowserCacheManager extends CacheManager {
382
461
  * @param accountKey
383
462
  * @returns
384
463
  */
385
- getCachedAccountEntity(accountKey: string): AccountEntity | null {
464
+ getCachedAccountEntity(
465
+ accountKey: string,
466
+ correlationId: string
467
+ ): AccountEntity | null {
386
468
  const serializedAccount = this.getItem(accountKey);
387
469
  if (!serializedAccount) {
388
- this.removeAccountKeyFromMap(accountKey);
470
+ this.removeAccountKeyFromMap(accountKey, correlationId);
389
471
  return null;
390
472
  }
391
473
 
392
474
  const parsedAccount = this.validateAndParseJson(serializedAccount);
393
475
  if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
394
- this.removeAccountKeyFromMap(accountKey);
395
476
  return null;
396
477
  }
397
478
 
@@ -405,11 +486,12 @@ export class BrowserCacheManager extends CacheManager {
405
486
  * set account entity in the platform cache
406
487
  * @param account
407
488
  */
408
- setAccount(account: AccountEntity): void {
489
+ setAccount(account: AccountEntity, correlationId: string): void {
409
490
  this.logger.trace("BrowserCacheManager.setAccount called");
410
491
  const key = account.generateAccountKey();
411
- this.setItem(key, JSON.stringify(account));
412
- this.addAccountKeyToMap(key);
492
+ account.lastUpdatedAt = Date.now().toString();
493
+ this.setItem(key, JSON.stringify(account), correlationId);
494
+ this.addAccountKeyToMap(key, correlationId);
413
495
  }
414
496
 
415
497
  /**
@@ -433,7 +515,7 @@ export class BrowserCacheManager extends CacheManager {
433
515
  * Add a new account to the key map
434
516
  * @param key
435
517
  */
436
- addAccountKeyToMap(key: string): void {
518
+ addAccountKeyToMap(key: string, correlationId: string): void {
437
519
  this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
438
520
  this.logger.tracePii(
439
521
  `BrowserCacheManager.addAccountKeyToMap called with key: ${key}`
@@ -444,7 +526,8 @@ export class BrowserCacheManager extends CacheManager {
444
526
  accountKeys.push(key);
445
527
  this.setItem(
446
528
  StaticCacheKeys.ACCOUNT_KEYS,
447
- JSON.stringify(accountKeys)
529
+ JSON.stringify(accountKeys),
530
+ correlationId
448
531
  );
449
532
  this.logger.verbose(
450
533
  "BrowserCacheManager.addAccountKeyToMap account key added"
@@ -460,7 +543,7 @@ export class BrowserCacheManager extends CacheManager {
460
543
  * Remove an account from the key map
461
544
  * @param key
462
545
  */
463
- removeAccountKeyFromMap(key: string): void {
546
+ removeAccountKeyFromMap(key: string, correlationId: string): void {
464
547
  this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
465
548
  this.logger.tracePii(
466
549
  `BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`
@@ -469,10 +552,17 @@ export class BrowserCacheManager extends CacheManager {
469
552
  const removalIndex = accountKeys.indexOf(key);
470
553
  if (removalIndex > -1) {
471
554
  accountKeys.splice(removalIndex, 1);
472
- this.setItem(
473
- StaticCacheKeys.ACCOUNT_KEYS,
474
- JSON.stringify(accountKeys)
475
- );
555
+ if (accountKeys.length === 0) {
556
+ // If no keys left, remove the map
557
+ this.removeItem(StaticCacheKeys.ACCOUNT_KEYS);
558
+ return;
559
+ } else {
560
+ this.setItem(
561
+ StaticCacheKeys.ACCOUNT_KEYS,
562
+ JSON.stringify(accountKeys),
563
+ correlationId
564
+ );
565
+ }
476
566
  this.logger.trace(
477
567
  "BrowserCacheManager.removeAccountKeyFromMap account key removed"
478
568
  );
@@ -487,45 +577,79 @@ export class BrowserCacheManager extends CacheManager {
487
577
  * Extends inherited removeAccount function to include removal of the account key from the map
488
578
  * @param key
489
579
  */
490
- async removeAccount(key: string): Promise<void> {
491
- void super.removeAccount(key);
492
- this.removeAccountKeyFromMap(key);
580
+ async removeAccount(key: string, correlationId: string): Promise<void> {
581
+ void super.removeAccount(key, correlationId);
582
+ this.removeAccountKeyFromMap(key, correlationId);
493
583
  }
494
584
 
495
585
  /**
496
586
  * Remove account entity from the platform cache if it's outdated
497
587
  * @param accountKey
498
588
  */
499
- removeOutdatedAccount(accountKey: string): void {
589
+ removeOutdatedAccount(accountKey: string, correlationId: string): void {
500
590
  this.removeItem(accountKey);
501
- this.removeAccountKeyFromMap(accountKey);
591
+ this.removeAccountKeyFromMap(accountKey, correlationId);
502
592
  }
503
593
 
504
594
  /**
505
595
  * Removes given idToken from the cache and from the key map
506
596
  * @param key
507
597
  */
508
- removeIdToken(key: string): void {
509
- super.removeIdToken(key);
510
- this.removeTokenKey(key, CredentialType.ID_TOKEN);
598
+ removeIdToken(key: string, correlationId: string): void {
599
+ super.removeIdToken(key, correlationId);
600
+ this.removeTokenKey(key, CredentialType.ID_TOKEN, correlationId);
511
601
  }
512
602
 
513
603
  /**
514
604
  * Removes given accessToken from the cache and from the key map
515
605
  * @param key
516
606
  */
517
- async removeAccessToken(key: string): Promise<void> {
518
- void super.removeAccessToken(key);
519
- this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
607
+ removeAccessToken(
608
+ key: string,
609
+ correlationId: string,
610
+ updateTokenKeys: boolean = true
611
+ ): void {
612
+ super.removeAccessToken(key, correlationId);
613
+ this.performanceClient?.incrementFields(
614
+ { accessTokensRemoved: 1 },
615
+ correlationId
616
+ );
617
+ updateTokenKeys &&
618
+ this.removeTokenKey(
619
+ key,
620
+ CredentialType.ACCESS_TOKEN,
621
+ correlationId
622
+ );
623
+ }
624
+
625
+ removeAccessTokenKeys(keys: Array<string>, correlationId: string): void {
626
+ this.logger.trace("removeAccessTokenKey called");
627
+ const tokenKeys = this.getTokenKeys();
628
+ let keysRemoved = 0;
629
+ keys.forEach((key) => {
630
+ const accessRemoval = tokenKeys.accessToken.indexOf(key);
631
+ if (accessRemoval > -1) {
632
+ tokenKeys.accessToken.splice(accessRemoval, 1);
633
+ keysRemoved++;
634
+ }
635
+ });
636
+
637
+ if (keysRemoved > 0) {
638
+ this.logger.info(
639
+ `removed ${keysRemoved} accessToken keys from tokenKeys map`
640
+ );
641
+ this.setTokenKeys(tokenKeys, correlationId);
642
+ return;
643
+ }
520
644
  }
521
645
 
522
646
  /**
523
647
  * Removes given refreshToken from the cache and from the key map
524
648
  * @param key
525
649
  */
526
- removeRefreshToken(key: string): void {
527
- super.removeRefreshToken(key);
528
- this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
650
+ removeRefreshToken(key: string, correlationId: string): void {
651
+ super.removeRefreshToken(key, correlationId);
652
+ this.removeTokenKey(key, CredentialType.REFRESH_TOKEN, correlationId);
529
653
  }
530
654
 
531
655
  /**
@@ -564,12 +688,40 @@ export class BrowserCacheManager extends CacheManager {
564
688
  };
565
689
  }
566
690
 
691
+ /**
692
+ * Stores the token keys in the cache
693
+ * @param tokenKeys
694
+ * @param correlationId
695
+ * @returns
696
+ */
697
+ setTokenKeys(tokenKeys: TokenKeys, correlationId: string): void {
698
+ if (
699
+ tokenKeys.idToken.length === 0 &&
700
+ tokenKeys.accessToken.length === 0 &&
701
+ tokenKeys.refreshToken.length === 0
702
+ ) {
703
+ // If no keys left, remove the map
704
+ this.removeItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
705
+ return;
706
+ } else {
707
+ this.setItem(
708
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`,
709
+ JSON.stringify(tokenKeys),
710
+ correlationId
711
+ );
712
+ }
713
+ }
714
+
567
715
  /**
568
716
  * Adds the given key to the token key map
569
717
  * @param key
570
718
  * @param type
571
719
  */
572
- addTokenKey(key: string, type: CredentialType): void {
720
+ addTokenKey(
721
+ key: string,
722
+ type: CredentialType,
723
+ correlationId: string
724
+ ): void {
573
725
  this.logger.trace("BrowserCacheManager addTokenKey called");
574
726
  const tokenKeys = this.getTokenKeys();
575
727
 
@@ -583,12 +735,16 @@ export class BrowserCacheManager extends CacheManager {
583
735
  }
584
736
  break;
585
737
  case CredentialType.ACCESS_TOKEN:
586
- if (tokenKeys.accessToken.indexOf(key) === -1) {
587
- this.logger.info(
588
- "BrowserCacheManager: addTokenKey - accessToken added to map"
589
- );
590
- tokenKeys.accessToken.push(key);
738
+ const index = tokenKeys.accessToken.indexOf(key);
739
+ if (index !== -1) {
740
+ tokenKeys.accessToken.splice(index, 1); // Remove existing key before pushing to the end
591
741
  }
742
+ this.logger.trace(
743
+ `access token ${
744
+ index === -1 ? "added to" : "updated in"
745
+ } map`
746
+ );
747
+ tokenKeys.accessToken.push(key);
592
748
  break;
593
749
  case CredentialType.REFRESH_TOKEN:
594
750
  if (tokenKeys.refreshToken.indexOf(key) === -1) {
@@ -607,10 +763,7 @@ export class BrowserCacheManager extends CacheManager {
607
763
  );
608
764
  }
609
765
 
610
- this.setItem(
611
- `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`,
612
- JSON.stringify(tokenKeys)
613
- );
766
+ this.setTokenKeys(tokenKeys, correlationId);
614
767
  }
615
768
 
616
769
  /**
@@ -618,9 +771,13 @@ export class BrowserCacheManager extends CacheManager {
618
771
  * @param key
619
772
  * @param type
620
773
  */
621
- removeTokenKey(key: string, type: CredentialType): void {
774
+ removeTokenKey(
775
+ key: string,
776
+ type: CredentialType,
777
+ correlationId: string,
778
+ tokenKeys: TokenKeys = this.getTokenKeys()
779
+ ): void {
622
780
  this.logger.trace("BrowserCacheManager removeTokenKey called");
623
- const tokenKeys = this.getTokenKeys();
624
781
 
625
782
  switch (type) {
626
783
  case CredentialType.ID_TOKEN:
@@ -680,23 +837,23 @@ export class BrowserCacheManager extends CacheManager {
680
837
  );
681
838
  }
682
839
 
683
- this.setItem(
684
- `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`,
685
- JSON.stringify(tokenKeys)
686
- );
840
+ this.setTokenKeys(tokenKeys, correlationId);
687
841
  }
688
842
 
689
843
  /**
690
844
  * generates idToken entity from a string
691
845
  * @param idTokenKey
692
846
  */
693
- getIdTokenCredential(idTokenKey: string): IdTokenEntity | null {
847
+ getIdTokenCredential(
848
+ idTokenKey: string,
849
+ correlationId: string
850
+ ): IdTokenEntity | null {
694
851
  const value = this.getItem(idTokenKey);
695
852
  if (!value) {
696
853
  this.logger.trace(
697
854
  "BrowserCacheManager.getIdTokenCredential: called, no cache hit"
698
855
  );
699
- this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
856
+ this.removeIdToken(idTokenKey, correlationId);
700
857
  return null;
701
858
  }
702
859
 
@@ -705,7 +862,6 @@ export class BrowserCacheManager extends CacheManager {
705
862
  this.logger.trace(
706
863
  "BrowserCacheManager.getIdTokenCredential: called, no cache hit"
707
864
  );
708
- this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
709
865
  return null;
710
866
  }
711
867
 
@@ -719,26 +875,34 @@ export class BrowserCacheManager extends CacheManager {
719
875
  * set IdToken credential to the platform cache
720
876
  * @param idToken
721
877
  */
722
- setIdTokenCredential(idToken: IdTokenEntity): void {
878
+ setIdTokenCredential(idToken: IdTokenEntity, correlationId: string): void {
723
879
  this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
724
880
  const idTokenKey = CacheHelpers.generateCredentialKey(idToken);
881
+ idToken.lastUpdatedAt = Date.now().toString();
725
882
 
726
- this.setItem(idTokenKey, JSON.stringify(idToken));
883
+ this.setItem(idTokenKey, JSON.stringify(idToken), correlationId);
727
884
 
728
- this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
885
+ this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN, correlationId);
729
886
  }
730
887
 
731
888
  /**
732
889
  * generates accessToken entity from a string
733
890
  * @param key
734
891
  */
735
- getAccessTokenCredential(accessTokenKey: string): AccessTokenEntity | null {
892
+ getAccessTokenCredential(
893
+ accessTokenKey: string,
894
+ correlationId: string
895
+ ): AccessTokenEntity | null {
736
896
  const value = this.getItem(accessTokenKey);
737
897
  if (!value) {
738
898
  this.logger.trace(
739
899
  "BrowserCacheManager.getAccessTokenCredential: called, no cache hit"
740
900
  );
741
- this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
901
+ this.removeTokenKey(
902
+ accessTokenKey,
903
+ CredentialType.ACCESS_TOKEN,
904
+ correlationId
905
+ );
742
906
  return null;
743
907
  }
744
908
  const parsedAccessToken = this.validateAndParseJson(value);
@@ -749,7 +913,6 @@ export class BrowserCacheManager extends CacheManager {
749
913
  this.logger.trace(
750
914
  "BrowserCacheManager.getAccessTokenCredential: called, no cache hit"
751
915
  );
752
- this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
753
916
  return null;
754
917
  }
755
918
 
@@ -763,14 +926,27 @@ export class BrowserCacheManager extends CacheManager {
763
926
  * set accessToken credential to the platform cache
764
927
  * @param accessToken
765
928
  */
766
- setAccessTokenCredential(accessToken: AccessTokenEntity): void {
929
+ setAccessTokenCredential(
930
+ accessToken: AccessTokenEntity,
931
+ correlationId: string
932
+ ): void {
767
933
  this.logger.trace(
768
934
  "BrowserCacheManager.setAccessTokenCredential called"
769
935
  );
770
936
  const accessTokenKey = CacheHelpers.generateCredentialKey(accessToken);
771
- this.setItem(accessTokenKey, JSON.stringify(accessToken));
937
+ accessToken.lastUpdatedAt = Date.now().toString();
772
938
 
773
- this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
939
+ this.setItem(
940
+ accessTokenKey,
941
+ JSON.stringify(accessToken),
942
+ correlationId
943
+ );
944
+
945
+ this.addTokenKey(
946
+ accessTokenKey,
947
+ CredentialType.ACCESS_TOKEN,
948
+ correlationId
949
+ );
774
950
  }
775
951
 
776
952
  /**
@@ -778,14 +954,19 @@ export class BrowserCacheManager extends CacheManager {
778
954
  * @param refreshTokenKey
779
955
  */
780
956
  getRefreshTokenCredential(
781
- refreshTokenKey: string
957
+ refreshTokenKey: string,
958
+ correlationId: string
782
959
  ): RefreshTokenEntity | null {
783
960
  const value = this.getItem(refreshTokenKey);
784
961
  if (!value) {
785
962
  this.logger.trace(
786
963
  "BrowserCacheManager.getRefreshTokenCredential: called, no cache hit"
787
964
  );
788
- this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
965
+ this.removeTokenKey(
966
+ refreshTokenKey,
967
+ CredentialType.REFRESH_TOKEN,
968
+ correlationId
969
+ );
789
970
  return null;
790
971
  }
791
972
  const parsedRefreshToken = this.validateAndParseJson(value);
@@ -796,7 +977,6 @@ export class BrowserCacheManager extends CacheManager {
796
977
  this.logger.trace(
797
978
  "BrowserCacheManager.getRefreshTokenCredential: called, no cache hit"
798
979
  );
799
- this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
800
980
  return null;
801
981
  }
802
982
 
@@ -810,15 +990,28 @@ export class BrowserCacheManager extends CacheManager {
810
990
  * set refreshToken credential to the platform cache
811
991
  * @param refreshToken
812
992
  */
813
- setRefreshTokenCredential(refreshToken: RefreshTokenEntity): void {
993
+ setRefreshTokenCredential(
994
+ refreshToken: RefreshTokenEntity,
995
+ correlationId: string
996
+ ): void {
814
997
  this.logger.trace(
815
998
  "BrowserCacheManager.setRefreshTokenCredential called"
816
999
  );
817
1000
  const refreshTokenKey =
818
1001
  CacheHelpers.generateCredentialKey(refreshToken);
819
- this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
820
1002
 
821
- this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
1003
+ refreshToken.lastUpdatedAt = Date.now().toString();
1004
+ this.setItem(
1005
+ refreshTokenKey,
1006
+ JSON.stringify(refreshToken),
1007
+ correlationId
1008
+ );
1009
+
1010
+ this.addTokenKey(
1011
+ refreshTokenKey,
1012
+ CredentialType.REFRESH_TOKEN,
1013
+ correlationId
1014
+ );
822
1015
  }
823
1016
 
824
1017
  /**
@@ -853,10 +1046,17 @@ export class BrowserCacheManager extends CacheManager {
853
1046
  * set appMetadata entity to the platform cache
854
1047
  * @param appMetadata
855
1048
  */
856
- setAppMetadata(appMetadata: AppMetadataEntity): void {
1049
+ setAppMetadata(
1050
+ appMetadata: AppMetadataEntity,
1051
+ correlationId: string
1052
+ ): void {
857
1053
  this.logger.trace("BrowserCacheManager.setAppMetadata called");
858
1054
  const appMetadataKey = CacheHelpers.generateAppMetadataKey(appMetadata);
859
- this.setItem(appMetadataKey, JSON.stringify(appMetadata));
1055
+ this.setItem(
1056
+ appMetadataKey,
1057
+ JSON.stringify(appMetadata),
1058
+ correlationId
1059
+ );
860
1060
  }
861
1061
 
862
1062
  /**
@@ -898,10 +1098,15 @@ export class BrowserCacheManager extends CacheManager {
898
1098
  */
899
1099
  setServerTelemetry(
900
1100
  serverTelemetryKey: string,
901
- serverTelemetry: ServerTelemetryEntity
1101
+ serverTelemetry: ServerTelemetryEntity,
1102
+ correlationId: string
902
1103
  ): void {
903
1104
  this.logger.trace("BrowserCacheManager.setServerTelemetry called");
904
- this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
1105
+ this.setItem(
1106
+ serverTelemetryKey,
1107
+ JSON.stringify(serverTelemetry),
1108
+ correlationId
1109
+ );
905
1110
  }
906
1111
 
907
1112
  /**
@@ -976,7 +1181,7 @@ export class BrowserCacheManager extends CacheManager {
976
1181
  /**
977
1182
  * Gets the active account
978
1183
  */
979
- getActiveAccount(): AccountInfo | null {
1184
+ getActiveAccount(correlationId: string): AccountInfo | null {
980
1185
  const activeAccountKeyFilters = this.generateCacheKey(
981
1186
  PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS
982
1187
  );
@@ -996,9 +1201,12 @@ export class BrowserCacheManager extends CacheManager {
996
1201
  );
997
1202
  return null;
998
1203
  }
999
- const activeAccount = this.getAccountInfoFilteredBy({
1000
- localAccountId: activeAccountValueLocal,
1001
- });
1204
+ const activeAccount = this.getAccountInfoFilteredBy(
1205
+ {
1206
+ localAccountId: activeAccountValueLocal,
1207
+ },
1208
+ correlationId
1209
+ );
1002
1210
  if (activeAccount) {
1003
1211
  this.logger.trace(
1004
1212
  "BrowserCacheManager.getActiveAccount: Legacy active account cache schema found"
@@ -1006,7 +1214,7 @@ export class BrowserCacheManager extends CacheManager {
1006
1214
  this.logger.trace(
1007
1215
  "BrowserCacheManager.getActiveAccount: Adding active account filters cache schema"
1008
1216
  );
1009
- this.setActiveAccount(activeAccount);
1217
+ this.setActiveAccount(activeAccount, correlationId);
1010
1218
  return activeAccount;
1011
1219
  }
1012
1220
  return null;
@@ -1018,11 +1226,14 @@ export class BrowserCacheManager extends CacheManager {
1018
1226
  this.logger.trace(
1019
1227
  "BrowserCacheManager.getActiveAccount: Active account filters schema found"
1020
1228
  );
1021
- return this.getAccountInfoFilteredBy({
1022
- homeAccountId: activeAccountValueObj.homeAccountId,
1023
- localAccountId: activeAccountValueObj.localAccountId,
1024
- tenantId: activeAccountValueObj.tenantId,
1025
- });
1229
+ return this.getAccountInfoFilteredBy(
1230
+ {
1231
+ homeAccountId: activeAccountValueObj.homeAccountId,
1232
+ localAccountId: activeAccountValueObj.localAccountId,
1233
+ tenantId: activeAccountValueObj.tenantId,
1234
+ },
1235
+ correlationId
1236
+ );
1026
1237
  }
1027
1238
  this.logger.trace(
1028
1239
  "BrowserCacheManager.getActiveAccount: No active account found"
@@ -1034,7 +1245,7 @@ export class BrowserCacheManager extends CacheManager {
1034
1245
  * Sets the active account's localAccountId in cache
1035
1246
  * @param account
1036
1247
  */
1037
- setActiveAccount(account: AccountInfo | null): void {
1248
+ setActiveAccount(account: AccountInfo | null, correlationId: string): void {
1038
1249
  const activeAccountKey = this.generateCacheKey(
1039
1250
  PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS
1040
1251
  );
@@ -1047,14 +1258,17 @@ export class BrowserCacheManager extends CacheManager {
1047
1258
  homeAccountId: account.homeAccountId,
1048
1259
  localAccountId: account.localAccountId,
1049
1260
  tenantId: account.tenantId,
1261
+ lastUpdatedAt: Date.now().toString(),
1050
1262
  };
1051
- this.browserStorage.setItem(
1263
+ this.setItem(
1052
1264
  activeAccountKey,
1053
- JSON.stringify(activeAccountValue)
1265
+ JSON.stringify(activeAccountValue),
1266
+ correlationId
1054
1267
  );
1055
- this.browserStorage.setItem(
1268
+ this.setItem(
1056
1269
  activeAccountKeyLocal,
1057
- account.localAccountId
1270
+ account.localAccountId,
1271
+ correlationId
1058
1272
  );
1059
1273
  } else {
1060
1274
  this.logger.verbose(
@@ -1103,10 +1317,15 @@ export class BrowserCacheManager extends CacheManager {
1103
1317
  */
1104
1318
  setThrottlingCache(
1105
1319
  throttlingCacheKey: string,
1106
- throttlingCache: ThrottlingEntity
1320
+ throttlingCache: ThrottlingEntity,
1321
+ correlationId: string
1107
1322
  ): void {
1108
1323
  this.logger.trace("BrowserCacheManager.setThrottlingCache called");
1109
- this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
1324
+ this.setItem(
1325
+ throttlingCacheKey,
1326
+ JSON.stringify(throttlingCache),
1327
+ correlationId
1328
+ );
1110
1329
  }
1111
1330
 
1112
1331
  /**
@@ -1213,10 +1432,10 @@ export class BrowserCacheManager extends CacheManager {
1213
1432
  /**
1214
1433
  * Clears all cache entries created by MSAL.
1215
1434
  */
1216
- async clear(): Promise<void> {
1435
+ async clear(correlationId: string): Promise<void> {
1217
1436
  // Removes all accounts and their credentials
1218
- await this.removeAllAccounts();
1219
- this.removeAppMetadata();
1437
+ await this.removeAllAccounts(correlationId);
1438
+ this.removeAppMetadata(correlationId);
1220
1439
 
1221
1440
  // Remove temp storage first to make sure any cookies are cleared
1222
1441
  this.temporaryCacheStorage.getKeys().forEach((cacheKey: string) => {
@@ -1258,23 +1477,26 @@ export class BrowserCacheManager extends CacheManager {
1258
1477
 
1259
1478
  const tokenKeys = this.getTokenKeys();
1260
1479
 
1261
- const removedAccessTokens: Array<Promise<void>> = [];
1480
+ let removedAccessTokens: number = 0;
1262
1481
  tokenKeys.accessToken.forEach((key: string) => {
1263
1482
  // if the access token has claims in its key, remove the token key and the token
1264
- const credential = this.getAccessTokenCredential(key);
1483
+ const credential = this.getAccessTokenCredential(
1484
+ key,
1485
+ correlationId
1486
+ );
1265
1487
  if (
1266
1488
  credential?.requestedClaimsHash &&
1267
1489
  key.includes(credential.requestedClaimsHash.toLowerCase())
1268
1490
  ) {
1269
- removedAccessTokens.push(this.removeAccessToken(key));
1491
+ this.removeAccessToken(key, correlationId);
1492
+ removedAccessTokens++;
1270
1493
  }
1271
1494
  });
1272
- await Promise.all(removedAccessTokens);
1273
1495
 
1274
1496
  // warn if any access tokens are removed
1275
- if (removedAccessTokens.length > 0) {
1497
+ if (removedAccessTokens > 0) {
1276
1498
  this.logger.warning(
1277
- `${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`
1499
+ `${removedAccessTokens} access tokens with claims in the cache keys have been removed from the cache.`
1278
1500
  );
1279
1501
  }
1280
1502
  }
@@ -1678,7 +1900,8 @@ export class BrowserCacheManager extends CacheManager {
1678
1900
  */
1679
1901
  updateCredentialCacheKey(
1680
1902
  currentCacheKey: string,
1681
- credential: ValidCredentialType
1903
+ credential: ValidCredentialType,
1904
+ correlationId: string
1682
1905
  ): string {
1683
1906
  const updatedCacheKey = CacheHelpers.generateCredentialKey(credential);
1684
1907
 
@@ -1686,7 +1909,7 @@ export class BrowserCacheManager extends CacheManager {
1686
1909
  const cacheItem = this.getItem(currentCacheKey);
1687
1910
  if (cacheItem) {
1688
1911
  this.browserStorage.removeItem(currentCacheKey);
1689
- this.setItem(updatedCacheKey, cacheItem);
1912
+ this.setItem(updatedCacheKey, cacheItem, correlationId);
1690
1913
  this.logger.verbose(
1691
1914
  `Updated an outdated ${credential.credentialType} cache key`
1692
1915
  );
@@ -1757,7 +1980,7 @@ export class BrowserCacheManager extends CacheManager {
1757
1980
  idToken: idTokenEntity,
1758
1981
  accessToken: accessTokenEntity,
1759
1982
  };
1760
- return this.saveCacheRecord(cacheRecord);
1983
+ return this.saveCacheRecord(cacheRecord, result.correlationId);
1761
1984
  }
1762
1985
 
1763
1986
  /**
@@ -1768,14 +1991,14 @@ export class BrowserCacheManager extends CacheManager {
1768
1991
  */
1769
1992
  async saveCacheRecord(
1770
1993
  cacheRecord: CacheRecord,
1771
- storeInCache?: StoreInCache,
1772
- correlationId?: string
1994
+ correlationId: string,
1995
+ storeInCache?: StoreInCache
1773
1996
  ): Promise<void> {
1774
1997
  try {
1775
1998
  await super.saveCacheRecord(
1776
1999
  cacheRecord,
1777
- storeInCache,
1778
- correlationId
2000
+ correlationId,
2001
+ storeInCache
1779
2002
  );
1780
2003
  } catch (e) {
1781
2004
  if (