@azure/msal-browser 3.11.1 → 3.14.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 (110) hide show
  1. package/dist/app/IPublicClientApplication.d.ts +2 -1
  2. package/dist/app/IPublicClientApplication.d.ts.map +1 -1
  3. package/dist/app/IPublicClientApplication.mjs +4 -1
  4. package/dist/app/IPublicClientApplication.mjs.map +1 -1
  5. package/dist/app/PublicClientApplication.mjs +1 -1
  6. package/dist/app/PublicClientNext.mjs +1 -1
  7. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +1 -1
  8. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +1 -1
  9. package/dist/cache/AsyncMemoryStorage.mjs +1 -1
  10. package/dist/cache/BrowserCacheManager.d.ts +10 -2
  11. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  12. package/dist/cache/BrowserCacheManager.mjs +31 -3
  13. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  14. package/dist/cache/BrowserStorage.mjs +1 -1
  15. package/dist/cache/DatabaseStorage.mjs +1 -1
  16. package/dist/cache/MemoryStorage.mjs +1 -1
  17. package/dist/cache/TokenCache.mjs +1 -1
  18. package/dist/config/Configuration.mjs +1 -1
  19. package/dist/controllers/ControllerFactory.d.ts.map +1 -1
  20. package/dist/controllers/ControllerFactory.mjs +2 -3
  21. package/dist/controllers/ControllerFactory.mjs.map +1 -1
  22. package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
  23. package/dist/controllers/NestedAppAuthController.mjs +3 -8
  24. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  25. package/dist/controllers/StandardController.d.ts +14 -0
  26. package/dist/controllers/StandardController.d.ts.map +1 -1
  27. package/dist/controllers/StandardController.mjs +213 -117
  28. package/dist/controllers/StandardController.mjs.map +1 -1
  29. package/dist/controllers/UnknownOperatingContextController.d.ts.map +1 -1
  30. package/dist/controllers/UnknownOperatingContextController.mjs +2 -2
  31. package/dist/controllers/UnknownOperatingContextController.mjs.map +1 -1
  32. package/dist/crypto/BrowserCrypto.mjs +1 -1
  33. package/dist/crypto/CryptoOps.mjs +1 -1
  34. package/dist/crypto/PkceGenerator.mjs +1 -1
  35. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  36. package/dist/encode/Base64Decode.mjs +1 -1
  37. package/dist/encode/Base64Encode.mjs +1 -1
  38. package/dist/error/BrowserAuthError.mjs +1 -1
  39. package/dist/error/BrowserAuthErrorCodes.mjs +1 -1
  40. package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
  41. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
  42. package/dist/error/NativeAuthError.mjs +1 -1
  43. package/dist/error/NativeAuthErrorCodes.mjs +1 -1
  44. package/dist/error/NestedAppAuthError.mjs +1 -1
  45. package/dist/event/EventHandler.mjs +1 -1
  46. package/dist/event/EventMessage.mjs +1 -1
  47. package/dist/event/EventType.mjs +1 -1
  48. package/dist/index.mjs +1 -1
  49. package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
  50. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  51. package/dist/interaction_client/NativeInteractionClient.mjs +1 -1
  52. package/dist/interaction_client/PopupClient.mjs +1 -1
  53. package/dist/interaction_client/RedirectClient.d.ts +4 -5
  54. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  55. package/dist/interaction_client/RedirectClient.mjs +5 -8
  56. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  57. package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
  58. package/dist/interaction_client/SilentCacheClient.mjs +1 -1
  59. package/dist/interaction_client/SilentIframeClient.mjs +1 -1
  60. package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
  61. package/dist/interaction_client/StandardInteractionClient.mjs +1 -1
  62. package/dist/interaction_handler/InteractionHandler.mjs +1 -1
  63. package/dist/interaction_handler/RedirectHandler.mjs +1 -1
  64. package/dist/interaction_handler/SilentHandler.mjs +2 -1
  65. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  66. package/dist/naa/BridgeError.mjs +1 -1
  67. package/dist/naa/BridgeProxy.d.ts +0 -2
  68. package/dist/naa/BridgeProxy.d.ts.map +1 -1
  69. package/dist/naa/BridgeProxy.mjs +4 -10
  70. package/dist/naa/BridgeProxy.mjs.map +1 -1
  71. package/dist/naa/BridgeStatusCode.mjs +1 -1
  72. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +1 -1
  73. package/dist/navigation/NavigationClient.mjs +1 -1
  74. package/dist/network/FetchClient.mjs +1 -1
  75. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  76. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  77. package/dist/operatingcontext/TeamsAppOperatingContext.d.ts.map +1 -1
  78. package/dist/operatingcontext/TeamsAppOperatingContext.mjs +10 -11
  79. package/dist/operatingcontext/TeamsAppOperatingContext.mjs.map +1 -1
  80. package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
  81. package/dist/packageMetadata.d.ts +1 -1
  82. package/dist/packageMetadata.mjs +2 -2
  83. package/dist/request/RequestHelpers.mjs +1 -1
  84. package/dist/response/ResponseHandler.mjs +1 -1
  85. package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
  86. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  87. package/dist/utils/BrowserConstants.mjs +1 -1
  88. package/dist/utils/BrowserProtocolUtils.mjs +1 -1
  89. package/dist/utils/BrowserUtils.d.ts +3 -0
  90. package/dist/utils/BrowserUtils.d.ts.map +1 -1
  91. package/dist/utils/BrowserUtils.mjs +2 -1
  92. package/dist/utils/BrowserUtils.mjs.map +1 -1
  93. package/lib/msal-browser.cjs +486 -257
  94. package/lib/msal-browser.cjs.map +1 -1
  95. package/lib/msal-browser.js +486 -257
  96. package/lib/msal-browser.js.map +1 -1
  97. package/lib/msal-browser.min.js +68 -66
  98. package/package.json +4 -4
  99. package/src/app/IPublicClientApplication.ts +5 -0
  100. package/src/cache/BrowserCacheManager.ts +49 -1
  101. package/src/controllers/ControllerFactory.ts +1 -4
  102. package/src/controllers/NestedAppAuthController.ts +7 -6
  103. package/src/controllers/StandardController.ts +380 -263
  104. package/src/controllers/UnknownOperatingContextController.ts +3 -1
  105. package/src/interaction_client/RedirectClient.ts +6 -12
  106. package/src/interaction_handler/SilentHandler.ts +1 -0
  107. package/src/naa/BridgeProxy.ts +3 -12
  108. package/src/operatingcontext/TeamsAppOperatingContext.ts +14 -8
  109. package/src/packageMetadata.ts +1 -1
  110. package/src/utils/BrowserUtils.ts +4 -1
