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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +213 -213
  3. package/dist/app/IPublicClientApplication.d.ts +45 -45
  4. package/dist/app/IPublicClientApplication.js +96 -96
  5. package/dist/app/PublicClientApplication.d.ts +244 -244
  6. package/dist/app/PublicClientApplication.js +307 -307
  7. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts +63 -63
  8. package/dist/broker/nativeBroker/NativeMessageHandler.js +255 -255
  9. package/dist/broker/nativeBroker/NativeRequest.d.ts +43 -43
  10. package/dist/broker/nativeBroker/NativeResponse.d.ts +48 -48
  11. package/dist/cache/AsyncMemoryStorage.d.ts +51 -51
  12. package/dist/cache/AsyncMemoryStorage.js +132 -132
  13. package/dist/cache/BrowserCacheManager.d.ts +377 -377
  14. package/dist/cache/BrowserCacheManager.js +1276 -1276
  15. package/dist/cache/BrowserStorage.d.ts +11 -11
  16. package/dist/cache/BrowserStorage.js +35 -35
  17. package/dist/cache/CryptoKeyStore.d.ts +17 -17
  18. package/dist/cache/CryptoKeyStore.js +41 -41
  19. package/dist/cache/DatabaseStorage.d.ts +56 -56
  20. package/dist/cache/DatabaseStorage.js +190 -190
  21. package/dist/cache/IAsyncMemoryStorage.d.ts +27 -27
  22. package/dist/cache/ITokenCache.d.ts +10 -10
  23. package/dist/cache/IWindowStorage.d.ts +27 -27
  24. package/dist/cache/MemoryStorage.d.ts +11 -11
  25. package/dist/cache/MemoryStorage.js +31 -31
  26. package/dist/cache/TokenCache.d.ts +76 -76
  27. package/dist/cache/TokenCache.js +225 -225
  28. package/dist/config/Configuration.d.ts +203 -203
  29. package/dist/config/Configuration.js +101 -101
  30. package/dist/controllers/ControllerFactory.d.ts +9 -9
  31. package/dist/controllers/ControllerFactory.js +37 -37
  32. package/dist/controllers/IController.d.ts +66 -66
  33. package/dist/controllers/StandardController.d.ts +413 -413
  34. package/dist/controllers/StandardController.js +1272 -1272
  35. package/dist/crypto/BrowserCrypto.d.ts +61 -61
  36. package/dist/crypto/BrowserCrypto.js +131 -131
  37. package/dist/crypto/CryptoOps.d.ts +73 -73
  38. package/dist/crypto/CryptoOps.js +150 -150
  39. package/dist/crypto/GuidGenerator.d.ts +12 -12
  40. package/dist/crypto/GuidGenerator.js +96 -96
  41. package/dist/crypto/ISubtleCrypto.d.ts +9 -9
  42. package/dist/crypto/ModernBrowserCrypto.d.ts +9 -9
  43. package/dist/crypto/ModernBrowserCrypto.js +24 -24
  44. package/dist/crypto/MsBrowserCrypto.d.ts +9 -9
  45. package/dist/crypto/MsBrowserCrypto.js +79 -79
  46. package/dist/crypto/MsrBrowserCrypto.d.ts +17 -17
  47. package/dist/crypto/MsrBrowserCrypto.js +28 -28
  48. package/dist/crypto/PkceGenerator.d.ts +24 -24
  49. package/dist/crypto/PkceGenerator.js +58 -58
  50. package/dist/crypto/SignedHttpRequest.d.ts +30 -30
  51. package/dist/crypto/SignedHttpRequest.js +39 -39
  52. package/dist/encode/Base64Decode.d.ts +22 -22
  53. package/dist/encode/Base64Decode.js +73 -73
  54. package/dist/encode/Base64Encode.d.ts +31 -31
  55. package/dist/encode/Base64Encode.js +79 -79
  56. package/dist/error/BrowserAuthError.d.ts +382 -382
  57. package/dist/error/BrowserAuthError.js +483 -483
  58. package/dist/error/BrowserConfigurationAuthError.d.ts +70 -70
  59. package/dist/error/BrowserConfigurationAuthError.js +96 -96
  60. package/dist/error/NativeAuthError.d.ts +56 -56
  61. package/dist/error/NativeAuthError.js +88 -88
  62. package/dist/event/EventHandler.d.ts +41 -41
  63. package/dist/event/EventHandler.js +119 -119
  64. package/dist/event/EventMessage.d.ts +25 -25
  65. package/dist/event/EventMessage.js +67 -67
  66. package/dist/event/EventType.d.ts +26 -26
  67. package/dist/event/EventType.js +31 -31
  68. package/dist/index.d.ts +35 -35
  69. package/dist/index.js +1 -1
  70. package/dist/interaction_client/BaseInteractionClient.d.ts +52 -52
  71. package/dist/interaction_client/BaseInteractionClient.js +138 -138
  72. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.d.ts +4 -4
  73. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.js +10 -10
  74. package/dist/interaction_client/NativeInteractionClient.d.ts +148 -148
  75. package/dist/interaction_client/NativeInteractionClient.js +516 -516
  76. package/dist/interaction_client/PopupClient.d.ts +111 -111
  77. package/dist/interaction_client/PopupClient.js +476 -476
  78. package/dist/interaction_client/RedirectClient.d.ts +48 -48
  79. package/dist/interaction_client/RedirectClient.js +290 -290
  80. package/dist/interaction_client/SilentAuthCodeClient.d.ts +22 -22
  81. package/dist/interaction_client/SilentAuthCodeClient.js +62 -62
  82. package/dist/interaction_client/SilentCacheClient.d.ts +21 -21
  83. package/dist/interaction_client/SilentCacheClient.js +67 -67
  84. package/dist/interaction_client/SilentIframeClient.d.ts +31 -31
  85. package/dist/interaction_client/SilentIframeClient.js +127 -127
  86. package/dist/interaction_client/SilentRefreshClient.d.ts +19 -19
  87. package/dist/interaction_client/SilentRefreshClient.js +62 -62
  88. package/dist/interaction_client/StandardInteractionClient.d.ts +59 -59
  89. package/dist/interaction_client/StandardInteractionClient.js +259 -259
  90. package/dist/interaction_handler/InteractionHandler.d.ts +39 -39
  91. package/dist/interaction_handler/InteractionHandler.js +127 -127
  92. package/dist/interaction_handler/RedirectHandler.d.ts +24 -24
  93. package/dist/interaction_handler/RedirectHandler.js +118 -118
  94. package/dist/interaction_handler/SilentHandler.d.ts +47 -47
  95. package/dist/interaction_handler/SilentHandler.js +162 -162
  96. package/dist/internals.d.ts +23 -23
  97. package/dist/internals.js +4 -4
  98. package/dist/navigation/INavigationClient.d.ts +16 -16
  99. package/dist/navigation/NavigationClient.d.ts +22 -22
  100. package/dist/navigation/NavigationClient.js +40 -40
  101. package/dist/navigation/NavigationOptions.d.ts +12 -12
  102. package/dist/network/FetchClient.d.ts +26 -26
  103. package/dist/network/FetchClient.js +99 -99
  104. package/dist/network/XhrClient.d.ts +40 -40
  105. package/dist/network/XhrClient.js +117 -117
  106. package/dist/operatingcontext/BaseOperatingContext.d.ts +40 -40
  107. package/dist/operatingcontext/BaseOperatingContext.js +44 -44
  108. package/dist/operatingcontext/StandardOperatingContext.d.ts +25 -25
  109. package/dist/operatingcontext/StandardOperatingContext.js +43 -43
  110. package/dist/operatingcontext/TeamsAppOperatingContext.d.ts +25 -25
  111. package/dist/operatingcontext/TeamsAppOperatingContext.js +42 -42
  112. package/dist/packageMetadata.d.ts +2 -2
  113. package/dist/packageMetadata.js +4 -4
  114. package/dist/request/AuthorizationCodeRequest.d.ts +8 -8
  115. package/dist/request/AuthorizationUrlRequest.d.ts +8 -8
  116. package/dist/request/EndSessionPopupRequest.d.ts +18 -18
  117. package/dist/request/EndSessionRequest.d.ts +15 -15
  118. package/dist/request/PopupRequest.d.ts +32 -32
  119. package/dist/request/PopupWindowAttributes.d.ts +15 -15
  120. package/dist/request/RedirectRequest.d.ts +33 -33
  121. package/dist/request/SilentRequest.d.ts +30 -30
  122. package/dist/request/SsoSilentRequest.d.ts +26 -26
  123. package/dist/telemetry/BrowserPerformanceClient.d.ts +38 -38
  124. package/dist/telemetry/BrowserPerformanceClient.js +124 -124
  125. package/dist/telemetry/BrowserPerformanceMeasurement.d.ts +21 -21
  126. package/dist/telemetry/BrowserPerformanceMeasurement.js +92 -92
  127. package/dist/utils/BrowserConstants.d.ts +166 -166
  128. package/dist/utils/BrowserConstants.js +214 -214
  129. package/dist/utils/BrowserProtocolUtils.d.ts +18 -18
  130. package/dist/utils/BrowserProtocolUtils.js +34 -34
  131. package/dist/utils/BrowserStringUtils.d.ts +26 -26
  132. package/dist/utils/BrowserStringUtils.js +149 -149
  133. package/dist/utils/BrowserUtils.d.ts +65 -65
  134. package/dist/utils/BrowserUtils.js +138 -138
  135. package/dist/utils/MathUtils.d.ts +11 -11
  136. package/dist/utils/MathUtils.js +21 -21
  137. package/lib/msal-browser.cjs.js +18383 -0
  138. package/lib/msal-browser.cjs.js.map +1 -0
  139. package/lib/msal-browser.js +18389 -0
  140. package/lib/msal-browser.js.map +1 -0
  141. package/lib/msal-browser.min.js +74 -0
  142. package/package.json +97 -95
