From 46096bf70aa25b01be3376251a2040dc53db9ac1 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 10:24:16 -0700 Subject: [PATCH 01/13] Fix error condition in SilentIframeClient --- lib/msal-browser/src/interaction_client/SilentIframeClient.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/msal-browser/src/interaction_client/SilentIframeClient.ts b/lib/msal-browser/src/interaction_client/SilentIframeClient.ts index d950eaa755..104828d901 100644 --- a/lib/msal-browser/src/interaction_client/SilentIframeClient.ts +++ b/lib/msal-browser/src/interaction_client/SilentIframeClient.ts @@ -15,7 +15,6 @@ import { PerformanceEvents, invokeAsync, invoke, - ServerError, } from "@azure/msal-common"; import { StandardInteractionClient } from "./StandardInteractionClient"; import { AuthorizationUrlRequest } from "../request/AuthorizationUrlRequest"; @@ -160,7 +159,7 @@ export class SilentIframeClient extends StandardInteractionClient { if ( !authClient || - !(e instanceof ServerError) || + !(e instanceof AuthError) || e.errorCode !== BrowserConstants.INVALID_GRANT_ERROR ) { throw e; From 72c86823c66eddfce50cc8cd6fafc2c55ee93a51 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 11:15:10 -0700 Subject: [PATCH 02/13] Remove popup retry --- .../src/interaction_client/PopupClient.ts | 320 +++++++----------- .../interaction_client/PopupClient.spec.ts | 187 ---------- 2 files changed, 118 insertions(+), 389 deletions(-) diff --git a/lib/msal-browser/src/interaction_client/PopupClient.ts b/lib/msal-browser/src/interaction_client/PopupClient.ts index f93e004b50..4a8be4ef27 100644 --- a/lib/msal-browser/src/interaction_client/PopupClient.ts +++ b/lib/msal-browser/src/interaction_client/PopupClient.ts @@ -20,7 +20,6 @@ import { ServerResponseType, invokeAsync, invoke, - ServerError, } from "@azure/msal-common"; import { StandardInteractionClient } from "./StandardInteractionClient"; import { EventType } from "../event/EventType"; @@ -48,7 +47,6 @@ import { PopupWindowAttributes } from "../request/PopupWindowAttributes"; import { EventError } from "../event/EventMessage"; import { AuthenticationResult } from "../response/AuthenticationResult"; import * as ResponseHandler from "../response/ResponseHandler"; -import { AuthorizationUrlRequest } from "../request/AuthorizationUrlRequest"; export type PopupParams = { popup?: Window | null; @@ -184,7 +182,7 @@ export class PopupClient extends StandardInteractionClient { } /** - * Helper which initializes authorization clients and requests + * Helper which obtains an access_token for your API via opening a popup window in the user's browser * @param validRequest * @param popupName * @param popup @@ -213,20 +211,19 @@ export class PopupClient extends StandardInteractionClient { BrowserUtils.preconnect(validRequest.authority); - let authClient: AuthorizationCodeClient | undefined; - try { // Create auth code request and generate PKCE params - const authCodeRequest = await invokeAsync( - this.initializeAuthorizationCodeRequest.bind(this), - PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, - this.logger, - this.performanceClient, - this.correlationId - )(validRequest); + const authCodeRequest: CommonAuthorizationCodeRequest = + await invokeAsync( + this.initializeAuthorizationCodeRequest.bind(this), + PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, + this.logger, + this.performanceClient, + this.correlationId + )(validRequest); // Initialize the client - authClient = await invokeAsync( + const authClient: AuthorizationCodeClient = await invokeAsync( this.createAuthCodeClient.bind(this), PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, this.logger, @@ -239,218 +236,137 @@ export class PopupClient extends StandardInteractionClient { validRequest.account ); - return await invokeAsync( - this.acquireTokenPopupAsyncHelper.bind(this), - PerformanceEvents.PopupClientTokenHelper, + const isNativeBroker = NativeMessageHandler.isNativeAvailable( + this.config, this.logger, - this.performanceClient, - this.correlationId - )( - authClient, - authCodeRequest, - validRequest, - request, - popupName, - popupWindowAttributes, - popup + this.nativeMessageHandler, + request.authenticationScheme ); - } catch (e) { - if (popup) { - // Close the synchronous popup if an error is thrown before the window unload event is registered - popup.close(); + // Start measurement for server calls with native brokering enabled + let fetchNativeAccountIdMeasurement; + if (isNativeBroker) { + fetchNativeAccountIdMeasurement = + this.performanceClient.startMeasurement( + PerformanceEvents.FetchAccountIdWithNativeBroker, + request.correlationId + ); } - if (e instanceof AuthError) { - (e as AuthError).setCorrelationId(this.correlationId); - serverTelemetryManager.cacheFailedRequest(e); - } + // Create acquire token url. + const navigateUrl = await authClient.getAuthCodeUrl({ + ...validRequest, + nativeBroker: isNativeBroker, + }); - if ( - !authClient || - !(e instanceof ServerError) || - e.errorCode !== BrowserConstants.INVALID_GRANT_ERROR - ) { - throw e; - } + // Create popup interaction handler. + const interactionHandler = new InteractionHandler( + authClient, + this.browserStorage, + authCodeRequest, + this.logger, + this.performanceClient + ); - this.performanceClient.addFields( - { - retryError: e.errorCode, - }, - this.correlationId + // Show the UI once the url has been created. Get the window handle for the popup. + const popupParameters: PopupParams = { + popup, + popupName, + popupWindowAttributes, + }; + const popupWindow: Window = this.initiateAuthRequest( + navigateUrl, + popupParameters + ); + this.eventHandler.emitEvent( + EventType.POPUP_OPENED, + InteractionType.Popup, + { popupWindow }, + null ); - const retryAuthCodeRequest = await invokeAsync( - this.initializeAuthorizationCodeRequest.bind(this), - PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, - this.logger, - this.performanceClient, - this.correlationId - )(validRequest); + // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds. + const responseString = await this.monitorPopupForHash(popupWindow); - return await invokeAsync( - this.acquireTokenPopupAsyncHelper.bind(this), - PerformanceEvents.PopupClientTokenHelper, + const serverParams = invoke( + ResponseHandler.deserializeResponse, + PerformanceEvents.DeserializeResponse, this.logger, this.performanceClient, this.correlationId )( - authClient, - retryAuthCodeRequest, - validRequest, - request, - popupName, - popupWindowAttributes, - popup + responseString, + this.config.auth.OIDCOptions.serverResponseType, + this.logger + ); + // Remove throttle if it exists + ThrottlingUtils.removeThrottle( + this.browserStorage, + this.config.auth.clientId, + authCodeRequest ); - } - } - - /** - * Helper which obtains an access_token for your API via opening a popup window in the user's browser - * @param authClient - * @param authCodeRequest - * @param validRequest - * @param request - * @param popupName - * @param popupWindowAttributes - * @param popup - * @returns A promise that is fulfilled when this function has completed, or rejected if an error was raised. - */ - protected async acquireTokenPopupAsyncHelper( - authClient: AuthorizationCodeClient, - authCodeRequest: CommonAuthorizationCodeRequest, - validRequest: AuthorizationUrlRequest, - request: PopupRequest, - popupName: string, - popupWindowAttributes: PopupWindowAttributes, - popup?: Window | null - ): Promise { - const correlationId = validRequest.correlationId; - this.performanceClient.addQueueMeasurement( - PerformanceEvents.PopupClientTokenHelper, - correlationId - ); - - const isNativeBroker = NativeMessageHandler.isNativeAvailable( - this.config, - this.logger, - this.nativeMessageHandler, - request.authenticationScheme - ); - // Start measurement for server calls with native brokering enabled - let fetchNativeAccountIdMeasurement; - if (isNativeBroker) { - fetchNativeAccountIdMeasurement = - this.performanceClient.startMeasurement( - PerformanceEvents.FetchAccountIdWithNativeBroker, - request.correlationId + if (serverParams.accountId) { + this.logger.verbose( + "Account id found in hash, calling WAM for token" ); - } - - // Create acquire token url. - const navigateUrl = await authClient.getAuthCodeUrl({ - ...validRequest, - nativeBroker: isNativeBroker, - }); - - // Create popup interaction handler. - const interactionHandler = new InteractionHandler( - authClient, - this.browserStorage, - authCodeRequest, - this.logger, - this.performanceClient - ); - - // Show the UI once the url has been created. Get the window handle for the popup. - const popupParameters: PopupParams = { - popup, - popupName, - popupWindowAttributes, - }; - const popupWindow: Window = this.initiateAuthRequest( - navigateUrl, - popupParameters - ); - this.eventHandler.emitEvent( - EventType.POPUP_OPENED, - InteractionType.Popup, - { popupWindow }, - null - ); - - // Monitor the window for the hash. Return the string value and close the popup when the hash is received. Default timeout is 60 seconds. - const responseString = await this.monitorPopupForHash(popupWindow); - - const serverParams = invoke( - ResponseHandler.deserializeResponse, - PerformanceEvents.DeserializeResponse, - this.logger, - this.performanceClient, - this.correlationId - )( - responseString, - this.config.auth.OIDCOptions.serverResponseType, - this.logger - ); - // Remove throttle if it exists - ThrottlingUtils.removeThrottle( - this.browserStorage, - this.config.auth.clientId, - authCodeRequest - ); + // end measurement for server call with native brokering enabled + if (fetchNativeAccountIdMeasurement) { + fetchNativeAccountIdMeasurement.end({ + success: true, + isNativeBroker: true, + }); + } - if (serverParams.accountId) { - this.logger.verbose( - "Account id found in hash, calling WAM for token" - ); - // end measurement for server call with native brokering enabled - if (fetchNativeAccountIdMeasurement) { - fetchNativeAccountIdMeasurement.end({ - success: true, - isNativeBroker: true, + if (!this.nativeMessageHandler) { + throw createBrowserAuthError( + BrowserAuthErrorCodes.nativeConnectionNotEstablished + ); + } + 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 + ); + const { userRequestState } = ProtocolUtils.parseRequestState( + this.browserCrypto, + validRequest.state + ); + return await nativeInteractionClient.acquireToken({ + ...validRequest, + state: userRequestState, + prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently }); } - if (!this.nativeMessageHandler) { - throw createBrowserAuthError( - BrowserAuthErrorCodes.nativeConnectionNotEstablished - ); - } - 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 - ); - const { userRequestState } = ProtocolUtils.parseRequestState( - this.browserCrypto, - validRequest.state + // Handle response from hash string. + const result = await interactionHandler.handleCodeResponse( + serverParams, + validRequest ); - return nativeInteractionClient.acquireToken({ - ...validRequest, - state: userRequestState, - prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently - }); - } - // Handle response from hash string. - const result = await interactionHandler.handleCodeResponse( - serverParams, - validRequest - ); + return result; + } catch (e) { + if (popup) { + // Close the synchronous popup if an error is thrown before the window unload event is registered + popup.close(); + } - return result; + if (e instanceof AuthError) { + (e as AuthError).setCorrelationId(this.correlationId); + serverTelemetryManager.cacheFailedRequest(e); + } + + throw e; + } } /** diff --git a/lib/msal-browser/test/interaction_client/PopupClient.spec.ts b/lib/msal-browser/test/interaction_client/PopupClient.spec.ts index 43621e588f..df8f318165 100644 --- a/lib/msal-browser/test/interaction_client/PopupClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/PopupClient.spec.ts @@ -18,7 +18,6 @@ import { TEST_SSH_VALUES, TEST_TOKEN_RESPONSE, ID_TOKEN_CLAIMS, - calculateExpiresDate, } from "../utils/StringConstants"; import { Constants, @@ -38,7 +37,6 @@ import { NetworkManager, ProtocolUtils, ProtocolMode, - TenantProfile, } from "@azure/msal-common"; import { TemporaryCacheKeys, @@ -775,191 +773,6 @@ describe("PopupClient", () => { expect(e).toEqual(testError); } }); - - it("retries on invalid_grant error and returns successful response", async () => { - const testServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - suberror: "first_server_error", - }, - status: 200, - }; - const testServerTokenResponse = { - token_type: TEST_CONFIG.TOKEN_TYPE_BEARER, - scope: TEST_CONFIG.DEFAULT_SCOPES.join(" "), - expires_in: TEST_TOKEN_LIFETIMES.DEFAULT_EXPIRES_IN, - ext_expires_in: TEST_TOKEN_LIFETIMES.DEFAULT_EXPIRES_IN, - access_token: TEST_TOKENS.ACCESS_TOKEN, - refresh_token: TEST_TOKENS.REFRESH_TOKEN, - id_token: TEST_TOKENS.IDTOKEN_V2, - }; - const testServerResponse = { - headers: {}, - body: testServerTokenResponse, - status: 200, - }; - const testAccount: AccountInfo = { - homeAccountId: ID_TOKEN_CLAIMS.sub, - environment: "login.windows.net", - tenantId: ID_TOKEN_CLAIMS.tid, - username: ID_TOKEN_CLAIMS.preferred_username, - localAccountId: TEST_DATA_CLIENT_INFO.TEST_UID, - name: ID_TOKEN_CLAIMS.name, - nativeAccountId: undefined, - authorityType: "MSSTS", - tenantProfiles: new Map([ - [ - ID_TOKEN_CLAIMS.tid, - { - isHomeTenant: false, - localAccountId: TEST_DATA_CLIENT_INFO.TEST_UID, - name: ID_TOKEN_CLAIMS.name, - tenantId: ID_TOKEN_CLAIMS.tid, - }, - ], - ]), - idTokenClaims: ID_TOKEN_CLAIMS, - idToken: TEST_TOKENS.IDTOKEN_V2, - }; - const testTokenResponse: AuthenticationResult = { - authority: TEST_CONFIG.validAuthority, - uniqueId: ID_TOKEN_CLAIMS.oid, - tenantId: ID_TOKEN_CLAIMS.tid, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - idToken: testServerTokenResponse.id_token, - idTokenClaims: ID_TOKEN_CLAIMS, - accessToken: testServerTokenResponse.access_token, - fromCache: false, - fromNativeBroker: false, - code: undefined, - correlationId: TEST_CONFIG.CORRELATION_ID, - expiresOn: calculateExpiresDate( - testServerTokenResponse.expires_in - ), - extExpiresOn: calculateExpiresDate( - testServerTokenResponse.expires_in + - testServerTokenResponse.ext_expires_in - ), - account: testAccount, - tokenType: AuthenticationScheme.BEARER, - refreshOn: undefined, - requestId: "", - familyId: "", - state: TEST_STATE_VALUES.USER_STATE, - msGraphHost: "", - cloudGraphHostName: "", - }; - jest.spyOn( - AuthorizationCodeClient.prototype, - "getAuthCodeUrl" - ).mockResolvedValue(testNavUrl); - jest.spyOn( - PopupClient.prototype, - "initiateAuthRequest" - ).mockImplementation((requestUrl: string): Window => { - expect(requestUrl).toEqual(testNavUrl); - return window; - }); - jest.spyOn( - PopupClient.prototype, - "monitorPopupForHash" - ).mockResolvedValue(TEST_HASHES.TEST_SUCCESS_CODE_HASH_POPUP); - const sendPostRequestSpy = jest - .spyOn(NetworkManager.prototype, "sendPostRequest") - .mockResolvedValueOnce(testServerErrorResponse) - .mockResolvedValueOnce(testServerResponse); - jest.spyOn(PkceGenerator, "generatePkceCodes").mockResolvedValue({ - challenge: TEST_CONFIG.TEST_CHALLENGE, - verifier: TEST_CONFIG.TEST_VERIFIER, - }); - jest.spyOn(BrowserCrypto, "createNewGuid").mockReturnValue( - RANDOM_TEST_GUID - ); - - const result = await popupClient.acquireToken({ - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - nonce: "123523", - state: TEST_STATE_VALUES.USER_STATE, - }); - - expect(result).toEqual(testTokenResponse); - expect(sendPostRequestSpy).toHaveBeenCalledTimes(2); - expect(sendPostRequestSpy).toHaveNthReturnedWith( - 1, - Promise.resolve(testServerErrorResponse) - ); - }); - - it("retries on invalid_grant error once and throws if still error", async () => { - const testFirstServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - suberror: "first_server_error", - }, - status: 200, - }; - const testSecondServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - suberror: "second_server_error", - }, - status: 200, - }; - jest.spyOn( - AuthorizationCodeClient.prototype, - "getAuthCodeUrl" - ).mockResolvedValue(testNavUrl); - jest.spyOn( - PopupClient.prototype, - "initiateAuthRequest" - ).mockImplementation((requestUrl: string): Window => { - expect(requestUrl).toEqual(testNavUrl); - return window; - }); - jest.spyOn( - PopupClient.prototype, - "monitorPopupForHash" - ).mockResolvedValue(TEST_HASHES.TEST_SUCCESS_CODE_HASH_POPUP); - const sendPostRequestSpy = jest - .spyOn(NetworkManager.prototype, "sendPostRequest") - .mockResolvedValueOnce(testFirstServerErrorResponse) - .mockResolvedValueOnce(testSecondServerErrorResponse); - jest.spyOn(PkceGenerator, "generatePkceCodes").mockResolvedValue({ - challenge: TEST_CONFIG.TEST_CHALLENGE, - verifier: TEST_CONFIG.TEST_VERIFIER, - }); - jest.spyOn(BrowserCrypto, "createNewGuid").mockReturnValue( - RANDOM_TEST_GUID - ); - - await popupClient - .acquireToken({ - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - state: TEST_STATE_VALUES.USER_STATE, - }) - .catch((e) => { - expect(e.errorCode).toEqual( - BrowserConstants.INVALID_GRANT_ERROR - ); - expect(e.subError).toEqual("second_server_error"); - expect(sendPostRequestSpy).toHaveBeenCalledTimes(2); - expect(sendPostRequestSpy).toHaveNthReturnedWith( - 1, - Promise.resolve(testFirstServerErrorResponse) - ); - }); - }); }); describe("logout", () => { From 259d80a58a9081eb9e2abfd61bb86ac6150d0c31 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 11:17:03 -0700 Subject: [PATCH 03/13] Remove popup performanceevent --- .../src/telemetry/performance/PerformanceEvent.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts index 04380721de..711f54a32f 100644 --- a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts +++ b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts @@ -189,11 +189,6 @@ export const PerformanceEvents = { InitializeClientApplication: "initializeClientApplication", - /** - * Helper function in PopupClient class (msal-browser). - */ - PopupClientTokenHelper: "popupClientTokenHelper", - /** * Helper function in SilentIframeClient class (msal-browser). */ From 8cd9a39a72ae0048b9a0606fa14854414f261ae5 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 12:05:02 -0700 Subject: [PATCH 04/13] Remove redirect retry --- lib/msal-browser/docs/configuration.md | 1 - .../src/cache/BrowserCacheManager.ts | 95 ---- lib/msal-browser/src/config/Configuration.ts | 10 +- .../src/controllers/StandardController.ts | 41 +- .../src/error/BrowserAuthError.ts | 2 - .../src/error/BrowserAuthErrorCodes.ts | 1 - .../src/interaction_client/RedirectClient.ts | 63 +-- .../interaction_handler/RedirectHandler.ts | 1 - .../src/request/RedirectRequest.ts | 5 - .../src/utils/BrowserConstants.ts | 2 - .../test/app/PublicClientApplication.spec.ts | 46 -- .../test/cache/BrowserCacheManager.spec.ts | 133 ------ .../interaction_client/RedirectClient.spec.ts | 429 ------------------ .../RedirectHandler.spec.ts | 26 -- 14 files changed, 15 insertions(+), 840 deletions(-) diff --git a/lib/msal-browser/docs/configuration.md b/lib/msal-browser/docs/configuration.md index 31fb3fa8f2..f6b1a54c2c 100644 --- a/lib/msal-browser/docs/configuration.md +++ b/lib/msal-browser/docs/configuration.md @@ -89,7 +89,6 @@ const msalInstance = new PublicClientApplication(msalConfig); | `protocolMode` | Enum representing the protocol mode to use. If `"AAD"`, will function on the OIDC-compliant AAD v2 endpoints; if `"OIDC"`, will function on other OIDC-compliant endpoints. | string | `"AAD"` | | `azureCloudOptions` | A defined set of azure cloud options for developers to default to their specific cloud authorities, for specific clouds supported please refer to the [AzureCloudInstance](https://aka.ms/msaljs/azure_cloud_instance) | [AzureCloudOptions](https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_common.html#azurecloudoptions) | [AzureCloudInstance.None](msaljs/azure_cloud_instance) | | `skipAuthorityMetadataCache` | A flag to choose whether to use the local metadata cache during authority initialization. Metadata cache would be used if no authority metadata is provided and before a network call for metadata has been made (see [Authority](../../msal-common/docs/authority.md)) | boolean | `false` | -| `onRedirectNavigate` | A callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. ### Cache Config Options diff --git a/lib/msal-browser/src/cache/BrowserCacheManager.ts b/lib/msal-browser/src/cache/BrowserCacheManager.ts index 2906978945..f6b5939d48 100644 --- a/lib/msal-browser/src/cache/BrowserCacheManager.ts +++ b/lib/msal-browser/src/cache/BrowserCacheManager.ts @@ -1552,9 +1552,6 @@ export class BrowserCacheManager extends CacheManager { this.removeTemporaryItem( this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST) ); - this.removeTemporaryItem( - this.generateCacheKey(TemporaryCacheKeys.REDIRECT_REQUEST) - ); this.setInteractionInProgress(false); } @@ -1616,98 +1613,6 @@ export class BrowserCacheManager extends CacheManager { this.setInteractionInProgress(false); } - /** - * Create request retry key to cache retry status - */ - generateRequestRetriedKey(): string { - return `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${this.clientId}`; - } - - /** - * Gets the request retry value from the cache - */ - getRequestRetried(): number | null { - const requestRetriedKey = this.generateRequestRetriedKey(); - const cachedRetryNumber = this.getTemporaryCache(requestRetriedKey); - if (!cachedRetryNumber) { - return null; - } - return parseInt(cachedRetryNumber); - } - - /** - * Sets the request retry value to "retried" in the cache - */ - setRequestRetried(): void { - this.logger.trace("BrowserCacheManager.setRequestRetried called"); - const requestRetriedKey = this.generateRequestRetriedKey(); - this.setTemporaryCache(requestRetriedKey, "1", false); - } - - /** - * Removes all request retry values in the cache - */ - removeRequestRetried(): void { - const requestRetriedKey = this.generateRequestRetriedKey(); - this.removeTemporaryItem(requestRetriedKey); - } - - /** - * Caches the redirectRequest in the cache - * @param redirectRequest - */ - cacheRedirectRequest(redirectRequest: RedirectRequest): void { - this.logger.trace("BrowserCacheManager.cacheRedirectRequest called"); - const { ...restParams } = redirectRequest; - delete restParams.onRedirectNavigate; - const encodedValue = JSON.stringify(restParams); - - this.setTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST, - encodedValue, - true - ); - } - - /** - * Gets redirect request from the cache. Logs an error and returns undefined if nothing is found. - */ - getCachedRedirectRequest(): RedirectRequest | undefined { - this.logger.trace( - "BrowserCacheManager.getCachedRedirectRequest called" - ); - const cachedRedirectRequest = this.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST, - true - ); - if (!cachedRedirectRequest) { - this.logger.error(`No cached redirect request found.`); - } else { - this.removeTemporaryItem( - this.generateCacheKey(TemporaryCacheKeys.REDIRECT_REQUEST) - ); - let parsedRequest: RedirectRequest; - try { - parsedRequest = JSON.parse( - cachedRedirectRequest - ) as RedirectRequest; - } catch (e) { - this.logger.errorPii( - `Attempted to parse: ${cachedRedirectRequest}` - ); - this.logger.error( - `Parsing cached redirect request threw with error: ${e}` - ); - return; - } - - if (parsedRequest) { - return parsedRequest; - } - } - return; - } - cacheCodeRequest(authCodeRequest: CommonAuthorizationCodeRequest): void { this.logger.trace("BrowserCacheManager.cacheCodeRequest called"); diff --git a/lib/msal-browser/src/config/Configuration.ts b/lib/msal-browser/src/config/Configuration.ts index 2f66680073..df5370648d 100644 --- a/lib/msal-browser/src/config/Configuration.ts +++ b/lib/msal-browser/src/config/Configuration.ts @@ -99,19 +99,11 @@ export type BrowserAuthOptions = { * @deprecated This flag is deprecated and will be removed in the next major version. createNestablePublicClientApplication should be used instead. */ supportsNestedAppAuth?: boolean; - /** - * Callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. - */ - onRedirectNavigate?: (url: string) => boolean | void; }; /** @internal */ -export type InternalAuthOptions = Omit< - Required, - "onRedirectNavigate" -> & { +export type InternalAuthOptions = Required & { OIDCOptions: Required; - onRedirectNavigate?: (url: string) => boolean | void; }; /** diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index 5270c86056..cb57939aa2 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -586,35 +586,18 @@ export class StandardController implements IController { // Override on request only if set, as onRedirectNavigate field is deprecated const onRedirectNavigateCb = request.onRedirectNavigate; - if (onRedirectNavigateCb) { - request.onRedirectNavigate = (url: string) => { - const navigate = - typeof onRedirectNavigateCb === "function" - ? onRedirectNavigateCb(url) - : undefined; - if (navigate !== false) { - atrMeasurement.end({ success: true }); - } else { - atrMeasurement.discard(); - } - return navigate; - }; - } else { - const configOnRedirectNavigateCb = - this.config.auth.onRedirectNavigate; - this.config.auth.onRedirectNavigate = (url: string) => { - const navigate = - typeof configOnRedirectNavigateCb === "function" - ? configOnRedirectNavigateCb(url) - : undefined; - if (navigate !== false) { - atrMeasurement.end({ success: true }); - } else { - atrMeasurement.discard(); - } - return navigate; - }; - } + request.onRedirectNavigate = (url: string) => { + const navigate = + typeof onRedirectNavigateCb === "function" + ? onRedirectNavigateCb(url) + : undefined; + if (navigate !== false) { + atrMeasurement.end({ success: true }); + } else { + atrMeasurement.discard(); + } + return navigate; + }; // If logged in, emit acquire token events const isLoggedIn = this.getAllAccounts().length > 0; diff --git a/lib/msal-browser/src/error/BrowserAuthError.ts b/lib/msal-browser/src/error/BrowserAuthError.ts index fcb43d3db1..483f03d33b 100644 --- a/lib/msal-browser/src/error/BrowserAuthError.ts +++ b/lib/msal-browser/src/error/BrowserAuthError.ts @@ -92,8 +92,6 @@ export const BrowserAuthErrorMessages = { "Invalid base64 encoded string.", [BrowserAuthErrorCodes.invalidPopTokenRequest]: "Invalid PoP token request. The request should not have both a popKid value and signPopToken set to true.", - [BrowserAuthErrorCodes.failedToRetry]: - "Unable to retry failed auth code redemption due to usage of the onRedirectNavigate request parameter. Please set onRedirectNavigate on the PublicClientApplication configuration instead or call loginRedirect again.", }; /** diff --git a/lib/msal-browser/src/error/BrowserAuthErrorCodes.ts b/lib/msal-browser/src/error/BrowserAuthErrorCodes.ts index 2c0a1c873e..ac099a1db8 100644 --- a/lib/msal-browser/src/error/BrowserAuthErrorCodes.ts +++ b/lib/msal-browser/src/error/BrowserAuthErrorCodes.ts @@ -56,4 +56,3 @@ export const uninitializedPublicClientApplication = export const nativePromptNotSupported = "native_prompt_not_supported"; export const invalidBase64String = "invalid_base64_string"; export const invalidPopTokenRequest = "invalid_pop_token_request"; -export const failedToRetry = "failed_to_retry"; diff --git a/lib/msal-browser/src/interaction_client/RedirectClient.ts b/lib/msal-browser/src/interaction_client/RedirectClient.ts index b723dcf056..9a5fe6675f 100644 --- a/lib/msal-browser/src/interaction_client/RedirectClient.ts +++ b/lib/msal-browser/src/interaction_client/RedirectClient.ts @@ -22,12 +22,10 @@ import { ServerResponseType, UrlUtils, InProgressPerformanceEvent, - ServerError, } from "@azure/msal-common"; import { StandardInteractionClient } from "./StandardInteractionClient"; import { ApiId, - BrowserConstants, InteractionType, TemporaryCacheKeys, } from "../utils/BrowserConstants"; @@ -85,14 +83,6 @@ export class RedirectClient extends StandardInteractionClient { * @param request */ async acquireToken(request: RedirectRequest): Promise { - if (request.onRedirectNavigate) { - this.logger.warning( - "Unable to cache redirect request, onRedirectNavigate request option has been deprecated. Please set onRedirectNavigate on PublicClientApplication config instead." - ); - } else { - this.browserStorage.cacheRedirectRequest(request); - } - const validRequest = await invokeAsync( this.initializeAuthorizationRequest.bind(this), PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, @@ -184,9 +174,7 @@ export class RedirectClient extends StandardInteractionClient { navigationClient: this.navigationClient, redirectTimeout: this.config.system.redirectNavigationTimeout, redirectStartPage: redirectStartPage, - onRedirectNavigate: - request.onRedirectNavigate || - this.config.auth.onRedirectNavigate, + onRedirectNavigate: request.onRedirectNavigate, }); } catch (e) { if (e instanceof AuthError) { @@ -344,57 +332,10 @@ export class RedirectClient extends StandardInteractionClient { (e as AuthError).setCorrelationId(this.correlationId); serverTelemetryManager.cacheFailedRequest(e); } - - if ( - e instanceof ServerError && - e.errorCode === BrowserConstants.INVALID_GRANT_ERROR - ) { - this.performanceClient.addFields( - { - retryError: e.errorCode, - }, - this.correlationId - ); - - const requestRetried = this.browserStorage.getRequestRetried(); - - if (requestRetried) { - this.logger.error( - "Retried request already detected. Throwing error." - ); - this.browserStorage.removeRequestRetried(); - throw e; - } - - const redirectRequest = - this.browserStorage.getCachedRedirectRequest(); - if (!redirectRequest) { - this.logger.error( - "Unable to retry. Please retry with redirect request" - ); - this.browserStorage.setRequestRetried(); - throw createBrowserAuthError( - BrowserAuthErrorCodes.failedToRetry - ); - } - - this.browserStorage.setRequestRetried(); - - await this.acquireToken(redirectRequest); - return null; - } - - this.browserStorage.removeTemporaryItem( - this.browserStorage.generateCacheKey( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ); - this.browserStorage.removeRequestRetried(); - throw e; - } finally { this.browserStorage.cleanRequestByInteractionType( InteractionType.Redirect ); + throw e; } } diff --git a/lib/msal-browser/src/interaction_handler/RedirectHandler.ts b/lib/msal-browser/src/interaction_handler/RedirectHandler.ts index 46f527ad00..b16a3d7699 100644 --- a/lib/msal-browser/src/interaction_handler/RedirectHandler.ts +++ b/lib/msal-browser/src/interaction_handler/RedirectHandler.ts @@ -222,7 +222,6 @@ export class RedirectHandler { )) as AuthenticationResult; this.browserStorage.cleanRequestByState(state); - this.browserStorage.removeRequestRetried(); return tokenResponse; } diff --git a/lib/msal-browser/src/request/RedirectRequest.ts b/lib/msal-browser/src/request/RedirectRequest.ts index 2b51527d47..54ef92dd74 100644 --- a/lib/msal-browser/src/request/RedirectRequest.ts +++ b/lib/msal-browser/src/request/RedirectRequest.ts @@ -46,11 +46,6 @@ export type RedirectRequest = Partial< > & { scopes: Array; redirectStartPage?: string; - /** - * @deprecated - * onRedirectNavigate is deprecated and will be removed in the next major version. - * Set onRedirectNavigate in Configuration instead. - */ onRedirectNavigate?: (url: string) => boolean | void; tokenBodyParameters?: StringDict; }; diff --git a/lib/msal-browser/src/utils/BrowserConstants.ts b/lib/msal-browser/src/utils/BrowserConstants.ts index 6b797a6483..10c2043773 100644 --- a/lib/msal-browser/src/utils/BrowserConstants.ts +++ b/lib/msal-browser/src/utils/BrowserConstants.ts @@ -93,8 +93,6 @@ export const TemporaryCacheKeys = { CORRELATION_ID: "request.correlationId", NATIVE_REQUEST: "request.native", REDIRECT_CONTEXT: "request.redirect.context", - REDIRECT_REQUEST: "request.redirect", - REQUEST_RETRY: "request.retry", } as const; export type TemporaryCacheKeys = (typeof TemporaryCacheKeys)[keyof typeof TemporaryCacheKeys]; diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index 26b953a040..2bf8b219f7 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -1900,52 +1900,6 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { pca.acquireTokenRedirect(loginRequest); }); - it("emits pre-redirect telemetry event when onRedirectNavigate callback is set in configuration", async () => { - const onRedirectNavigate = (url: string) => { - expect(url).toBeDefined(); - }; - - pca = new PublicClientApplication({ - auth: { - clientId: TEST_CONFIG.MSAL_CLIENT_ID, - onRedirectNavigate, - }, - telemetry: { - client: new BrowserPerformanceClient(testAppConfig), - application: { - appName: TEST_CONFIG.applicationName, - appVersion: TEST_CONFIG.applicationVersion, - }, - }, - }); - pca = (pca as any).controller; - await pca.initialize(); - - const callbackId = pca.addPerformanceCallback((events) => { - expect(events[0].success).toBe(true); - expect(events[0].name).toBe( - PerformanceEvents.AcquireTokenPreRedirect - ); - pca.removePerformanceCallback(callbackId); - }); - - jest.spyOn( - NavigationClient.prototype, - "navigateExternal" - ).mockImplementation(() => Promise.resolve(true)); - - jest.spyOn(PkceGenerator, "generatePkceCodes").mockResolvedValue({ - challenge: TEST_CONFIG.TEST_CHALLENGE, - verifier: TEST_CONFIG.TEST_VERIFIER, - }); - const loginRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: ["user.read", "openid", "profile"], - state: TEST_STATE_VALUES.USER_STATE, - }; - await pca.acquireTokenRedirect(loginRequest); - }); - it("discards pre-redirect telemetry event when onRedirectNavigate callback returns false", async () => { const onRedirectNavigate = (url: string) => { return false; diff --git a/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts b/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts index 4a4b539337..fbe2e25bc3 100644 --- a/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts +++ b/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts @@ -52,7 +52,6 @@ import { BrowserStateObject } from "../../src/utils/BrowserProtocolUtils"; import { base64Decode } from "../../src/encode/Base64Decode"; import { getDefaultPerformanceClient } from "../utils/TelemetryUtils"; import { BrowserPerformanceClient } from "../../src/telemetry/BrowserPerformanceClient"; -import { RedirectRequest } from "../../src/request/RedirectRequest"; describe("BrowserCacheManager tests", () => { let cacheConfig: Required; @@ -2973,138 +2972,6 @@ describe("BrowserCacheManager tests", () => { ).toBeUndefined(); }); - it("generateRequestRetriedKey() creates a valid cache key for request retry", () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - const requestRetriedKey = - browserStorage.generateRequestRetriedKey(); - expect(requestRetriedKey).toBe( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}` - ); - }); - - it("getRequestRetried() retrieves the request retry value from cache", () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - browserStorage.setTemporaryCache( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}`, - "1" - ); - - browserStorage.getRequestRetried(); - - expect(browserStorage.getRequestRetried()).toEqual(1); - }); - - it("setRequestRetried() sets a request retry value for client Id", () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - - browserStorage.setRequestRetried(); - - expect( - window.sessionStorage[ - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}` - ] - ).toBeTruthy(); - }); - - it("removeRequestRetried() removes request retried value for clientId", () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - browserStorage.setTemporaryCache( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}`, - "1" - ); - - browserStorage.removeRequestRetried(); - - expect(browserStorage.getRequestRetried()).toEqual(null); - }); - - it("Successfully retrieves redirect request from cache", async () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - - browserStorage.setTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST, - JSON.stringify(testRedirectRequest), - true - ); - - const cachedRequest = browserStorage.getCachedRedirectRequest(); - expect(cachedRequest).toEqual(testRedirectRequest); - }); - - it("Returns undefined if redirect cannot be retrieved from cache", async () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - - const cachedRequest = browserStorage.getCachedRedirectRequest(); - expect(cachedRequest).toBeUndefined(); - }); - - it("Returns undefined if cached redirect request cannot be parsed correctly", async () => { - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - logger - ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - const stringifiedRequest = JSON.stringify(testRedirectRequest); - browserStorage.setTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST, - stringifiedRequest.substring(0, stringifiedRequest.length / 2), - true - ); - const cachedRequest = browserStorage.getCachedRedirectRequest(); - expect(cachedRequest).toBeUndefined(); - }); - it("Successfully retrieves and decodes response from cache", async () => { const browserStorage = new BrowserCacheManager( TEST_CONFIG.MSAL_CLIENT_ID, diff --git a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts index 34944d0976..ebddf43f54 100644 --- a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts @@ -51,7 +51,6 @@ import { IdTokenEntity, CredentialType, InProgressPerformanceEvent, - StringUtils, } from "@azure/msal-common"; import * as BrowserUtils from "../../src/utils/BrowserUtils"; import { @@ -165,8 +164,6 @@ describe("RedirectClient", () => { pca.performanceClient, //@ts-ignore pca.nativeInternalStorage, - undefined, - TEST_CONFIG.CORRELATION_ID ); rootMeasurement = new BrowserPerformanceClient( @@ -474,20 +471,6 @@ describe("RedirectClient", () => { `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`, "123523" ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REDIRECT_REQUEST}`, - JSON.stringify(testRedirectRequest) - ); const testTokenReq: CommonAuthorizationCodeRequest = { redirectUri: `${TEST_URIS.DEFAULT_INSTANCE}/`, code: "thisIsATestCode", @@ -572,12 +555,6 @@ describe("RedirectClient", () => { testTokenResponse.expiresOn.getMilliseconds() >= tokenResponse.expiresOn.getMilliseconds() ).toBeTruthy(); - expect( - browserStorage.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ).toEqual(null); - expect(browserStorage.getRequestRetried()).toEqual(null); }); it("gets hash from cache and calls native broker if hash contains accountId", async () => { @@ -1850,342 +1827,6 @@ describe("RedirectClient", () => { }); redirectClient.handleRedirectPromise("", rootMeasurement); }); - - it("retries on invalid_grant error when onRedirectNavigate is set in config", async () => { - let pca = new PublicClientApplication({ - auth: { - clientId: TEST_CONFIG.MSAL_CLIENT_ID, - onRedirectNavigate: (url: string) => {}, - }, - }); - - //PCA implementation moved to controller - pca = (pca as any).controller; - - // @ts-ignore - redirectClient = new RedirectClient( - // @ts-ignore - pca.config, - // @ts-ignore - pca.browserStorage, - // @ts-ignore - pca.browserCrypto, - // @ts-ignore - pca.logger, - // @ts-ignore - pca.eventHandler, - // @ts-ignore - pca.navigationClient, - // @ts-ignore - pca.performanceClient, - // @ts-ignore - pca.nativeInternalStorage, - undefined, - TEST_CONFIG.CORRELATION_ID - ); - - const stateString = TEST_STATE_VALUES.TEST_STATE_REDIRECT; - const browserCrypto = new CryptoOps(new Logger({})); - const stateId = ProtocolUtils.parseRequestState( - browserCrypto, - stateString - ).libraryState.id; - - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.ORIGIN_URI}`, - TEST_URIS.TEST_REDIR_URI - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.AUTHORITY}.${stateId}`, - TEST_CONFIG.validAuthority - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`, - TEST_STATE_VALUES.TEST_STATE_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.URL_HASH}`, - TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`, - TEST_CONFIG.MSAL_CLIENT_ID - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`, - "123523" - ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REDIRECT_REQUEST}`, - JSON.stringify(testRedirectRequest) - ); - const testTokenReq: CommonAuthorizationCodeRequest = { - redirectUri: `${TEST_URIS.DEFAULT_INSTANCE}/`, - code: "thisIsATestCode", - scopes: TEST_CONFIG.DEFAULT_SCOPES, - codeVerifier: TEST_CONFIG.TEST_VERIFIER, - authority: `${Constants.DEFAULT_AUTHORITY}`, - correlationId: TEST_CONFIG.CORRELATION_ID, - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_PARAMS}`, - base64Encode(JSON.stringify(testTokenReq)) - ); - const testServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - }, - status: 200, - }; - - jest.spyOn( - FetchClient.prototype, - "sendGetRequestAsync" - ).mockImplementation((url): any => { - if (url.includes("discovery/instance")) { - return DEFAULT_TENANT_DISCOVERY_RESPONSE; - } else if (url.includes(".well-known/openid-configuration")) { - return DEFAULT_OPENID_CONFIG_RESPONSE; - } - }); - - jest.spyOn( - FetchClient.prototype, - "sendPostRequestAsync" - ).mockResolvedValueOnce(testServerErrorResponse); - - const acquireTokenSpy = jest.spyOn(redirectClient, "acquireToken"); - - const tokenResponse = await redirectClient.handleRedirectPromise( - "", - rootMeasurement - ); - - expect(tokenResponse).toBe(null); - expect(acquireTokenSpy).toHaveBeenCalledTimes(1); - expect( - browserStorage.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ).toEqual(null); - expect(browserStorage.getRequestRetried()).toEqual(1); - }); - - it("throws invalid_grant error if already retried", (done) => { - const stateString = TEST_STATE_VALUES.TEST_STATE_REDIRECT; - const browserCrypto = new CryptoOps(new Logger({})); - const stateId = ProtocolUtils.parseRequestState( - browserCrypto, - stateString - ).libraryState.id; - - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.ORIGIN_URI}`, - TEST_URIS.TEST_REDIR_URI - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.AUTHORITY}.${stateId}`, - TEST_CONFIG.validAuthority - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`, - TEST_STATE_VALUES.TEST_STATE_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.URL_HASH}`, - TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`, - TEST_CONFIG.MSAL_CLIENT_ID - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`, - "123523" - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}`, - JSON.stringify(1) - ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REDIRECT_REQUEST}`, - JSON.stringify(testRedirectRequest) - ); - const testTokenReq: CommonAuthorizationCodeRequest = { - redirectUri: `${TEST_URIS.DEFAULT_INSTANCE}/`, - code: "thisIsATestCode", - scopes: TEST_CONFIG.DEFAULT_SCOPES, - codeVerifier: TEST_CONFIG.TEST_VERIFIER, - authority: `${Constants.DEFAULT_AUTHORITY}`, - correlationId: TEST_CONFIG.CORRELATION_ID, - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_PARAMS}`, - base64Encode(JSON.stringify(testTokenReq)) - ); - const testServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - }, - status: 200, - }; - - jest.spyOn( - FetchClient.prototype, - "sendGetRequestAsync" - ).mockImplementation((url): any => { - if (url.includes("discovery/instance")) { - return DEFAULT_TENANT_DISCOVERY_RESPONSE; - } else if (url.includes(".well-known/openid-configuration")) { - return DEFAULT_OPENID_CONFIG_RESPONSE; - } - }); - - jest.spyOn( - FetchClient.prototype, - "sendPostRequestAsync" - ).mockResolvedValueOnce(testServerErrorResponse); - - const acquireTokenSpy = jest.spyOn(redirectClient, "acquireToken"); - - redirectClient - .handleRedirectPromise("", rootMeasurement) - .catch((err) => { - expect(err instanceof ServerError).toBeTruthy(); - expect(err.errorCode).toEqual("invalid_grant"); - expect(acquireTokenSpy).toHaveBeenCalledTimes(0); - - expect( - browserStorage.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ).toEqual(null); - expect(browserStorage.getRequestRetried()).toEqual(null); - - done(); - }); - }); - - it("throws failed_to_retry if invalid_grant is returned from server and redirect request is not cached", (done) => { - const stateString = TEST_STATE_VALUES.TEST_STATE_REDIRECT; - const browserCrypto = new CryptoOps(new Logger({})); - const stateId = ProtocolUtils.parseRequestState( - browserCrypto, - stateString - ).libraryState.id; - - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.ORIGIN_URI}`, - TEST_URIS.TEST_REDIR_URI - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.AUTHORITY}.${stateId}`, - TEST_CONFIG.validAuthority - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`, - TEST_STATE_VALUES.TEST_STATE_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.URL_HASH}`, - TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`, - TEST_CONFIG.MSAL_CLIENT_ID - ); - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`, - "123523" - ); - const testTokenReq: CommonAuthorizationCodeRequest = { - redirectUri: `${TEST_URIS.DEFAULT_INSTANCE}/`, - code: "thisIsATestCode", - scopes: TEST_CONFIG.DEFAULT_SCOPES, - codeVerifier: TEST_CONFIG.TEST_VERIFIER, - authority: `${Constants.DEFAULT_AUTHORITY}`, - correlationId: TEST_CONFIG.CORRELATION_ID, - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - window.sessionStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REQUEST_PARAMS}`, - base64Encode(JSON.stringify(testTokenReq)) - ); - const testServerErrorResponse = { - headers: {}, - body: { - error: "invalid_grant", - error_description: "invalid_grant", - error_codes: ["invalid_grant"], - }, - status: 200, - }; - - jest.spyOn( - FetchClient.prototype, - "sendGetRequestAsync" - ).mockImplementation((url): any => { - if (url.includes("discovery/instance")) { - return DEFAULT_TENANT_DISCOVERY_RESPONSE; - } else if (url.includes(".well-known/openid-configuration")) { - return DEFAULT_OPENID_CONFIG_RESPONSE; - } - }); - - jest.spyOn( - FetchClient.prototype, - "sendPostRequestAsync" - ).mockResolvedValueOnce(testServerErrorResponse); - - const acquireTokenSpy = jest.spyOn(redirectClient, "acquireToken"); - - redirectClient - .handleRedirectPromise("", rootMeasurement) - .catch((err) => { - expect(err instanceof AuthError).toBeTruthy(); - expect(err.errorCode).toEqual("failed_to_retry"); - expect(acquireTokenSpy).toHaveBeenCalledTimes(0); - - expect( - browserStorage.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ).toEqual(null); - expect(browserStorage.getRequestRetried()).toEqual(1); - done(); - }); - }); }); describe("acquireToken", () => { @@ -2420,13 +2061,6 @@ describe("RedirectClient", () => { ) ) ).toEqual(null); - expect( - browserStorage.getTemporaryCache( - browserStorage.generateCacheKey( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ) - ).toEqual(null); done(); return Promise.resolve(true); } @@ -2585,69 +2219,6 @@ describe("RedirectClient", () => { ).toEqual(JSON.stringify(testCcsCred)); }); - it("Caches redirect request correctly", async () => { - const redirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: RANDOM_TEST_GUID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - - const browserCrypto = new CryptoOps(new Logger({})); - const testLogger = new Logger(loggerOptions); - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - testLogger - ); - await redirectClient.acquireToken(redirectRequest); - const cachedRequest: RedirectRequest = JSON.parse( - browserStorage.getTemporaryCache( - TemporaryCacheKeys.REDIRECT_REQUEST, - true - ) || "" - ); - expect(cachedRequest.scopes).toEqual(TEST_CONFIG.DEFAULT_SCOPES); - expect(cachedRequest.authority).toEqual( - `${Constants.DEFAULT_AUTHORITY}` - ); - expect(cachedRequest.correlationId).toEqual(RANDOM_TEST_GUID); - expect(cachedRequest.authenticationScheme).toEqual( - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme - ); - }); - - it("Does not cache redirect request if onRedirectNavigate is set", async () => { - const redirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: RANDOM_TEST_GUID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - onRedirectNavigate: (url: string) => {}, - }; - - const browserCrypto = new CryptoOps(new Logger({})); - const testLogger = new Logger(loggerOptions); - const browserStorage = new BrowserCacheManager( - TEST_CONFIG.MSAL_CLIENT_ID, - cacheConfig, - browserCrypto, - testLogger - ); - await redirectClient.acquireToken(redirectRequest); - const cachedRequest = browserStorage.getCachedRedirectRequest(); - expect(cachedRequest).toBeUndefined(); - }); - it("Caches token request correctly", async () => { const tokenRequest: CommonAuthorizationUrlRequest = { redirectUri: TEST_URIS.TEST_REDIR_URI, diff --git a/lib/msal-browser/test/interaction_handler/RedirectHandler.spec.ts b/lib/msal-browser/test/interaction_handler/RedirectHandler.spec.ts index 2288fc1d91..60c973b155 100644 --- a/lib/msal-browser/test/interaction_handler/RedirectHandler.spec.ts +++ b/lib/msal-browser/test/interaction_handler/RedirectHandler.spec.ts @@ -445,24 +445,6 @@ describe("RedirectHandler.ts Unit Tests", () => { browserStorage.generateCacheKey(TemporaryCacheKeys.URL_HASH), TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT ); - browserStorage.setItem( - `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.REQUEST_RETRY}.${TEST_CONFIG.MSAL_CLIENT_ID}`, - JSON.stringify(1) - ); - const testRedirectRequest: RedirectRequest = { - redirectUri: TEST_URIS.TEST_REDIR_URI, - scopes: TEST_CONFIG.DEFAULT_SCOPES, - correlationId: TEST_CONFIG.CORRELATION_ID, - state: TEST_STATE_VALUES.USER_STATE, - authority: TEST_CONFIG.validAuthority, - nonce: "", - authenticationScheme: - TEST_CONFIG.TOKEN_TYPE_BEARER as AuthenticationScheme, - }; - browserStorage.setItem( - `${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.REDIRECT_REQUEST}`, - JSON.stringify(testRedirectRequest) - ); sinon .stub( AuthorizationCodeClient.prototype, @@ -500,14 +482,6 @@ describe("RedirectHandler.ts Unit Tests", () => { browserStorage.generateCacheKey(TemporaryCacheKeys.URL_HASH) ) ).toBe(null); - expect( - browserStorage.getTemporaryCache( - browserStorage.generateCacheKey( - TemporaryCacheKeys.REDIRECT_REQUEST - ) - ) - ).toBe(null); - expect(browserStorage.getRequestRetried()).toBe(null); }); it("successfully handles response adds CCS credential to auth code request", async () => { From eb0b41cbaa7b3ce42fa87192dc328becca03ce60 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 12:07:31 -0700 Subject: [PATCH 05/13] Change files --- ...-msal-browser-85cc015e-a981-4a3a-931b-39e4ef1c980c.json | 7 +++++++ ...e-msal-common-dd361954-4720-4d1f-a6a2-4750c47d3673.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@azure-msal-browser-85cc015e-a981-4a3a-931b-39e4ef1c980c.json create mode 100644 change/@azure-msal-common-dd361954-4720-4d1f-a6a2-4750c47d3673.json diff --git a/change/@azure-msal-browser-85cc015e-a981-4a3a-931b-39e4ef1c980c.json b/change/@azure-msal-browser-85cc015e-a981-4a3a-931b-39e4ef1c980c.json new file mode 100644 index 0000000000..f0d55d3c59 --- /dev/null +++ b/change/@azure-msal-browser-85cc015e-a981-4a3a-931b-39e4ef1c980c.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Remove retry for popup and redirect #7270", + "packageName": "@azure/msal-browser", + "email": "joarroyo@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-msal-common-dd361954-4720-4d1f-a6a2-4750c47d3673.json b/change/@azure-msal-common-dd361954-4720-4d1f-a6a2-4750c47d3673.json new file mode 100644 index 0000000000..7d919a706c --- /dev/null +++ b/change/@azure-msal-common-dd361954-4720-4d1f-a6a2-4750c47d3673.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Remove PerformanceEvent for PopupTokenHelper #7270", + "packageName": "@azure/msal-common", + "email": "joarroyo@microsoft.com", + "dependentChangeType": "patch" +} From 2a27aedabbac87955366816776c8f97584629115 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 12:20:03 -0700 Subject: [PATCH 06/13] Update API Extractor report --- lib/msal-browser/apiReview/msal-browser.api.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/msal-browser/apiReview/msal-browser.api.md b/lib/msal-browser/apiReview/msal-browser.api.md index faa073e16e..7750818ecf 100644 --- a/lib/msal-browser/apiReview/msal-browser.api.md +++ b/lib/msal-browser/apiReview/msal-browser.api.md @@ -235,8 +235,7 @@ declare namespace BrowserAuthErrorCodes { uninitializedPublicClientApplication, nativePromptNotSupported, invalidBase64String, - invalidPopTokenRequest, - failedToRetry + invalidPopTokenRequest } } export { BrowserAuthErrorCodes } @@ -449,7 +448,6 @@ export type BrowserAuthOptions = { azureCloudOptions?: AzureCloudOptions; skipAuthorityMetadataCache?: boolean; supportsNestedAppAuth?: boolean; - onRedirectNavigate?: (url: string) => boolean | void; }; // Warning: (ae-missing-release-tag) "BrowserCacheLocation" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -840,11 +838,6 @@ export { ExternalTokenResponse } // @public (undocumented) const failedToParseResponse = "failed_to_parse_response"; -// Warning: (ae-missing-release-tag) "failedToRetry" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -const failedToRetry = "failed_to_retry"; - // Warning: (ae-missing-release-tag) "getCurrentUri" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -1679,7 +1672,7 @@ const userCancelled = "user_cancelled"; // Warning: (ae-missing-release-tag) "version" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const version = "3.20.0"; +export const version = "3.21.0"; // Warning: (ae-missing-release-tag) "WrapperSKU" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "WrapperSKU" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1706,7 +1699,7 @@ export type WrapperSKU = (typeof WrapperSKU)[keyof typeof WrapperSKU]; // src/app/PublicClientNext.ts:81:79 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" // src/app/PublicClientNext.ts:84:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/app/PublicClientNext.ts:85:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/config/Configuration.ts:241:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts +// src/config/Configuration.ts:233:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts // src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // src/index.ts:8:4 - (tsdoc-undefined-tag) The TSDoc tag "@module" is not defined in this configuration // src/navigation/NavigationClient.ts:36:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen From 70ea49d6ddb015605041dbd584737f00535883f7 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 13:37:28 -0700 Subject: [PATCH 07/13] Update API Extractor report for msal-common --- lib/msal-common/apiReview/msal-common.api.md | 179 +++++++++---------- 1 file changed, 89 insertions(+), 90 deletions(-) diff --git a/lib/msal-common/apiReview/msal-common.api.md b/lib/msal-common/apiReview/msal-common.api.md index f4e20e5c74..90c2d6608c 100644 --- a/lib/msal-common/apiReview/msal-common.api.md +++ b/lib/msal-common/apiReview/msal-common.api.md @@ -3093,7 +3093,6 @@ export const PerformanceEvents: { readonly InitializeBaseRequest: "initializeBaseRequest"; readonly InitializeSilentRequest: "initializeSilentRequest"; readonly InitializeClientApplication: "initializeClientApplication"; - readonly PopupClientTokenHelper: "popupClientTokenHelper"; readonly SilentIframeClientTokenHelper: "silentIframeClientTokenHelper"; readonly SilentHandlerInitiateAuthRequest: "silentHandlerInitiateAuthRequest"; readonly SilentHandlerMonitorIframeForHash: "silentHandlerMonitorIframeForHash"; @@ -4147,7 +4146,7 @@ export type ValidCredentialType = IdTokenEntity | AccessTokenEntity | RefreshTok // Warning: (ae-missing-release-tag) "version" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const version = "14.14.0"; +export const version = "14.14.1"; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -4291,93 +4290,93 @@ const X_MS_LIB_CAPABILITY = "x-ms-lib-capability"; // src/telemetry/performance/PerformanceClient.ts:898:27 - (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' // src/telemetry/performance/PerformanceClient.ts:899:24 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag // src/telemetry/performance/PerformanceClient.ts:899:17 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:565:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:565:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:565:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:572:37 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:572:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:572:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:579:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:579:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:579:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:586:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:586:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:586:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:593:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:593:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:593:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:599:8 - (tsdoc-undefined-tag) The TSDoc tag "@date" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:601:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:601:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:601:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:609:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:609:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:609:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:616:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:616:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:616:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:623:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:623:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:623:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:631:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:631:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:631:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:638:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:638:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:638:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:645:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:645:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:645:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:652:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:652:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:652:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:659:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:659:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:659:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:671:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:671:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:671:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:678:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:678:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:678:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:685:23 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:685:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:685:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:692:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:692:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:692:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:699:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:699:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:699:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:705:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:705:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:705:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:712:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:712:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:712:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:731:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:731:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:731:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:737:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:737:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:737:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:744:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:744:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:744:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:752:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:752:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:752:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:761:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:761:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:761:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:769:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:769:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:769:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:776:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:776:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:776:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:839:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/telemetry/performance/PerformanceEvent.ts:839:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:839:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:560:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:560:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:560:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:567:37 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:567:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:567:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:574:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:574:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:574:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:581:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:581:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:581:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:588:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:588:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:588:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:594:8 - (tsdoc-undefined-tag) The TSDoc tag "@date" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:596:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:596:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:596:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:604:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:604:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:604:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:611:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:611:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:611:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:618:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:618:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:618:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:626:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:626:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:626:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:633:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:633:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:633:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:640:31 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:640:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:640:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:647:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:647:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:647:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:654:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:654:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:654:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:666:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:666:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:666:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:673:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:673:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:673:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:680:23 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:680:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:680:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:687:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:687:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:687:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:694:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:694:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:694:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:700:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:700:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:700:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:707:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:707:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:707:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:726:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:726:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:726:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:732:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:732:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:732:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:739:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:739:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:739:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:747:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:747:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:747:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:756:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:756:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:756:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:764:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:764:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:764:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:771:22 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:771:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:771:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:834:21 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/telemetry/performance/PerformanceEvent.ts:834:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:834:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration ``` From 9c02afb65be9c030bb7187586e37f4c103361f58 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 13:37:33 -0700 Subject: [PATCH 08/13] Format:fix --- lib/msal-browser/test/interaction_client/RedirectClient.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts index ebddf43f54..e6933b8b26 100644 --- a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts @@ -163,7 +163,7 @@ describe("RedirectClient", () => { //@ts-ignore pca.performanceClient, //@ts-ignore - pca.nativeInternalStorage, + pca.nativeInternalStorage ); rootMeasurement = new BrowserPerformanceClient( From 4d951d2fb2cd3db93dc195b3a46802e053a31a70 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 13:49:15 -0700 Subject: [PATCH 09/13] Update API Extractor report for msal-node --- lib/msal-node/apiReview/msal-node.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-node/apiReview/msal-node.api.md b/lib/msal-node/apiReview/msal-node.api.md index fb2eb18f57..ea6c783f70 100644 --- a/lib/msal-node/apiReview/msal-node.api.md +++ b/lib/msal-node/apiReview/msal-node.api.md @@ -732,7 +732,7 @@ export { ValidCacheType } // Warning: (ae-missing-release-tag) "version" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const version = "2.12.0"; +export const version = "2.13.0"; // Warnings were encountered during analysis: // From 40684a43231f65d18c4f2f7c62efbc997264966d Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 16:16:33 -0700 Subject: [PATCH 10/13] Revert onRedirectNavigate removal --- .../apiReview/msal-browser.api.md | 3 +- lib/msal-browser/docs/configuration.md | 1 + lib/msal-browser/src/config/Configuration.ts | 10 +++- .../src/controllers/StandardController.ts | 41 ++++++++++++----- .../src/interaction_client/RedirectClient.ts | 4 +- .../src/request/RedirectRequest.ts | 5 ++ .../test/app/PublicClientApplication.spec.ts | 46 +++++++++++++++++++ 7 files changed, 95 insertions(+), 15 deletions(-) diff --git a/lib/msal-browser/apiReview/msal-browser.api.md b/lib/msal-browser/apiReview/msal-browser.api.md index 7750818ecf..3480a43163 100644 --- a/lib/msal-browser/apiReview/msal-browser.api.md +++ b/lib/msal-browser/apiReview/msal-browser.api.md @@ -448,6 +448,7 @@ export type BrowserAuthOptions = { azureCloudOptions?: AzureCloudOptions; skipAuthorityMetadataCache?: boolean; supportsNestedAppAuth?: boolean; + onRedirectNavigate?: (url: string) => boolean | void; }; // Warning: (ae-missing-release-tag) "BrowserCacheLocation" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1699,7 +1700,7 @@ export type WrapperSKU = (typeof WrapperSKU)[keyof typeof WrapperSKU]; // src/app/PublicClientNext.ts:81:79 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" // src/app/PublicClientNext.ts:84:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/app/PublicClientNext.ts:85:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/config/Configuration.ts:233:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts +// src/config/Configuration.ts:241:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts // src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // src/index.ts:8:4 - (tsdoc-undefined-tag) The TSDoc tag "@module" is not defined in this configuration // src/navigation/NavigationClient.ts:36:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen diff --git a/lib/msal-browser/docs/configuration.md b/lib/msal-browser/docs/configuration.md index f6b1a54c2c..084fe79fa2 100644 --- a/lib/msal-browser/docs/configuration.md +++ b/lib/msal-browser/docs/configuration.md @@ -89,6 +89,7 @@ const msalInstance = new PublicClientApplication(msalConfig); | `protocolMode` | Enum representing the protocol mode to use. If `"AAD"`, will function on the OIDC-compliant AAD v2 endpoints; if `"OIDC"`, will function on other OIDC-compliant endpoints. | string | `"AAD"` | | `azureCloudOptions` | A defined set of azure cloud options for developers to default to their specific cloud authorities, for specific clouds supported please refer to the [AzureCloudInstance](https://aka.ms/msaljs/azure_cloud_instance) | [AzureCloudOptions](https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_common.html#azurecloudoptions) | [AzureCloudInstance.None](msaljs/azure_cloud_instance) | | `skipAuthorityMetadataCache` | A flag to choose whether to use the local metadata cache during authority initialization. Metadata cache would be used if no authority metadata is provided and before a network call for metadata has been made (see [Authority](../../msal-common/docs/authority.md)) | boolean | `false` | +| `onRedirectNavigate` | A callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. ### Cache Config Options diff --git a/lib/msal-browser/src/config/Configuration.ts b/lib/msal-browser/src/config/Configuration.ts index df5370648d..2f66680073 100644 --- a/lib/msal-browser/src/config/Configuration.ts +++ b/lib/msal-browser/src/config/Configuration.ts @@ -99,11 +99,19 @@ export type BrowserAuthOptions = { * @deprecated This flag is deprecated and will be removed in the next major version. createNestablePublicClientApplication should be used instead. */ supportsNestedAppAuth?: boolean; + /** + * Callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. + */ + onRedirectNavigate?: (url: string) => boolean | void; }; /** @internal */ -export type InternalAuthOptions = Required & { +export type InternalAuthOptions = Omit< + Required, + "onRedirectNavigate" +> & { OIDCOptions: Required; + onRedirectNavigate?: (url: string) => boolean | void; }; /** diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index cb57939aa2..5270c86056 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -586,18 +586,35 @@ export class StandardController implements IController { // Override on request only if set, as onRedirectNavigate field is deprecated const onRedirectNavigateCb = request.onRedirectNavigate; - request.onRedirectNavigate = (url: string) => { - const navigate = - typeof onRedirectNavigateCb === "function" - ? onRedirectNavigateCb(url) - : undefined; - if (navigate !== false) { - atrMeasurement.end({ success: true }); - } else { - atrMeasurement.discard(); - } - return navigate; - }; + if (onRedirectNavigateCb) { + request.onRedirectNavigate = (url: string) => { + const navigate = + typeof onRedirectNavigateCb === "function" + ? onRedirectNavigateCb(url) + : undefined; + if (navigate !== false) { + atrMeasurement.end({ success: true }); + } else { + atrMeasurement.discard(); + } + return navigate; + }; + } else { + const configOnRedirectNavigateCb = + this.config.auth.onRedirectNavigate; + this.config.auth.onRedirectNavigate = (url: string) => { + const navigate = + typeof configOnRedirectNavigateCb === "function" + ? configOnRedirectNavigateCb(url) + : undefined; + if (navigate !== false) { + atrMeasurement.end({ success: true }); + } else { + atrMeasurement.discard(); + } + return navigate; + }; + } // If logged in, emit acquire token events const isLoggedIn = this.getAllAccounts().length > 0; diff --git a/lib/msal-browser/src/interaction_client/RedirectClient.ts b/lib/msal-browser/src/interaction_client/RedirectClient.ts index 9a5fe6675f..71bba1e269 100644 --- a/lib/msal-browser/src/interaction_client/RedirectClient.ts +++ b/lib/msal-browser/src/interaction_client/RedirectClient.ts @@ -174,7 +174,9 @@ export class RedirectClient extends StandardInteractionClient { navigationClient: this.navigationClient, redirectTimeout: this.config.system.redirectNavigationTimeout, redirectStartPage: redirectStartPage, - onRedirectNavigate: request.onRedirectNavigate, + onRedirectNavigate: + request.onRedirectNavigate || + this.config.auth.onRedirectNavigate, }); } catch (e) { if (e instanceof AuthError) { diff --git a/lib/msal-browser/src/request/RedirectRequest.ts b/lib/msal-browser/src/request/RedirectRequest.ts index 54ef92dd74..2b51527d47 100644 --- a/lib/msal-browser/src/request/RedirectRequest.ts +++ b/lib/msal-browser/src/request/RedirectRequest.ts @@ -46,6 +46,11 @@ export type RedirectRequest = Partial< > & { scopes: Array; redirectStartPage?: string; + /** + * @deprecated + * onRedirectNavigate is deprecated and will be removed in the next major version. + * Set onRedirectNavigate in Configuration instead. + */ onRedirectNavigate?: (url: string) => boolean | void; tokenBodyParameters?: StringDict; }; diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index 2bf8b219f7..26b953a040 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -1900,6 +1900,52 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { pca.acquireTokenRedirect(loginRequest); }); + it("emits pre-redirect telemetry event when onRedirectNavigate callback is set in configuration", async () => { + const onRedirectNavigate = (url: string) => { + expect(url).toBeDefined(); + }; + + pca = new PublicClientApplication({ + auth: { + clientId: TEST_CONFIG.MSAL_CLIENT_ID, + onRedirectNavigate, + }, + telemetry: { + client: new BrowserPerformanceClient(testAppConfig), + application: { + appName: TEST_CONFIG.applicationName, + appVersion: TEST_CONFIG.applicationVersion, + }, + }, + }); + pca = (pca as any).controller; + await pca.initialize(); + + const callbackId = pca.addPerformanceCallback((events) => { + expect(events[0].success).toBe(true); + expect(events[0].name).toBe( + PerformanceEvents.AcquireTokenPreRedirect + ); + pca.removePerformanceCallback(callbackId); + }); + + jest.spyOn( + NavigationClient.prototype, + "navigateExternal" + ).mockImplementation(() => Promise.resolve(true)); + + jest.spyOn(PkceGenerator, "generatePkceCodes").mockResolvedValue({ + challenge: TEST_CONFIG.TEST_CHALLENGE, + verifier: TEST_CONFIG.TEST_VERIFIER, + }); + const loginRequest: RedirectRequest = { + redirectUri: TEST_URIS.TEST_REDIR_URI, + scopes: ["user.read", "openid", "profile"], + state: TEST_STATE_VALUES.USER_STATE, + }; + await pca.acquireTokenRedirect(loginRequest); + }); + it("discards pre-redirect telemetry event when onRedirectNavigate callback returns false", async () => { const onRedirectNavigate = (url: string) => { return false; From 3d26d29362d7ed702320d77f42069a5b80187744 Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Fri, 23 Aug 2024 16:27:41 -0700 Subject: [PATCH 11/13] Fix spacing --- lib/msal-browser/docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-browser/docs/configuration.md b/lib/msal-browser/docs/configuration.md index 084fe79fa2..31fb3fa8f2 100644 --- a/lib/msal-browser/docs/configuration.md +++ b/lib/msal-browser/docs/configuration.md @@ -89,7 +89,7 @@ const msalInstance = new PublicClientApplication(msalConfig); | `protocolMode` | Enum representing the protocol mode to use. If `"AAD"`, will function on the OIDC-compliant AAD v2 endpoints; if `"OIDC"`, will function on other OIDC-compliant endpoints. | string | `"AAD"` | | `azureCloudOptions` | A defined set of azure cloud options for developers to default to their specific cloud authorities, for specific clouds supported please refer to the [AzureCloudInstance](https://aka.ms/msaljs/azure_cloud_instance) | [AzureCloudOptions](https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_common.html#azurecloudoptions) | [AzureCloudInstance.None](msaljs/azure_cloud_instance) | | `skipAuthorityMetadataCache` | A flag to choose whether to use the local metadata cache during authority initialization. Metadata cache would be used if no authority metadata is provided and before a network call for metadata has been made (see [Authority](../../msal-common/docs/authority.md)) | boolean | `false` | -| `onRedirectNavigate` | A callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. +| `onRedirectNavigate` | A callback that will be passed the url that MSAL will navigate to in redirect flows. Returning false in the callback will stop navigation. ### Cache Config Options From 80eb5bb4d6ffa41d25adbd656928dede3519fd6d Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Wed, 28 Aug 2024 10:45:05 -0700 Subject: [PATCH 12/13] msal-node format:fix --- lib/msal-node/src/network/LoopbackClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-node/src/network/LoopbackClient.ts b/lib/msal-node/src/network/LoopbackClient.ts index 734d9e14d9..d8cd5f137f 100644 --- a/lib/msal-node/src/network/LoopbackClient.ts +++ b/lib/msal-node/src/network/LoopbackClient.ts @@ -74,7 +74,7 @@ export class LoopbackClient implements ILoopbackClient { resolve(authCodeResponse); } ); - this.server.listen(0, '127.0.0.1'); // Listen on any available port + this.server.listen(0, "127.0.0.1"); // Listen on any available port } ); } From 5cbc9acf897a91b2ec4c39f161f7bf18204e333e Mon Sep 17 00:00:00 2001 From: Jo Arroyo Date: Wed, 28 Aug 2024 10:52:32 -0700 Subject: [PATCH 13/13] Change files --- ...ure-msal-node-cd98a92a-846b-47bc-894b-873d04f9e2d7.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@azure-msal-node-cd98a92a-846b-47bc-894b-873d04f9e2d7.json diff --git a/change/@azure-msal-node-cd98a92a-846b-47bc-894b-873d04f9e2d7.json b/change/@azure-msal-node-cd98a92a-846b-47bc-894b-873d04f9e2d7.json new file mode 100644 index 0000000000..c9128f66a4 --- /dev/null +++ b/change/@azure-msal-node-cd98a92a-846b-47bc-894b-873d04f9e2d7.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Format fix #7270", + "packageName": "@azure/msal-node", + "email": "joarroyo@microsoft.com", + "dependentChangeType": "none" +}