@@ -84,6 +84,22 @@ import { ClearCacheRequest } from "../request/ClearCacheRequest";
84
84
  import { createNewGuid } from "../crypto/BrowserCrypto";
85
85
  import { initializeSilentRequest } from "../request/RequestHelpers";
86
86
 
87
+ function getAccountType(
88
+ account?: AccountInfo
89
+ ): "AAD" | "MSA" | "B2C" | undefined {
90
+ const idTokenClaims = account?.idTokenClaims;
91
+ if (idTokenClaims?.tfp || idTokenClaims?.acr) {
92
+ return "B2C";
93
+ }
94
+
95
+ if (!idTokenClaims?.tid) {
96
+ return undefined;
97
+ } else if (idTokenClaims?.tid === "9188040d-6c67-4c5b-b112-36a304b66dad") {
98
+ return "MSA";
99
+ }
100
+ return "AAD";
101
+ }
102
+
87
103
  export class StandardController implements IController {
88
104
  // OperatingContext
89
105
  protected readonly operatingContext: StandardOperatingContext;
@@ -141,6 +157,9 @@ export class StandardController implements IController {
141
157
  Promise<AuthenticationResult>
142
158
  >;
143
159
 
160
+ // Active Iframe request
161
+ private activeIframeRequest: [Promise<boolean>, string] | undefined;
162
+
144
163
  private ssoSilentMeasurement?: InProgressPerformanceEvent;
145
164
  private acquireTokenByCodeAsyncMeasurement?: InProgressPerformanceEvent;
146
165
  /**
@@ -204,7 +223,8 @@ export class StandardController implements IController {
204
223
  this.config.cache,
205
224
  this.browserCrypto,
206
225
  this.logger,
207
- buildStaticAuthorityOptions(this.config.auth)
226
+ buildStaticAuthorityOptions(this.config.auth),
227
+ this.performanceClient
208
228
  )
209
229
  : DEFAULT_BROWSER_CACHE_MANAGER(
210
230
  this.config.auth.clientId,
@@ -224,7 +244,9 @@ export class StandardController implements IController {
224
244
  this.config.auth.clientId,
225
245
  nativeCacheOptions,
226
246
  this.browserCrypto,
227
- this.logger
247
+ this.logger,
248
+ undefined,
249
+ this.performanceClient
228
250
  );
229
251
 
230
252
  // Initialize the token cache
@@ -332,7 +354,6 @@ export class StandardController implements IController {
332
354
  // Block token acquisition before initialize has been called
333
355
  BrowserUtils.blockAPICallsBeforeInitialize(this.initialized);
334
356
 
335
- const loggedInAccounts = this.getAllAccounts();
336
357
  if (this.isBrowserEnvironment) {
337
358
  /**
338
359
  * Store the promise on the PublicClientApplication instance if this is the first invocation of handleRedirectPromise,
@@ -342,155 +363,11 @@ export class StandardController implements IController {
342
363
  const redirectResponseKey = hash || "";
343
364
  let response = this.redirectResponse.get(redirectResponseKey);
344
365
  if (typeof response === "undefined") {
345
- const request: NativeTokenRequest | null =
346
- this.browserStorage.getCachedNativeRequest();
347
- const useNative =
348
- request &&
349
- NativeMessageHandler.isNativeAvailable(
350
- this.config,
351
- this.logger,
352
- this.nativeExtensionProvider
353
- ) &&
354
- this.nativeExtensionProvider &&
355
- !hash;
356
- const correlationId = useNative
357
- ? request?.correlationId
358
- : this.browserStorage.getTemporaryCache(
359
- TemporaryCacheKeys.CORRELATION_ID,
360
- true
361
- ) || "";
362
- const rootMeasurement = this.performanceClient.startMeasurement(
363
- "acquireTokenRedirect",
364
- correlationId
365
- );
366
- this.eventHandler.emitEvent(
367
- EventType.HANDLE_REDIRECT_START,
368
- InteractionType.Redirect
369
- );
366
+ response = this.handleRedirectPromiseInternal(hash);
367
+ this.redirectResponse.set(redirectResponseKey, response);
370
368
  this.logger.verbose(
371
369
  "handleRedirectPromise has been called for the first time, storing the promise"
372
370
  );
373
- let redirectResponse: Promise<AuthenticationResult | null>;
374
- if (useNative && this.nativeExtensionProvider) {
375
- this.logger.trace(
376
- "handleRedirectPromise - acquiring token from native platform"
377
- );
378
- const nativeClient = new NativeInteractionClient(
379
- this.config,
380
- this.browserStorage,
381
- this.browserCrypto,
382
- this.logger,
383
- this.eventHandler,
384
- this.navigationClient,
385
- ApiId.handleRedirectPromise,
386
- this.performanceClient,
387
- this.nativeExtensionProvider,
388
- request.accountId,
389
- this.nativeInternalStorage,
390
- request.correlationId
391
- );
392
-
393
- redirectResponse = invokeAsync(
394
- nativeClient.handleRedirectPromise.bind(nativeClient),
395
- PerformanceEvents.HandleNativeRedirectPromiseMeasurement,
396
- this.logger,
397
- this.performanceClient,
398
- rootMeasurement.event.correlationId
399
- )(
400
- this.performanceClient,
401
- rootMeasurement.event.correlationId
402
- );
403
- } else {
404
- this.logger.trace(
405
- "handleRedirectPromise - acquiring token from web flow"
406
- );
407
- const redirectClient =
408
- this.createRedirectClient(correlationId);
409
- redirectResponse = invokeAsync(
410
- redirectClient.handleRedirectPromise.bind(
411
- redirectClient
412
- ),
413
- PerformanceEvents.HandleRedirectPromiseMeasurement,
414
- this.logger,
415
- this.performanceClient,
416
- rootMeasurement.event.correlationId
417
- )(
418
- hash,
419
- this.performanceClient,
420
- rootMeasurement.event.correlationId
421
- );
422
- }
423
-
424
- response = redirectResponse
425
- .then((result: AuthenticationResult | null) => {
426
- if (result) {
427
- // Emit login event if number of accounts change
428
-
429
- const isLoggingIn =
430
- loggedInAccounts.length <
431
- this.getAllAccounts().length;
432
- if (isLoggingIn) {
433
- this.eventHandler.emitEvent(
434
- EventType.LOGIN_SUCCESS,
435
- InteractionType.Redirect,
436
- result
437
- );
438
- this.logger.verbose(
439
- "handleRedirectResponse returned result, login success"
440
- );
441
- } else {
442
- this.eventHandler.emitEvent(
443
- EventType.ACQUIRE_TOKEN_SUCCESS,
444
- InteractionType.Redirect,
445
- result
446
- );
447
- this.logger.verbose(
448
- "handleRedirectResponse returned result, acquire token success"
449
- );
450
- }
451
- rootMeasurement.end({ success: true });
452
- }
453
- this.eventHandler.emitEvent(
454
- EventType.HANDLE_REDIRECT_END,
455
- InteractionType.Redirect
456
- );
457
- rootMeasurement.end({ success: false });
458
-
459
- return result;
460
- })
461
- .catch((e) => {
462
- const eventError = e as EventError;
463
- // Emit login event if there is an account
464
- if (loggedInAccounts.length > 0) {
465
- this.eventHandler.emitEvent(
466
- EventType.ACQUIRE_TOKEN_FAILURE,
467
- InteractionType.Redirect,
468
- null,
469
- eventError
470
- );
471
- } else {
472
- this.eventHandler.emitEvent(
473
- EventType.LOGIN_FAILURE,
474
- InteractionType.Redirect,
475
- null,
476
- eventError
477
- );
478
- }
479
- this.eventHandler.emitEvent(
480
- EventType.HANDLE_REDIRECT_END,
481
- InteractionType.Redirect
482
- );
483
-
484
- rootMeasurement.end(
485
- {
486
- success: false,
487
- },
488
- eventError
489
- );
490
-
491
- throw e;
492
- });
493
- this.redirectResponse.set(redirectResponseKey, response);
494
371
  } else {
495
372
  this.logger.verbose(
496
373
  "handleRedirectPromise has been called previously, returning the result from the first call"
@@ -505,6 +382,165 @@ export class StandardController implements IController {
505
382
  return null;
506
383
  }
507
384
 
385
+ /**
386
+ * The internal details of handleRedirectPromise. This is separated out to a helper to allow handleRedirectPromise to memoize requests
387
+ * @param hash
388
+ * @returns
389
+ */
390
+ private async handleRedirectPromiseInternal(
391
+ hash?: string
392
+ ): Promise<AuthenticationResult | null> {
393
+ const loggedInAccounts = this.getAllAccounts();
394
+ const request: NativeTokenRequest | null =
395
+ this.browserStorage.getCachedNativeRequest();
396
+ const useNative =
397
+ request &&
398
+ NativeMessageHandler.isNativeAvailable(
399
+ this.config,
400
+ this.logger,
401
+ this.nativeExtensionProvider
402
+ ) &&
403
+ this.nativeExtensionProvider &&
404
+ !hash;
405
+ const correlationId = useNative
406
+ ? request?.correlationId
407
+ : this.browserStorage.getTemporaryCache(
408
+ TemporaryCacheKeys.CORRELATION_ID,
409
+ true
410
+ ) || "";
411
+ const rootMeasurement = this.performanceClient.startMeasurement(
412
+ "acquireTokenRedirect",
413
+ correlationId
414
+ );
415
+ this.eventHandler.emitEvent(
416
+ EventType.HANDLE_REDIRECT_START,
417
+ InteractionType.Redirect
418
+ );
419
+
420
+ let redirectResponse: Promise<AuthenticationResult | null>;
421
+ if (useNative && this.nativeExtensionProvider) {
422
+ this.logger.trace(
423
+ "handleRedirectPromise - acquiring token from native platform"
424
+ );
425
+ const nativeClient = new NativeInteractionClient(
426
+ this.config,
427
+ this.browserStorage,
428
+ this.browserCrypto,
429
+ this.logger,
430
+ this.eventHandler,
431
+ this.navigationClient,
432
+ ApiId.handleRedirectPromise,
433
+ this.performanceClient,
434
+ this.nativeExtensionProvider,
435
+ request.accountId,
436
+ this.nativeInternalStorage,
437
+ request.correlationId
438
+ );
439
+
440
+ redirectResponse = invokeAsync(
441
+ nativeClient.handleRedirectPromise.bind(nativeClient),
442
+ PerformanceEvents.HandleNativeRedirectPromiseMeasurement,
443
+ this.logger,
444
+ this.performanceClient,
445
+ rootMeasurement.event.correlationId
446
+ )(this.performanceClient, rootMeasurement.event.correlationId);
447
+ } else {
448
+ this.logger.trace(
449
+ "handleRedirectPromise - acquiring token from web flow"
450
+ );
451
+ const redirectClient = this.createRedirectClient(correlationId);
452
+ redirectResponse = invokeAsync(
453
+ redirectClient.handleRedirectPromise.bind(redirectClient),
454
+ PerformanceEvents.HandleRedirectPromiseMeasurement,
455
+ this.logger,
456
+ this.performanceClient,
457
+ rootMeasurement.event.correlationId
458
+ )(hash, rootMeasurement);
459
+ }
460
+
461
+ return redirectResponse
462
+ .then((result: AuthenticationResult | null) => {
463
+ if (result) {
464
+ // Emit login event if number of accounts change
465
+
466
+ const isLoggingIn =
467
+ loggedInAccounts.length < this.getAllAccounts().length;
468
+ if (isLoggingIn) {
469
+ this.eventHandler.emitEvent(
470
+ EventType.LOGIN_SUCCESS,
471
+ InteractionType.Redirect,
472
+ result
473
+ );
474
+ this.logger.verbose(
475
+ "handleRedirectResponse returned result, login success"
476
+ );
477
+ } else {
478
+ this.eventHandler.emitEvent(
479
+ EventType.ACQUIRE_TOKEN_SUCCESS,
480
+ InteractionType.Redirect,
481
+ result
482
+ );
483
+ this.logger.verbose(
484
+ "handleRedirectResponse returned result, acquire token success"
485
+ );
486
+ }
487
+ rootMeasurement.end({
488
+ success: true,
489
+ accountType: getAccountType(result.account),
490
+ });
491
+ } else {
492
+ /*
493
+ * Instrument an event only if an error code is set. Otherwise, discard it when the redirect response
494
+ * is empty and the error code is missing.
495
+ */
496
+ if (rootMeasurement.event.errorCode) {
497
+ rootMeasurement.end({ success: false });
498
+ } else {
499
+ rootMeasurement.discard();
500
+ }
501
+ }
502
+
503
+ this.eventHandler.emitEvent(
504
+ EventType.HANDLE_REDIRECT_END,
505
+ InteractionType.Redirect
506
+ );
507
+
508
+ return result;
509
+ })
510
+ .catch((e) => {
511
+ const eventError = e as EventError;
512
+ // Emit login event if there is an account
513
+ if (loggedInAccounts.length > 0) {
514
+ this.eventHandler.emitEvent(
515
+ EventType.ACQUIRE_TOKEN_FAILURE,
516
+ InteractionType.Redirect,
517
+ null,
518
+ eventError
519
+ );
520
+ } else {
521
+ this.eventHandler.emitEvent(
522
+ EventType.LOGIN_FAILURE,
523
+ InteractionType.Redirect,
524
+ null,
525
+ eventError
526
+ );
527
+ }
528
+ this.eventHandler.emitEvent(
529
+ EventType.HANDLE_REDIRECT_END,
530
+ InteractionType.Redirect
531
+ );
532
+
533
+ rootMeasurement.end(
534
+ {
535
+ success: false,
536
+ },
537
+ eventError
538
+ );
539
+
540
+ throw e;
541
+ });
542
+ }
543
+
508
544
  /**
509
545
  * Use when you want to obtain an access_token for your API by redirecting the user's browser window to the authorization endpoint. This function redirects
510
546
  * the page, so any code that follows this function will not execute.
@@ -620,6 +656,11 @@ export class StandardController implements IController {
620
656
  correlationId
621
657
  );
622
658
 
659
+ atPopupMeasurement.add({
660
+ scenarioId: request.scenarioId,
661
+ accountType: getAccountType(request.account),
662
+ });
663
+
623
664
  try {
624
665
  this.logger.verbose("acquireTokenPopup called", correlationId);
625
666
  BrowserUtils.preflightCheck(this.initialized);
@@ -661,6 +702,7 @@ export class StandardController implements IController {
661
702
  success: true,
662
703
  isNativeBroker: true,
663
704
  requestId: response.requestId,
705
+ accountType: getAccountType(response.account),
664
706
  });
665
707
  return response;
666
708
  })
@@ -710,13 +752,12 @@ export class StandardController implements IController {
710
752
  );
711
753
  }
712
754
 
713
- atPopupMeasurement.add({
714
- accessTokenSize: result.accessToken.length,
715
- idTokenSize: result.idToken.length,
716
- });
717
755
  atPopupMeasurement.end({
718
756
  success: true,
719
757
  requestId: result.requestId,
758
+ accessTokenSize: result.accessToken.length,
759
+ idTokenSize: result.idToken.length,
760
+ accountType: getAccountType(result.account),
720
761
  });
721
762
  return result;
722
763
  })
@@ -800,6 +841,11 @@ export class StandardController implements IController {
800
841
  this.ssoSilentMeasurement?.increment({
801
842
  visibilityChangeCount: 0,
802
843
  });
844
+ this.ssoSilentMeasurement?.add({
845
+ scenarioId: request.scenarioId,
846
+ accountType: getAccountType(request.account),
847
+ });
848
+
803
849
  document.addEventListener(
804
850
  "visibilitychange",
805
851
  this.trackPageVisibilityWithMeasurement
@@ -842,14 +888,13 @@ export class StandardController implements IController {
842
888
  InteractionType.Silent,
843
889
  response
844
890
  );
845
- this.ssoSilentMeasurement?.add({
846
- accessTokenSize: response.accessToken.length,
847
- idTokenSize: response.idToken.length,
848
- });
849
891
  this.ssoSilentMeasurement?.end({
850
892
  success: true,
851
893
  isNativeBroker: response.fromNativeBroker,
852
894
  requestId: response.requestId,
895
+ accessTokenSize: response.accessToken.length,
896
+ idTokenSize: response.idToken.length,
897
+ accountType: getAccountType(response.account),
853
898
  });
854
899
  return response;
855
900
  })
@@ -901,6 +946,7 @@ export class StandardController implements IController {
901
946
  PerformanceEvents.AcquireTokenByCode,
902
947
  correlationId
903
948
  );
949
+ atbcMeasurement.add({ scenarioId: request.scenarioId });
904
950
 
905
951
  try {
906
952
  if (request.code && request.nativeAccountId) {
@@ -927,14 +973,13 @@ export class StandardController implements IController {
927
973
  result
928
974
  );
929
975
  this.hybridAuthCodeResponses.delete(hybridAuthCode);
930
- atbcMeasurement.add({
931
- accessTokenSize: result.accessToken.length,
932
- idTokenSize: result.idToken.length,
933
- });
934
976
  atbcMeasurement.end({
935
977
  success: true,
936
978
  isNativeBroker: result.fromNativeBroker,
937
979
  requestId: result.requestId,
980
+ accessTokenSize: result.accessToken.length,
981
+ idTokenSize: result.idToken.length,
982
+ accountType: getAccountType(result.account),
938
983
  });
939
984
  return result;
940
985
  })
@@ -965,7 +1010,7 @@ export class StandardController implements IController {
965
1010
  return await response;
966
1011
  } else if (request.nativeAccountId) {
967
1012
  if (this.canUseNative(request, request.nativeAccountId)) {
968
- return await this.acquireTokenNative(
1013
+ const result = await this.acquireTokenNative(
969
1014
  {
970
1015
  ...request,
971
1016
  correlationId,
@@ -982,6 +1027,11 @@ export class StandardController implements IController {
982
1027
  }
983
1028
  throw e;
984
1029
  });
1030
+ atbcMeasurement.end({
1031
+ accountType: getAccountType(result.account),
1032
+ success: true,
1033
+ });
1034
+ return result;
985
1035
  } else {
986
1036
  throw createBrowserAuthError(
987
1037
  BrowserAuthErrorCodes.unableToAcquireTokenFromNativePlatform
@@ -1845,6 +1895,7 @@ export class StandardController implements IController {
1845
1895
  );
1846
1896
  atsMeasurement.add({
1847
1897
  cacheLookupPolicy: request.cacheLookupPolicy,
1898
+ scenarioId: request.scenarioId,
1848
1899
  });
1849
1900
 
1850
1901
  BrowserUtils.preflightCheck(this.initialized);
@@ -1854,6 +1905,7 @@ export class StandardController implements IController {
1854
1905
  if (!account) {
1855
1906
  throw createBrowserAuthError(BrowserAuthErrorCodes.noAccountError);
1856
1907
  }
1908
+ atsMeasurement.add({ accountType: getAccountType(account) });
1857
1909
 
1858
1910
  const thumbprint: RequestThumbprint = {
1859
1911
  clientId: this.config.auth.clientId,
@@ -1893,16 +1945,14 @@ export class StandardController implements IController {
1893
1945
  )
1894
1946
  .then((result) => {
1895
1947
  this.activeSilentTokenRequests.delete(silentRequestKey);
1896
- atsMeasurement.add({
1897
- accessTokenSize: result.accessToken.length,
1898
- idTokenSize: result.idToken.length,
1899
- });
1900
1948
  atsMeasurement.end({
1901
1949
  success: true,
1902
1950
  fromCache: result.fromCache,
1903
1951
  isNativeBroker: result.fromNativeBroker,
1904
1952
  cacheLookupPolicy: request.cacheLookupPolicy,
1905
1953
  requestId: result.requestId,
1954
+ accessTokenSize: result.accessToken.length,
1955
+ idTokenSize: result.idToken.length,
1906
1956
  });
1907
1957
  return result;
1908
1958
  })
@@ -1966,24 +2016,175 @@ export class StandardController implements IController {
1966
2016
  document.addEventListener("visibilitychange", () =>
1967
2017
  this.trackPageVisibility(request.correlationId)
1968
2018
  );
1969
- let result: Promise<AuthenticationResult>;
2019
+
2020
+ const silentRequest = await invokeAsync(
2021
+ initializeSilentRequest,
2022
+ PerformanceEvents.InitializeSilentRequest,
2023
+ this.logger,
2024
+ this.performanceClient,
2025
+ request.correlationId
2026
+ )(request, account, this.config, this.performanceClient, this.logger);
2027
+ const cacheLookupPolicy =
2028
+ request.cacheLookupPolicy || CacheLookupPolicy.Default;
2029
+
2030
+ const result = this.acquireTokenSilentNoIframe(
2031
+ silentRequest,
2032
+ cacheLookupPolicy
2033
+ ).catch(async (refreshTokenError: AuthError) => {
2034
+ const shouldTryToResolveSilently =
2035
+ checkIfRefreshTokenErrorCanBeResolvedSilently(
2036
+ refreshTokenError,
2037
+ cacheLookupPolicy
2038
+ );
2039
+
2040
+ if (shouldTryToResolveSilently) {
2041
+ if (!this.activeIframeRequest) {
2042
+ let _resolve: (result: boolean) => void;
2043
+ // Always set the active request tracker immediately after checking it to prevent races
2044
+ this.activeIframeRequest = [
2045
+ new Promise((resolve) => {
2046
+ _resolve = resolve;
2047
+ }),
2048
+ silentRequest.correlationId,
2049
+ ];
2050
+ this.logger.verbose(
2051
+ "Refresh token expired/invalid or CacheLookupPolicy is set to Skip, attempting acquire token by iframe.",
2052
+ silentRequest.correlationId
2053
+ );
2054
+ return invokeAsync(
2055
+ this.acquireTokenBySilentIframe.bind(this),
2056
+ PerformanceEvents.AcquireTokenBySilentIframe,
2057
+ this.logger,
2058
+ this.performanceClient,
2059
+ silentRequest.correlationId
2060
+ )(silentRequest)
2061
+ .then((iframeResult) => {
2062
+ _resolve(true);
2063
+ return iframeResult;
2064
+ })
2065
+ .catch((e) => {
2066
+ _resolve(false);
2067
+ throw e;
2068
+ })
2069
+ .finally(() => {
2070
+ this.activeIframeRequest = undefined;
2071
+ });
2072
+ } else if (cacheLookupPolicy !== CacheLookupPolicy.Skip) {
2073
+ const [activePromise, activeCorrelationId] =
2074
+ this.activeIframeRequest;
2075
+ this.logger.verbose(
2076
+ `Iframe request is already in progress, awaiting resolution for request with correlationId: ${activeCorrelationId}`,
2077
+ silentRequest.correlationId
2078
+ );
2079
+ const awaitConcurrentIframeMeasure =
2080
+ this.performanceClient.startMeasurement(
2081
+ PerformanceEvents.AwaitConcurrentIframe,
2082
+ silentRequest.correlationId
2083
+ );
2084
+ awaitConcurrentIframeMeasure.add({
2085
+ awaitIframeCorrelationId: activeCorrelationId,
2086
+ });
2087
+
2088
+ const activePromiseResult = await activePromise;
2089
+ awaitConcurrentIframeMeasure.end({
2090
+ success: activePromiseResult,
2091
+ });
2092
+ if (activePromiseResult) {
2093
+ this.logger.verbose(
2094
+ `Parallel iframe request with correlationId: ${activeCorrelationId} succeeded. Retrying cache and/or RT redemption`,
2095
+ silentRequest.correlationId
2096
+ );
2097
+ // Retry cache lookup and/or RT exchange after iframe completes
2098
+ return this.acquireTokenSilentNoIframe(
2099
+ silentRequest,
2100
+ cacheLookupPolicy
2101
+ );
2102
+ } else {
2103
+ this.logger.info(
2104
+ `Iframe request with correlationId: ${activeCorrelationId} failed. Interaction is required.`
2105
+ );
2106
+ // If previous iframe request failed, it's unlikely to succeed this time. Throw original error.
2107
+ throw refreshTokenError;
2108
+ }
2109
+ } else {
2110
+ // Cache policy set to skip and another iframe request is already in progress
2111
+ this.logger.warning(
2112
+ "Another iframe request is currently in progress and CacheLookupPolicy is set to Skip. This may result in degraded performance and/or reliability for both calls. Please consider changing the CacheLookupPolicy to take advantage of request queuing and token cache.",
2113
+ silentRequest.correlationId
2114
+ );
2115
+ return invokeAsync(
2116
+ this.acquireTokenBySilentIframe.bind(this),
2117
+ PerformanceEvents.AcquireTokenBySilentIframe,
2118
+ this.logger,
2119
+ this.performanceClient,
2120
+ silentRequest.correlationId
2121
+ )(silentRequest);
2122
+ }
2123
+ } else {
2124
+ // Error cannot be silently resolved or iframe renewal is not allowed, interaction required
2125
+ throw refreshTokenError;
2126
+ }
2127
+ });
2128
+
2129
+ return result
2130
+ .then((response) => {
2131
+ this.eventHandler.emitEvent(
2132
+ EventType.ACQUIRE_TOKEN_SUCCESS,
2133
+ InteractionType.Silent,
2134
+ response
2135
+ );
2136
+ if (request.correlationId) {
2137
+ this.performanceClient.addFields(
2138
+ {
2139
+ fromCache: response.fromCache,
2140
+ isNativeBroker: response.fromNativeBroker,
2141
+ requestId: response.requestId,
2142
+ },
2143
+ request.correlationId
2144
+ );
2145
+ }
2146
+
2147
+ return response;
2148
+ })
2149
+ .catch((tokenRenewalError: Error) => {
2150
+ this.eventHandler.emitEvent(
2151
+ EventType.ACQUIRE_TOKEN_FAILURE,
2152
+ InteractionType.Silent,
2153
+ null,
2154
+ tokenRenewalError
2155
+ );
2156
+ throw tokenRenewalError;
2157
+ })
2158
+ .finally(() => {
2159
+ document.removeEventListener("visibilitychange", () =>
2160
+ this.trackPageVisibility(request.correlationId)
2161
+ );
2162
+ });
2163
+ }
2164
+
2165
+ /**
2166
+ * AcquireTokenSilent without the iframe fallback. This is used to enable the correct fallbacks in cases where there's a potential for multiple silent requests to be made in parallel and prevent those requests from making concurrent iframe requests.
2167
+ * @param silentRequest
2168
+ * @param cacheLookupPolicy
2169
+ * @returns
2170
+ */
2171
+ private async acquireTokenSilentNoIframe(
2172
+ silentRequest: CommonSilentFlowRequest,
2173
+ cacheLookupPolicy: CacheLookupPolicy
2174
+ ): Promise<AuthenticationResult> {
1970
2175
  if (
1971
2176
  NativeMessageHandler.isNativeAvailable(
1972
2177
  this.config,
1973
2178
  this.logger,
1974
2179
  this.nativeExtensionProvider,
1975
- request.authenticationScheme
2180
+ silentRequest.authenticationScheme
1976
2181
  ) &&
1977
- account.nativeAccountId
2182
+ silentRequest.account.nativeAccountId
1978
2183
  ) {
1979
2184
  this.logger.verbose(
1980
2185
  "acquireTokenSilent - attempting to acquire token from native platform"
1981
2186
  );
1982
- const silentRequest: SilentRequest = {
1983
- ...request,
1984
- account,
1985
- };
1986
- result = this.acquireTokenNative(
2187
+ return this.acquireTokenNative(
1987
2188
  silentRequest,
1988
2189
  ApiId.acquireTokenSilent_silentFlow
1989
2190
  ).catch(async (e: AuthError) => {
@@ -1995,10 +2196,9 @@ export class StandardController implements IController {
1995
2196
  this.nativeExtensionProvider = undefined; // Prevent future requests from continuing to attempt
1996
2197
 
1997
2198
  // Cache will not contain tokens, given that previous WAM requests succeeded. Skip cache and RT renewal and go straight to iframe renewal
1998
- const silentIframeClient = this.createSilentIframeClient(
1999
- request.correlationId
2199
+ throw createClientAuthError(
2200
+ ClientAuthErrorCodes.tokenRefreshRequired
2000
2201
  );
2001
- return silentIframeClient.acquireToken(request);
2002
2202
  }
2003
2203
  throw e;
2004
2204
  });
@@ -2006,25 +2206,7 @@ export class StandardController implements IController {
2006
2206
  this.logger.verbose(
2007
2207
  "acquireTokenSilent - attempting to acquire token from web flow"
2008
2208
  );
2009
-
2010
- const silentRequest = await invokeAsync(
2011
- initializeSilentRequest,
2012
- PerformanceEvents.InitializeSilentRequest,
2013
- this.logger,
2014
- this.performanceClient,
2015
- request.correlationId
2016
- )(
2017
- request,
2018
- account,
2019
- this.config,
2020
- this.performanceClient,
2021
- this.logger
2022
- );
2023
-
2024
- const cacheLookupPolicy =
2025
- request.cacheLookupPolicy || CacheLookupPolicy.Default;
2026
-
2027
- result = invokeAsync(
2209
+ return invokeAsync(
2028
2210
  this.acquireTokenFromCache.bind(this),
2029
2211
  PerformanceEvents.AcquireTokenFromCache,
2030
2212
  this.logger,
@@ -2032,10 +2214,7 @@ export class StandardController implements IController {
2032
2214
  silentRequest.correlationId
2033
2215
  )(silentRequest, cacheLookupPolicy).catch(
2034
2216
  (cacheError: AuthError) => {
2035
- if (
2036
- request.cacheLookupPolicy ===
2037
- CacheLookupPolicy.AccessToken
2038
- ) {
2217
+ if (cacheLookupPolicy === CacheLookupPolicy.AccessToken) {
2039
2218
  throw cacheError;
2040
2219
  }
2041
2220
 
@@ -2051,71 +2230,10 @@ export class StandardController implements IController {
2051
2230
  this.logger,
2052
2231
  this.performanceClient,
2053
2232
  silentRequest.correlationId
2054
- )(silentRequest, cacheLookupPolicy).catch(
2055
- (refreshTokenError: AuthError) => {
2056
- const shouldTryToResolveSilently =
2057
- checkIfRefreshTokenErrorCanBeResolvedSilently(
2058
- refreshTokenError,
2059
- silentRequest,
2060
- cacheLookupPolicy
2061
- );
2062
-
2063
- if (shouldTryToResolveSilently) {
2064
- this.logger.verbose(
2065
- "Refresh token expired/invalid or CacheLookupPolicy is set to Skip, attempting acquire token by iframe.",
2066
- silentRequest.correlationId
2067
- );
2068
- return invokeAsync(
2069
- this.acquireTokenBySilentIframe.bind(this),
2070
- PerformanceEvents.AcquireTokenBySilentIframe,
2071
- this.logger,
2072
- this.performanceClient,
2073
- silentRequest.correlationId
2074
- )(silentRequest);
2075
- } else {
2076
- // Error cannot be silently resolved or iframe renewal is not allowed, interaction required
2077
- throw refreshTokenError;
2078
- }
2079
- }
2080
- );
2233
+ )(silentRequest, cacheLookupPolicy);
2081
2234
  }
2082
2235
  );
2083
2236
  }
2084
-
2085
- return result
2086
- .then((response) => {
2087
- this.eventHandler.emitEvent(
2088
- EventType.ACQUIRE_TOKEN_SUCCESS,
2089
- InteractionType.Silent,
2090
- response
2091
- );
2092
- if (request.correlationId) {
2093
- this.performanceClient.addFields(
2094
- {
2095
- fromCache: response.fromCache,
2096
- isNativeBroker: response.fromNativeBroker,
2097
- requestId: response.requestId,
2098
- },
2099
- request.correlationId
2100
- );
2101
- }
2102
-
2103
- return response;
2104
- })
2105
- .catch((tokenRenewalError: Error) => {
2106
- this.eventHandler.emitEvent(
2107
- EventType.ACQUIRE_TOKEN_FAILURE,
2108
- InteractionType.Silent,
2109
- null,
2110
- tokenRenewalError
2111
- );
2112
- throw tokenRenewalError;
2113
- })
2114
- .finally(() => {
2115
- document.removeEventListener("visibilitychange", () =>
2116
- this.trackPageVisibility(request.correlationId)
2117
- );
2118
- });
2119
2237
  }
2120
2238
  }
2121
2239
 
@@ -2128,7 +2246,6 @@ export class StandardController implements IController {
2128
2246
  */
2129
2247
  function checkIfRefreshTokenErrorCanBeResolvedSilently(
2130
2248
  refreshTokenError: AuthError,
2131
- silentRequest: CommonSilentFlowRequest,
2132
2249
  cacheLookupPolicy: CacheLookupPolicy
2133
2250
  ): boolean {
2134
2251
  const noInteractionRequired = !(