@@ -1,4 +1,4 @@
1
- /*! @azure/msal-browser v3.0.0-alpha.1 2023-05-08 */
1
+ /*! @azure/msal-browser v3.0.0-alpha.2 2023-05-17 */
2
2
  'use strict';
3
3
  import { OIDC_DEFAULT_SCOPES, PerformanceEvents, Constants, UrlString, ThrottlingUtils, ProtocolUtils, AuthError, StringUtils } from '@azure/msal-common';
4
4
  import { StandardInteractionClient } from './StandardInteractionClient.js';
@@ -10,481 +10,481 @@ import { NativeMessageHandler } from '../broker/nativeBroker/NativeMessageHandle
10
10
  import { BrowserAuthError } from '../error/BrowserAuthError.js';
11
11
  import { InteractionHandler } from '../interaction_handler/InteractionHandler.js';
12
12
 
13
- /*
14
- * Copyright (c) Microsoft Corporation. All rights reserved.
15
- * Licensed under the MIT License.
16
- */
17
- class PopupClient extends StandardInteractionClient {
18
- constructor(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeStorageImpl, nativeMessageHandler, correlationId) {
19
- super(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeMessageHandler, correlationId);
20
- // Properly sets this reference for the unload event.
21
- this.unloadWindow = this.unloadWindow.bind(this);
22
- this.nativeStorage = nativeStorageImpl;
23
- }
24
- /**
25
- * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
26
- * @param request
27
- */
28
- acquireToken(request) {
29
- try {
30
- const popupName = this.generatePopupName(request.scopes || OIDC_DEFAULT_SCOPES, request.authority || this.config.auth.authority);
31
- const popupWindowAttributes = request.popupWindowAttributes || {};
32
- // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
33
- if (this.config.system.asyncPopups) {
34
- this.logger.verbose("asyncPopups set to true, acquiring token");
35
- // Passes on popup position and dimensions if in request
36
- return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes);
37
- }
38
- else {
39
- // asyncPopups flag is set to false. Opens popup before acquiring token.
40
- this.logger.verbose("asyncPopup set to false, opening popup before acquiring token");
41
- const popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
42
- return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes, popup);
43
- }
44
- }
45
- catch (e) {
46
- return Promise.reject(e);
47
- }
48
- }
49
- /**
50
- * Clears local cache for the current user then opens a popup window prompting the user to sign-out of the server
51
- * @param logoutRequest
52
- */
53
- logout(logoutRequest) {
54
- try {
55
- this.logger.verbose("logoutPopup called");
56
- const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
57
- const popupName = this.generateLogoutPopupName(validLogoutRequest);
58
- const authority = logoutRequest && logoutRequest.authority;
59
- const mainWindowRedirectUri = logoutRequest && logoutRequest.mainWindowRedirectUri;
60
- const popupWindowAttributes = logoutRequest?.popupWindowAttributes || {};
61
- // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
62
- if (this.config.system.asyncPopups) {
63
- this.logger.verbose("asyncPopups set to true");
64
- // Passes on popup position and dimensions if in request
65
- return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, undefined, mainWindowRedirectUri);
66
- }
67
- else {
68
- // asyncPopups flag is set to false. Opens popup before logging out.
69
- this.logger.verbose("asyncPopup set to false, opening popup");
70
- const popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
71
- return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, popup, mainWindowRedirectUri);
72
- }
73
- }
74
- catch (e) {
75
- // Since this function is synchronous we need to reject
76
- return Promise.reject(e);
77
- }
78
- }
79
- /**
80
- * Helper which obtains an access_token for your API via opening a popup window in the user's browser
81
- * @param validRequest
82
- * @param popupName
83
- * @param popup
84
- * @param popupWindowAttributes
85
- *
86
- * @returns A promise that is fulfilled when this function has completed, or rejected if an error was raised.
87
- */
88
- async acquireTokenPopupAsync(request, popupName, popupWindowAttributes, popup) {
89
- this.logger.verbose("acquireTokenPopupAsync called");
90
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
91
- this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, request.correlationId);
92
- const validRequest = await this.initializeAuthorizationRequest(request, InteractionType.Popup);
93
- this.browserStorage.updateCacheEntries(validRequest.state, validRequest.nonce, validRequest.authority, validRequest.loginHint || Constants.EMPTY_STRING, validRequest.account || null);
94
- try {
95
- // Create auth code request and generate PKCE params
96
- this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, request.correlationId);
97
- const authCodeRequest = await this.initializeAuthorizationCodeRequest(validRequest);
98
- // Initialize the client
99
- this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, request.correlationId);
100
- const authClient = await this.createAuthCodeClient(serverTelemetryManager, validRequest.authority, validRequest.azureCloudOptions);
101
- this.logger.verbose("Auth code client created");
102
- const isNativeBroker = NativeMessageHandler.isNativeAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
103
- // Start measurement for server calls with native brokering enabled
104
- let fetchNativeAccountIdMeasurement;
105
- if (isNativeBroker) {
106
- fetchNativeAccountIdMeasurement =
107
- this.performanceClient.startMeasurement(PerformanceEvents.FetchAccountIdWithNativeBroker, request.correlationId);
108
- }
109
- // Create acquire token url.
110
- const navigateUrl = await authClient.getAuthCodeUrl({
111
- ...validRequest,
112
- nativeBroker: isNativeBroker,
113
- });
114
- // Create popup interaction handler.
115
- const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
116
- // Show the UI once the url has been created. Get the window handle for the popup.
117
- const popupParameters = {
118
- popup,
119
- popupName,
120
- popupWindowAttributes,
121
- };
122
- const popupWindow = this.initiateAuthRequest(navigateUrl, popupParameters);
123
- this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow }, null);
124
- // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
125
- const hash = await this.monitorPopupForHash(popupWindow);
126
- // Deserialize hash fragment response parameters.
127
- const serverParams = UrlString.getDeserializedHash(hash);
128
- const state = this.validateAndExtractStateFromHash(serverParams, InteractionType.Popup, validRequest.correlationId);
129
- // Remove throttle if it exists
130
- ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, authCodeRequest);
131
- if (serverParams.accountId) {
132
- this.logger.verbose("Account id found in hash, calling WAM for token");
133
- // end measurement for server call with native brokering enabled
134
- if (fetchNativeAccountIdMeasurement) {
135
- fetchNativeAccountIdMeasurement.endMeasurement({
136
- success: true,
137
- isNativeBroker: true,
138
- });
139
- }
140
- if (!this.nativeMessageHandler) {
141
- throw BrowserAuthError.createNativeConnectionNotEstablishedError();
142
- }
143
- const nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.acquireTokenPopup, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.nativeStorage, validRequest.correlationId);
144
- const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, state);
145
- return nativeInteractionClient
146
- .acquireToken({
147
- ...validRequest,
148
- state: userRequestState,
149
- prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
150
- })
151
- .finally(() => {
152
- this.browserStorage.cleanRequestByState(state);
153
- });
154
- }
155
- // Handle response from hash string.
156
- const result = await interactionHandler.handleCodeResponseFromHash(hash, state, authClient.authority, this.networkClient);
157
- return result;
158
- }
159
- catch (e) {
160
- if (popup) {
161
- // Close the synchronous popup if an error is thrown before the window unload event is registered
162
- popup.close();
163
- }
164
- if (e instanceof AuthError) {
165
- e.setCorrelationId(this.correlationId);
166
- serverTelemetryManager.cacheFailedRequest(e);
167
- }
168
- this.browserStorage.cleanRequestByState(validRequest.state);
169
- throw e;
170
- }
171
- }
172
- /**
173
- *
174
- * @param validRequest
175
- * @param popupName
176
- * @param requestAuthority
177
- * @param popup
178
- * @param mainWindowRedirectUri
179
- * @param popupWindowAttributes
180
- */
181
- async logoutPopupAsync(validRequest, popupName, popupWindowAttributes, requestAuthority, popup, mainWindowRedirectUri) {
182
- this.logger.verbose("logoutPopupAsync called");
183
- this.eventHandler.emitEvent(EventType.LOGOUT_START, InteractionType.Popup, validRequest);
184
- const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.logoutPopup);
185
- try {
186
- // Clear cache on logout
187
- await this.clearCacheOnLogout(validRequest.account);
188
- // Initialize the client
189
- this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, validRequest.correlationId);
190
- const authClient = await this.createAuthCodeClient(serverTelemetryManager, requestAuthority);
191
- this.logger.verbose("Auth code client created");
192
- // Create logout string and navigate user window to logout.
193
- const logoutUri = authClient.getLogoutUri(validRequest);
194
- this.eventHandler.emitEvent(EventType.LOGOUT_SUCCESS, InteractionType.Popup, validRequest);
195
- // Open the popup window to requestUrl.
196
- const popupWindow = this.openPopup(logoutUri, {
197
- popupName,
198
- popupWindowAttributes,
199
- popup,
200
- });
201
- this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow }, null);
202
- await this.waitForLogoutPopup(popupWindow);
203
- if (mainWindowRedirectUri) {
204
- const navigationOptions = {
205
- apiId: ApiId.logoutPopup,
206
- timeout: this.config.system.redirectNavigationTimeout,
207
- noHistory: false,
208
- };
209
- const absoluteUrl = UrlString.getAbsoluteUrl(mainWindowRedirectUri, BrowserUtils.getCurrentUri());
210
- this.logger.verbose("Redirecting main window to url specified in the request");
211
- this.logger.verbosePii(`Redirecting main window to: ${absoluteUrl}`);
212
- this.navigationClient.navigateInternal(absoluteUrl, navigationOptions);
213
- }
214
- else {
215
- this.logger.verbose("No main window navigation requested");
216
- }
217
- }
218
- catch (e) {
219
- if (popup) {
220
- // Close the synchronous popup if an error is thrown before the window unload event is registered
221
- popup.close();
222
- }
223
- if (e instanceof AuthError) {
224
- e.setCorrelationId(this.correlationId);
225
- serverTelemetryManager.cacheFailedRequest(e);
226
- }
227
- this.browserStorage.setInteractionInProgress(false);
228
- this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, InteractionType.Popup, null, e);
229
- this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
230
- throw e;
231
- }
232
- this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
233
- }
234
- /**
235
- * Opens a popup window with given request Url.
236
- * @param requestUrl
237
- */
238
- initiateAuthRequest(requestUrl, params) {
239
- // Check that request url is not empty.
240
- if (!StringUtils.isEmpty(requestUrl)) {
241
- this.logger.infoPii(`Navigate to: ${requestUrl}`);
242
- // Open the popup window to requestUrl.
243
- return this.openPopup(requestUrl, params);
244
- }
245
- else {
246
- // Throw error if request URL is empty.
247
- this.logger.error("Navigate url is empty");
248
- throw BrowserAuthError.createEmptyNavigationUriError();
249
- }
250
- }
251
- /**
252
- * Monitors a window until it loads a url with the same origin.
253
- * @param popupWindow - window that is being monitored
254
- * @param timeout - timeout for processing hash once popup is redirected back to application
255
- */
256
- monitorPopupForHash(popupWindow) {
257
- return new Promise((resolve, reject) => {
258
- /*
259
- * Polling for popups needs to be tick-based,
260
- * since a non-trivial amount of time can be spent on interaction (which should not count against the timeout).
261
- */
262
- const maxTicks = this.config.system.windowHashTimeout /
263
- this.config.system.pollIntervalMilliseconds;
264
- let ticks = 0;
265
- this.logger.verbose("PopupHandler.monitorPopupForHash - polling started");
266
- const intervalId = setInterval(() => {
267
- // Window is closed
268
- if (popupWindow.closed) {
269
- this.logger.error("PopupHandler.monitorPopupForHash - window closed");
270
- this.cleanPopup();
271
- clearInterval(intervalId);
272
- reject(BrowserAuthError.createUserCancelledError());
273
- return;
274
- }
275
- let href = Constants.EMPTY_STRING;
276
- let hash = Constants.EMPTY_STRING;
277
- try {
278
- /*
279
- * Will throw if cross origin,
280
- * which should be caught and ignored
281
- * since we need the interval to keep running while on STS UI.
282
- */
283
- href = popupWindow.location.href;
284
- hash = popupWindow.location.hash;
285
- }
286
- catch (e) { }
287
- // Don't process blank pages or cross domain
288
- if (StringUtils.isEmpty(href) || href === "about:blank") {
289
- return;
290
- }
291
- this.logger.verbose("PopupHandler.monitorPopupForHash - popup window is on same origin as caller");
292
- /*
293
- * Only run clock when we are on same domain for popups
294
- * as popup operations can take a long time.
295
- */
296
- ticks++;
297
- if (hash) {
298
- this.logger.verbose("PopupHandler.monitorPopupForHash - found hash in url");
299
- clearInterval(intervalId);
300
- this.cleanPopup(popupWindow);
301
- if (UrlString.hashContainsKnownProperties(hash)) {
302
- this.logger.verbose("PopupHandler.monitorPopupForHash - hash contains known properties, returning.");
303
- resolve(hash);
304
- }
305
- else {
306
- this.logger.error("PopupHandler.monitorPopupForHash - found hash in url but it does not contain known properties. Check that your router is not changing the hash prematurely.");
307
- this.logger.errorPii(`PopupHandler.monitorPopupForHash - hash found: ${hash}`);
308
- reject(BrowserAuthError.createHashDoesNotContainKnownPropertiesError());
309
- }
310
- }
311
- else if (ticks > maxTicks) {
312
- this.logger.error("PopupHandler.monitorPopupForHash - unable to find hash in url, timing out");
313
- clearInterval(intervalId);
314
- reject(BrowserAuthError.createMonitorPopupTimeoutError());
315
- }
316
- }, this.config.system.pollIntervalMilliseconds);
317
- });
318
- }
319
- /**
320
- * Waits for user interaction in logout popup window
321
- * @param popupWindow
322
- * @returns
323
- */
324
- waitForLogoutPopup(popupWindow) {
325
- return new Promise((resolve) => {
326
- this.logger.verbose("PopupHandler.waitForLogoutPopup - polling started");
327
- const intervalId = setInterval(() => {
328
- // Window is closed
329
- if (popupWindow.closed) {
330
- this.logger.error("PopupHandler.waitForLogoutPopup - window closed");
331
- this.cleanPopup();
332
- clearInterval(intervalId);
333
- resolve();
334
- }
335
- let href = Constants.EMPTY_STRING;
336
- try {
337
- /*
338
- * Will throw if cross origin,
339
- * which should be caught and ignored
340
- * since we need the interval to keep running while on STS UI.
341
- */
342
- href = popupWindow.location.href;
343
- }
344
- catch (e) { }
345
- // Don't process blank pages or cross domain
346
- if (StringUtils.isEmpty(href) || href === "about:blank") {
347
- return;
348
- }
349
- this.logger.verbose("PopupHandler.waitForLogoutPopup - popup window is on same origin as caller, closing.");
350
- clearInterval(intervalId);
351
- this.cleanPopup(popupWindow);
352
- resolve();
353
- }, this.config.system.pollIntervalMilliseconds);
354
- });
355
- }
356
- /**
357
- * @hidden
358
- *
359
- * Configures popup window for login.
360
- *
361
- * @param urlNavigate
362
- * @param title
363
- * @param popUpWidth
364
- * @param popUpHeight
365
- * @param popupWindowAttributes
366
- * @ignore
367
- * @hidden
368
- */
369
- openPopup(urlNavigate, popupParams) {
370
- try {
371
- let popupWindow;
372
- // Popup window passed in, setting url to navigate to
373
- if (popupParams.popup) {
374
- popupWindow = popupParams.popup;
375
- this.logger.verbosePii(`Navigating popup window to: ${urlNavigate}`);
376
- popupWindow.location.assign(urlNavigate);
377
- }
378
- else if (typeof popupParams.popup === "undefined") {
379
- // Popup will be undefined if it was not passed in
380
- this.logger.verbosePii(`Opening popup window to: ${urlNavigate}`);
381
- popupWindow = this.openSizedPopup(urlNavigate, popupParams.popupName, popupParams.popupWindowAttributes);
382
- }
383
- // Popup will be null if popups are blocked
384
- if (!popupWindow) {
385
- throw BrowserAuthError.createEmptyWindowCreatedError();
386
- }
387
- if (popupWindow.focus) {
388
- popupWindow.focus();
389
- }
390
- this.currentWindow = popupWindow;
391
- window.addEventListener("beforeunload", this.unloadWindow);
392
- return popupWindow;
393
- }
394
- catch (e) {
395
- this.logger.error("error opening popup " + e.message);
396
- this.browserStorage.setInteractionInProgress(false);
397
- throw BrowserAuthError.createPopupWindowError(e.toString());
398
- }
399
- }
400
- /**
401
- * Helper function to set popup window dimensions and position
402
- * @param urlNavigate
403
- * @param popupName
404
- * @param popupWindowAttributes
405
- * @returns
406
- */
407
- openSizedPopup(urlNavigate, popupName, popupWindowAttributes) {
408
- /**
409
- * adding winLeft and winTop to account for dual monitor
410
- * using screenLeft and screenTop for IE8 and earlier
411
- */
412
- const winLeft = window.screenLeft ? window.screenLeft : window.screenX;
413
- const winTop = window.screenTop ? window.screenTop : window.screenY;
414
- /**
415
- * window.innerWidth displays browser window"s height and width excluding toolbars
416
- * using document.documentElement.clientWidth for IE8 and earlier
417
- */
418
- const winWidth = window.innerWidth ||
419
- document.documentElement.clientWidth ||
420
- document.body.clientWidth;
421
- const winHeight = window.innerHeight ||
422
- document.documentElement.clientHeight ||
423
- document.body.clientHeight;
424
- let width = popupWindowAttributes.popupSize?.width;
425
- let height = popupWindowAttributes.popupSize?.height;
426
- let top = popupWindowAttributes.popupPosition?.top;
427
- let left = popupWindowAttributes.popupPosition?.left;
428
- if (!width || width < 0 || width > winWidth) {
429
- this.logger.verbose("Default popup window width used. Window width not configured or invalid.");
430
- width = BrowserConstants.POPUP_WIDTH;
431
- }
432
- if (!height || height < 0 || height > winHeight) {
433
- this.logger.verbose("Default popup window height used. Window height not configured or invalid.");
434
- height = BrowserConstants.POPUP_HEIGHT;
435
- }
436
- if (!top || top < 0 || top > winHeight) {
437
- this.logger.verbose("Default popup window top position used. Window top not configured or invalid.");
438
- top = Math.max(0, winHeight / 2 - BrowserConstants.POPUP_HEIGHT / 2 + winTop);
439
- }
440
- if (!left || left < 0 || left > winWidth) {
441
- this.logger.verbose("Default popup window left position used. Window left not configured or invalid.");
442
- left = Math.max(0, winWidth / 2 - BrowserConstants.POPUP_WIDTH / 2 + winLeft);
443
- }
444
- return window.open(urlNavigate, popupName, `width=${width}, height=${height}, top=${top}, left=${left}, scrollbars=yes`);
445
- }
446
- /**
447
- * Event callback to unload main window.
448
- */
449
- unloadWindow(e) {
450
- this.browserStorage.cleanRequestByInteractionType(InteractionType.Popup);
451
- if (this.currentWindow) {
452
- this.currentWindow.close();
453
- }
454
- // Guarantees browser unload will happen, so no other errors will be thrown.
455
- e.preventDefault();
456
- }
457
- /**
458
- * Closes popup, removes any state vars created during popup calls.
459
- * @param popupWindow
460
- */
461
- cleanPopup(popupWindow) {
462
- if (popupWindow) {
463
- // Close window.
464
- popupWindow.close();
465
- }
466
- // Remove window unload function
467
- window.removeEventListener("beforeunload", this.unloadWindow);
468
- // Interaction is completed - remove interaction status.
469
- this.browserStorage.setInteractionInProgress(false);
470
- }
471
- /**
472
- * Generates the name for the popup based on the client id and request
473
- * @param clientId
474
- * @param request
475
- */
476
- generatePopupName(scopes, authority) {
477
- return `${BrowserConstants.POPUP_NAME_PREFIX}.${this.config.auth.clientId}.${scopes.join("-")}.${authority}.${this.correlationId}`;
478
- }
479
- /**
480
- * Generates the name for the popup based on the client id and request for logouts
481
- * @param clientId
482
- * @param request
483
- */
484
- generateLogoutPopupName(request) {
485
- const homeAccountId = request.account && request.account.homeAccountId;
486
- return `${BrowserConstants.POPUP_NAME_PREFIX}.${this.config.auth.clientId}.${homeAccountId}.${this.correlationId}`;
487
- }
13
+ /*
14
+ * Copyright (c) Microsoft Corporation. All rights reserved.
15
+ * Licensed under the MIT License.
16
+ */
17
+ class PopupClient extends StandardInteractionClient {
18
+ constructor(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeStorageImpl, nativeMessageHandler, correlationId) {
19
+ super(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeMessageHandler, correlationId);
20
+ // Properly sets this reference for the unload event.
21
+ this.unloadWindow = this.unloadWindow.bind(this);
22
+ this.nativeStorage = nativeStorageImpl;
23
+ }
24
+ /**
25
+ * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
26
+ * @param request
27
+ */
28
+ acquireToken(request) {
29
+ try {
30
+ const popupName = this.generatePopupName(request.scopes || OIDC_DEFAULT_SCOPES, request.authority || this.config.auth.authority);
31
+ const popupWindowAttributes = request.popupWindowAttributes || {};
32
+ // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
33
+ if (this.config.system.asyncPopups) {
34
+ this.logger.verbose("asyncPopups set to true, acquiring token");
35
+ // Passes on popup position and dimensions if in request
36
+ return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes);
37
+ }
38
+ else {
39
+ // asyncPopups flag is set to false. Opens popup before acquiring token.
40
+ this.logger.verbose("asyncPopup set to false, opening popup before acquiring token");
41
+ const popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
42
+ return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes, popup);
43
+ }
44
+ }
45
+ catch (e) {
46
+ return Promise.reject(e);
47
+ }
48
+ }
49
+ /**
50
+ * Clears local cache for the current user then opens a popup window prompting the user to sign-out of the server
51
+ * @param logoutRequest
52
+ */
53
+ logout(logoutRequest) {
54
+ try {
55
+ this.logger.verbose("logoutPopup called");
56
+ const validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
57
+ const popupName = this.generateLogoutPopupName(validLogoutRequest);
58
+ const authority = logoutRequest && logoutRequest.authority;
59
+ const mainWindowRedirectUri = logoutRequest && logoutRequest.mainWindowRedirectUri;
60
+ const popupWindowAttributes = logoutRequest?.popupWindowAttributes || {};
61
+ // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
62
+ if (this.config.system.asyncPopups) {
63
+ this.logger.verbose("asyncPopups set to true");
64
+ // Passes on popup position and dimensions if in request
65
+ return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, undefined, mainWindowRedirectUri);
66
+ }
67
+ else {
68
+ // asyncPopups flag is set to false. Opens popup before logging out.
69
+ this.logger.verbose("asyncPopup set to false, opening popup");
70
+ const popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
71
+ return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, popup, mainWindowRedirectUri);
72
+ }
73
+ }
74
+ catch (e) {
75
+ // Since this function is synchronous we need to reject
76
+ return Promise.reject(e);
77
+ }
78
+ }
79
+ /**
80
+ * Helper which obtains an access_token for your API via opening a popup window in the user's browser
81
+ * @param validRequest
82
+ * @param popupName
83
+ * @param popup
84
+ * @param popupWindowAttributes
85
+ *
86
+ * @returns A promise that is fulfilled when this function has completed, or rejected if an error was raised.
87
+ */
88
+ async acquireTokenPopupAsync(request, popupName, popupWindowAttributes, popup) {
89
+ this.logger.verbose("acquireTokenPopupAsync called");
90
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
91
+ this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, request.correlationId);
92
+ const validRequest = await this.initializeAuthorizationRequest(request, InteractionType.Popup);
93
+ this.browserStorage.updateCacheEntries(validRequest.state, validRequest.nonce, validRequest.authority, validRequest.loginHint || Constants.EMPTY_STRING, validRequest.account || null);
94
+ try {
95
+ // Create auth code request and generate PKCE params
96
+ this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, request.correlationId);
97
+ const authCodeRequest = await this.initializeAuthorizationCodeRequest(validRequest);
98
+ // Initialize the client
99
+ this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, request.correlationId);
100
+ const authClient = await this.createAuthCodeClient(serverTelemetryManager, validRequest.authority, validRequest.azureCloudOptions);
101
+ this.logger.verbose("Auth code client created");
102
+ const isNativeBroker = NativeMessageHandler.isNativeAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
103
+ // Start measurement for server calls with native brokering enabled
104
+ let fetchNativeAccountIdMeasurement;
105
+ if (isNativeBroker) {
106
+ fetchNativeAccountIdMeasurement =
107
+ this.performanceClient.startMeasurement(PerformanceEvents.FetchAccountIdWithNativeBroker, request.correlationId);
108
+ }
109
+ // Create acquire token url.
110
+ const navigateUrl = await authClient.getAuthCodeUrl({
111
+ ...validRequest,
112
+ nativeBroker: isNativeBroker,
113
+ });
114
+ // Create popup interaction handler.
115
+ const interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
116
+ // Show the UI once the url has been created. Get the window handle for the popup.
117
+ const popupParameters = {
118
+ popup,
119
+ popupName,
120
+ popupWindowAttributes,
121
+ };
122
+ const popupWindow = this.initiateAuthRequest(navigateUrl, popupParameters);
123
+ this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow }, null);
124
+ // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds.
125
+ const hash = await this.monitorPopupForHash(popupWindow);
126
+ // Deserialize hash fragment response parameters.
127
+ const serverParams = UrlString.getDeserializedHash(hash);
128
+ const state = this.validateAndExtractStateFromHash(serverParams, InteractionType.Popup, validRequest.correlationId);
129
+ // Remove throttle if it exists
130
+ ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, authCodeRequest);
131
+ if (serverParams.accountId) {
132
+ this.logger.verbose("Account id found in hash, calling WAM for token");
133
+ // end measurement for server call with native brokering enabled
134
+ if (fetchNativeAccountIdMeasurement) {
135
+ fetchNativeAccountIdMeasurement.endMeasurement({
136
+ success: true,
137
+ isNativeBroker: true,
138
+ });
139
+ }
140
+ if (!this.nativeMessageHandler) {
141
+ throw BrowserAuthError.createNativeConnectionNotEstablishedError();
142
+ }
143
+ const nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.acquireTokenPopup, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.nativeStorage, validRequest.correlationId);
144
+ const { userRequestState } = ProtocolUtils.parseRequestState(this.browserCrypto, state);
145
+ return nativeInteractionClient
146
+ .acquireToken({
147
+ ...validRequest,
148
+ state: userRequestState,
149
+ prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
150
+ })
151
+ .finally(() => {
152
+ this.browserStorage.cleanRequestByState(state);
153
+ });
154
+ }
155
+ // Handle response from hash string.
156
+ const result = await interactionHandler.handleCodeResponseFromHash(hash, state, authClient.authority, this.networkClient);
157
+ return result;
158
+ }
159
+ catch (e) {
160
+ if (popup) {
161
+ // Close the synchronous popup if an error is thrown before the window unload event is registered
162
+ popup.close();
163
+ }
164
+ if (e instanceof AuthError) {
165
+ e.setCorrelationId(this.correlationId);
166
+ serverTelemetryManager.cacheFailedRequest(e);
167
+ }
168
+ this.browserStorage.cleanRequestByState(validRequest.state);
169
+ throw e;
170
+ }
171
+ }
172
+ /**
173
+ *
174
+ * @param validRequest
175
+ * @param popupName
176
+ * @param requestAuthority
177
+ * @param popup
178
+ * @param mainWindowRedirectUri
179
+ * @param popupWindowAttributes
180
+ */
181
+ async logoutPopupAsync(validRequest, popupName, popupWindowAttributes, requestAuthority, popup, mainWindowRedirectUri) {
182
+ this.logger.verbose("logoutPopupAsync called");
183
+ this.eventHandler.emitEvent(EventType.LOGOUT_START, InteractionType.Popup, validRequest);
184
+ const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.logoutPopup);
185
+ try {
186
+ // Clear cache on logout
187
+ await this.clearCacheOnLogout(validRequest.account);
188
+ // Initialize the client
189
+ this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, validRequest.correlationId);
190
+ const authClient = await this.createAuthCodeClient(serverTelemetryManager, requestAuthority);
191
+ this.logger.verbose("Auth code client created");
192
+ // Create logout string and navigate user window to logout.
193
+ const logoutUri = authClient.getLogoutUri(validRequest);
194
+ this.eventHandler.emitEvent(EventType.LOGOUT_SUCCESS, InteractionType.Popup, validRequest);
195
+ // Open the popup window to requestUrl.
196
+ const popupWindow = this.openPopup(logoutUri, {
197
+ popupName,
198
+ popupWindowAttributes,
199
+ popup,
200
+ });
201
+ this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow }, null);
202
+ await this.waitForLogoutPopup(popupWindow);
203
+ if (mainWindowRedirectUri) {
204
+ const navigationOptions = {
205
+ apiId: ApiId.logoutPopup,
206
+ timeout: this.config.system.redirectNavigationTimeout,
207
+ noHistory: false,
208
+ };
209
+ const absoluteUrl = UrlString.getAbsoluteUrl(mainWindowRedirectUri, BrowserUtils.getCurrentUri());
210
+ this.logger.verbose("Redirecting main window to url specified in the request");
211
+ this.logger.verbosePii(`Redirecting main window to: ${absoluteUrl}`);
212
+ this.navigationClient.navigateInternal(absoluteUrl, navigationOptions);
213
+ }
214
+ else {
215
+ this.logger.verbose("No main window navigation requested");
216
+ }
217
+ }
218
+ catch (e) {
219
+ if (popup) {
220
+ // Close the synchronous popup if an error is thrown before the window unload event is registered
221
+ popup.close();
222
+ }
223
+ if (e instanceof AuthError) {
224
+ e.setCorrelationId(this.correlationId);
225
+ serverTelemetryManager.cacheFailedRequest(e);
226
+ }
227
+ this.browserStorage.setInteractionInProgress(false);
228
+ this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, InteractionType.Popup, null, e);
229
+ this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
230
+ throw e;
231
+ }
232
+ this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
233
+ }
234
+ /**
235
+ * Opens a popup window with given request Url.
236
+ * @param requestUrl
237
+ */
238
+ initiateAuthRequest(requestUrl, params) {
239
+ // Check that request url is not empty.
240
+ if (!StringUtils.isEmpty(requestUrl)) {
241
+ this.logger.infoPii(`Navigate to: ${requestUrl}`);
242
+ // Open the popup window to requestUrl.
243
+ return this.openPopup(requestUrl, params);
244
+ }
245
+ else {
246
+ // Throw error if request URL is empty.
247
+ this.logger.error("Navigate url is empty");
248
+ throw BrowserAuthError.createEmptyNavigationUriError();
249
+ }
250
+ }
251
+ /**
252
+ * Monitors a window until it loads a url with the same origin.
253
+ * @param popupWindow - window that is being monitored
254
+ * @param timeout - timeout for processing hash once popup is redirected back to application
255
+ */
256
+ monitorPopupForHash(popupWindow) {
257
+ return new Promise((resolve, reject) => {
258
+ /*
259
+ * Polling for popups needs to be tick-based,
260
+ * since a non-trivial amount of time can be spent on interaction (which should not count against the timeout).
261
+ */
262
+ const maxTicks = this.config.system.windowHashTimeout /
263
+ this.config.system.pollIntervalMilliseconds;
264
+ let ticks = 0;
265
+ this.logger.verbose("PopupHandler.monitorPopupForHash - polling started");
266
+ const intervalId = setInterval(() => {
267
+ // Window is closed
268
+ if (popupWindow.closed) {
269
+ this.logger.error("PopupHandler.monitorPopupForHash - window closed");
270
+ this.cleanPopup();
271
+ clearInterval(intervalId);
272
+ reject(BrowserAuthError.createUserCancelledError());
273
+ return;
274
+ }
275
+ let href = Constants.EMPTY_STRING;
276
+ let hash = Constants.EMPTY_STRING;
277
+ try {
278
+ /*
279
+ * Will throw if cross origin,
280
+ * which should be caught and ignored
281
+ * since we need the interval to keep running while on STS UI.
282
+ */
283
+ href = popupWindow.location.href;
284
+ hash = popupWindow.location.hash;
285
+ }
286
+ catch (e) { }
287
+ // Don't process blank pages or cross domain
288
+ if (StringUtils.isEmpty(href) || href === "about:blank") {
289
+ return;
290
+ }
291
+ this.logger.verbose("PopupHandler.monitorPopupForHash - popup window is on same origin as caller");
292
+ /*
293
+ * Only run clock when we are on same domain for popups
294
+ * as popup operations can take a long time.
295
+ */
296
+ ticks++;
297
+ if (hash) {
298
+ this.logger.verbose("PopupHandler.monitorPopupForHash - found hash in url");
299
+ clearInterval(intervalId);
300
+ this.cleanPopup(popupWindow);
301
+ if (UrlString.hashContainsKnownProperties(hash)) {
302
+ this.logger.verbose("PopupHandler.monitorPopupForHash - hash contains known properties, returning.");
303
+ resolve(hash);
304
+ }
305
+ else {
306
+ this.logger.error("PopupHandler.monitorPopupForHash - found hash in url but it does not contain known properties. Check that your router is not changing the hash prematurely.");
307
+ this.logger.errorPii(`PopupHandler.monitorPopupForHash - hash found: ${hash}`);
308
+ reject(BrowserAuthError.createHashDoesNotContainKnownPropertiesError());
309
+ }
310
+ }
311
+ else if (ticks > maxTicks) {
312
+ this.logger.error("PopupHandler.monitorPopupForHash - unable to find hash in url, timing out");
313
+ clearInterval(intervalId);
314
+ reject(BrowserAuthError.createMonitorPopupTimeoutError());
315
+ }
316
+ }, this.config.system.pollIntervalMilliseconds);
317
+ });
318
+ }
319
+ /**
320
+ * Waits for user interaction in logout popup window
321
+ * @param popupWindow
322
+ * @returns
323
+ */
324
+ waitForLogoutPopup(popupWindow) {
325
+ return new Promise((resolve) => {
326
+ this.logger.verbose("PopupHandler.waitForLogoutPopup - polling started");
327
+ const intervalId = setInterval(() => {
328
+ // Window is closed
329
+ if (popupWindow.closed) {
330
+ this.logger.error("PopupHandler.waitForLogoutPopup - window closed");
331
+ this.cleanPopup();
332
+ clearInterval(intervalId);
333
+ resolve();
334
+ }
335
+ let href = Constants.EMPTY_STRING;
336
+ try {
337
+ /*
338
+ * Will throw if cross origin,
339
+ * which should be caught and ignored
340
+ * since we need the interval to keep running while on STS UI.
341
+ */
342
+ href = popupWindow.location.href;
343
+ }
344
+ catch (e) { }
345
+ // Don't process blank pages or cross domain
346
+ if (StringUtils.isEmpty(href) || href === "about:blank") {
347
+ return;
348
+ }
349
+ this.logger.verbose("PopupHandler.waitForLogoutPopup - popup window is on same origin as caller, closing.");
350
+ clearInterval(intervalId);
351
+ this.cleanPopup(popupWindow);
352
+ resolve();
353
+ }, this.config.system.pollIntervalMilliseconds);
354
+ });
355
+ }
356
+ /**
357
+ * @hidden
358
+ *
359
+ * Configures popup window for login.
360
+ *
361
+ * @param urlNavigate
362
+ * @param title
363
+ * @param popUpWidth
364
+ * @param popUpHeight
365
+ * @param popupWindowAttributes
366
+ * @ignore
367
+ * @hidden
368
+ */
369
+ openPopup(urlNavigate, popupParams) {
370
+ try {
371
+ let popupWindow;
372
+ // Popup window passed in, setting url to navigate to
373
+ if (popupParams.popup) {
374
+ popupWindow = popupParams.popup;
375
+ this.logger.verbosePii(`Navigating popup window to: ${urlNavigate}`);
376
+ popupWindow.location.assign(urlNavigate);
377
+ }
378
+ else if (typeof popupParams.popup === "undefined") {
379
+ // Popup will be undefined if it was not passed in
380
+ this.logger.verbosePii(`Opening popup window to: ${urlNavigate}`);
381
+ popupWindow = this.openSizedPopup(urlNavigate, popupParams.popupName, popupParams.popupWindowAttributes);
382
+ }
383
+ // Popup will be null if popups are blocked
384
+ if (!popupWindow) {
385
+ throw BrowserAuthError.createEmptyWindowCreatedError();
386
+ }
387
+ if (popupWindow.focus) {
388
+ popupWindow.focus();
389
+ }
390
+ this.currentWindow = popupWindow;
391
+ window.addEventListener("beforeunload", this.unloadWindow);
392
+ return popupWindow;
393
+ }
394
+ catch (e) {
395
+ this.logger.error("error opening popup " + e.message);
396
+ this.browserStorage.setInteractionInProgress(false);
397
+ throw BrowserAuthError.createPopupWindowError(e.toString());
398
+ }
399
+ }
400
+ /**
401
+ * Helper function to set popup window dimensions and position
402
+ * @param urlNavigate
403
+ * @param popupName
404
+ * @param popupWindowAttributes
405
+ * @returns
406
+ */
407
+ openSizedPopup(urlNavigate, popupName, popupWindowAttributes) {
408
+ /**
409
+ * adding winLeft and winTop to account for dual monitor
410
+ * using screenLeft and screenTop for IE8 and earlier
411
+ */
412
+ const winLeft = window.screenLeft ? window.screenLeft : window.screenX;
413
+ const winTop = window.screenTop ? window.screenTop : window.screenY;
414
+ /**
415
+ * window.innerWidth displays browser window"s height and width excluding toolbars
416
+ * using document.documentElement.clientWidth for IE8 and earlier
417
+ */
418
+ const winWidth = window.innerWidth ||
419
+ document.documentElement.clientWidth ||
420
+ document.body.clientWidth;
421
+ const winHeight = window.innerHeight ||
422
+ document.documentElement.clientHeight ||
423
+ document.body.clientHeight;
424
+ let width = popupWindowAttributes.popupSize?.width;
425
+ let height = popupWindowAttributes.popupSize?.height;
426
+ let top = popupWindowAttributes.popupPosition?.top;
427
+ let left = popupWindowAttributes.popupPosition?.left;
428
+ if (!width || width < 0 || width > winWidth) {
429
+ this.logger.verbose("Default popup window width used. Window width not configured or invalid.");
430
+ width = BrowserConstants.POPUP_WIDTH;
431
+ }
432
+ if (!height || height < 0 || height > winHeight) {
433
+ this.logger.verbose("Default popup window height used. Window height not configured or invalid.");
434
+ height = BrowserConstants.POPUP_HEIGHT;
435
+ }
436
+ if (!top || top < 0 || top > winHeight) {
437
+ this.logger.verbose("Default popup window top position used. Window top not configured or invalid.");
438
+ top = Math.max(0, winHeight / 2 - BrowserConstants.POPUP_HEIGHT / 2 + winTop);
439
+ }
440
+ if (!left || left < 0 || left > winWidth) {
441
+ this.logger.verbose("Default popup window left position used. Window left not configured or invalid.");
442
+ left = Math.max(0, winWidth / 2 - BrowserConstants.POPUP_WIDTH / 2 + winLeft);
443
+ }
444
+ return window.open(urlNavigate, popupName, `width=${width}, height=${height}, top=${top}, left=${left}, scrollbars=yes`);
445
+ }
446
+ /**
447
+ * Event callback to unload main window.
448
+ */
449
+ unloadWindow(e) {
450
+ this.browserStorage.cleanRequestByInteractionType(InteractionType.Popup);
451
+ if (this.currentWindow) {
452
+ this.currentWindow.close();
453
+ }
454
+ // Guarantees browser unload will happen, so no other errors will be thrown.
455
+ e.preventDefault();
456
+ }
457
+ /**
458
+ * Closes popup, removes any state vars created during popup calls.
459
+ * @param popupWindow
460
+ */
461
+ cleanPopup(popupWindow) {
462
+ if (popupWindow) {
463
+ // Close window.
464
+ popupWindow.close();
465
+ }
466
+ // Remove window unload function
467
+ window.removeEventListener("beforeunload", this.unloadWindow);
468
+ // Interaction is completed - remove interaction status.
469
+ this.browserStorage.setInteractionInProgress(false);
470
+ }
471
+ /**
472
+ * Generates the name for the popup based on the client id and request
473
+ * @param clientId
474
+ * @param request
475
+ */
476
+ generatePopupName(scopes, authority) {
477
+ return `${BrowserConstants.POPUP_NAME_PREFIX}.${this.config.auth.clientId}.${scopes.join("-")}.${authority}.${this.correlationId}`;
478
+ }
479
+ /**
480
+ * Generates the name for the popup based on the client id and request for logouts
481
+ * @param clientId
482
+ * @param request
483
+ */
484
+ generateLogoutPopupName(request) {
485
+ const homeAccountId = request.account && request.account.homeAccountId;
486
+ return `${BrowserConstants.POPUP_NAME_PREFIX}.${this.config.auth.clientId}.${homeAccountId}.${this.correlationId}`;
487
+ }
488
488
  }
489
489
 
490
490
  export { PopupClient };