From 749f0021d5b88ea44ec6c846d1aa95414772b1e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:08:41 +0000 Subject: [PATCH 01/23] Initial plan From 2ec6f7181e3c0f3f7b7eb522a825cc6069aa22fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:18:35 +0000 Subject: [PATCH 02/23] Add telemetry performance event fields for platform broker integration Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- lib/msal-browser/src/error/NativeAuthError.ts | 3 +-- lib/msal-browser/src/protocol/Authorize.ts | 8 ++++++++ lib/msal-common/apiReview/msal-common.api.md | 4 ++++ .../src/telemetry/performance/PerformanceEvent.ts | 8 ++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/msal-browser/src/error/NativeAuthError.ts b/lib/msal-browser/src/error/NativeAuthError.ts index 381f250c7d..bdeab1c52b 100644 --- a/lib/msal-browser/src/error/NativeAuthError.ts +++ b/lib/msal-browser/src/error/NativeAuthError.ts @@ -48,8 +48,7 @@ export function isFatalNativeAuthError(error: NativeAuthError): boolean { if ( error.ext && error.ext.status && - (error.ext.status === NativeStatusCodes.PERSISTENT_ERROR || - error.ext.status === NativeStatusCodes.DISABLED) + error.ext.status === NativeStatusCodes.DISABLED ) { return true; } diff --git a/lib/msal-browser/src/protocol/Authorize.ts b/lib/msal-browser/src/protocol/Authorize.ts index d8864d7c32..f39b275d2d 100644 --- a/lib/msal-browser/src/protocol/Authorize.ts +++ b/lib/msal-browser/src/protocol/Authorize.ts @@ -83,6 +83,14 @@ async function getStandardParameters( // signal ests that this is a WAM call RequestParameterBuilder.addNativeBroker(parameters); + // instrument JS-platform bridge specific fields + performanceClient.addFields( + { + isPlatformAuthorizeRequest: true, + }, + request.correlationId + ); + // pass the req_cnf for POP if ( request.authenticationScheme === Constants.AuthenticationScheme.POP diff --git a/lib/msal-common/apiReview/msal-common.api.md b/lib/msal-common/apiReview/msal-common.api.md index c02829ae79..ebc588d36e 100644 --- a/lib/msal-common/apiReview/msal-common.api.md +++ b/lib/msal-common/apiReview/msal-common.api.md @@ -3425,6 +3425,10 @@ export type PerformanceEvent = { libraryVersion: string; previousLibraryVersion?: string; isNativeBroker?: boolean; + isPlatformAuthorizeRequest?: boolean; + isPlatformBrokerRequest?: boolean; + brokerErrorName?: string; + brokerErrorCode?: string; requestId?: string; cacheLookupPolicy?: number | undefined; cacheOutcome?: number; diff --git a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts index 64e8ae83ef..5146d0be0c 100644 --- a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts +++ b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts @@ -163,6 +163,14 @@ export type PerformanceEvent = { */ isNativeBroker?: boolean; + /** + * Platform-specific fields, when calling STS and/or broker for token requests + */ + isPlatformAuthorizeRequest?: boolean; + isPlatformBrokerRequest?: boolean; + brokerErrorName?: string; + brokerErrorCode?: string; + /** * Request ID returned from the response * From d1ba0905106dfa146e6af36a7e0fdd334c88c089 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:22:22 +0000 Subject: [PATCH 03/23] Update PlatformAuthInteractionClient telemetry instrumentation Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../PlatformAuthInteractionClient.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index e41e7d4369..d491f212cf 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -166,7 +166,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { // start the perf measurement const nativeATMeasurement = this.performanceClient.startMeasurement( BrowserPerformanceEvents.NativeInteractionClientAcquireToken, - request.correlationId + this.correlationId ); const reqTimestamp = TimeUtils.nowSeconds(); @@ -200,6 +200,10 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { "MSAL internal Cache does not contain tokens, return error as per cache policy", this.correlationId ); + nativeATMeasurement.end({ + success: false, + brokerErrorCode: "cache_request_failed", + }); throw e; } // continue with a native call for any and all errors @@ -220,10 +224,15 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { .then((result: AuthenticationResult) => { nativeATMeasurement.end({ success: true, - isNativeBroker: true, requestId: result.requestId, }); serverTelemetryManager.clearNativeBrokerErrorCode(); + if (this.performanceClient && this.correlationId) { + this.performanceClient.addFields( + { isNativeBroker: true }, + this.correlationId + ); + } return result; }) .catch((error: AuthError) => { @@ -231,7 +240,6 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { success: false, errorCode: error.errorCode, subErrorCode: error.subError, - isNativeBroker: true, }); throw error; }); @@ -239,6 +247,9 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { if (e instanceof NativeAuthError) { serverTelemetryManager.setNativeBrokerErrorCode(e.errorCode); } + nativeATMeasurement.end({ + success: false, + }); throw e; } } @@ -284,7 +295,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { { nativeAccountId, }, - request.correlationId + this.correlationId ); if (!account) { @@ -1144,7 +1155,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { embeddedClientId: child_client_id, embeddedRedirectUri: child_redirect_uri, }, - request.correlationId + this.correlationId ); } } From 26b4174b12b737d766f90c53243dc5ebf3025458 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:24:45 +0000 Subject: [PATCH 04/23] Add StandardController telemetry for acquireToken methods (partial) Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../src/controllers/StandardController.ts | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index 73f56c0bc5..aedc3f7c4e 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -93,6 +93,22 @@ import { IPlatformAuthHandler } from "../broker/nativeBroker/IPlatformAuthHandle import { collectInstanceStats } from "../utils/MsalFrameStatsUtils.js"; import { HandleRedirectPromiseOptions } from "../request/HandleRedirectPromiseOptions.js"; +function getAccountType( + account?: AccountInfo +): "AAD" | "MSA" | "B2C" | undefined { + const idTokenClaims = account?.idTokenClaims; + if (idTokenClaims?.tfp || idTokenClaims?.acr) { + return "B2C"; + } + + if (!idTokenClaims?.tid) { + return undefined; + } else if (idTokenClaims?.tid === "9188040d-6c67-4c5b-b112-36a304b66dad") { + return "MSA"; + } + return "AAD"; +} + function preflightCheck( initialized: boolean, performanceEvent: InProgressPerformanceEvent, @@ -650,6 +666,9 @@ export class StandardController implements IController { this.platformAuthProvider && this.canUsePlatformBroker(request) ) { + atrMeasurement.add({ + isPlatformBrokerRequest: true, + }); const nativeClient = new PlatformAuthInteractionClient( this.config, this.browserStorage, @@ -664,14 +683,19 @@ export class StandardController implements IController { this.nativeInternalStorage, correlationId ); + result = nativeClient .acquireTokenRedirect(request, atrMeasurement) .catch((e: AuthError) => { + atrMeasurement.add({ + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }); if ( e instanceof NativeAuthError && isFatalNativeAuthError(e) ) { - this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt + this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt platform broker calls const redirectClient = this.createRedirectClient(correlationId); return redirectClient.acquireToken(request); @@ -771,6 +795,9 @@ export class StandardController implements IController { const pkce = this.getPreGeneratedPkceCodes(correlationId); if (this.canUsePlatformBroker(request)) { + atPopupMeasurement.add({ + isPlatformBrokerRequest: true, + }); result = this.acquireTokenNative( { ...request, @@ -782,7 +809,7 @@ export class StandardController implements IController { atPopupMeasurement.end( { success: true, - isNativeBroker: true, + accountType: getAccountType(response.account), }, undefined, response.account @@ -790,11 +817,15 @@ export class StandardController implements IController { return response; }) .catch((e: AuthError) => { + atPopupMeasurement.add({ + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }); if ( e instanceof NativeAuthError && isFatalNativeAuthError(e) ) { - this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt + this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to continuing to attempt platform broker calls const popupClient = this.createPopupClient(correlationId); return popupClient.acquireToken(request, pkce); From 445e38f8a64bb85dc4b6d59d15415da63de47650 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:29:56 +0000 Subject: [PATCH 05/23] Complete StandardController telemetry instrumentation Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../src/controllers/StandardController.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index aedc3f7c4e..d96189814c 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -478,6 +478,9 @@ export class StandardController implements IController { "handleRedirectPromise - acquiring token from native platform", correlationId ); + rootMeasurement.add({ + isPlatformBrokerRequest: true, + }); const nativeClient = new PlatformAuthInteractionClient( this.config, this.browserStorage, @@ -973,10 +976,17 @@ export class StandardController implements IController { let result: Promise; if (this.canUsePlatformBroker(validRequest)) { + this.ssoSilentMeasurement?.add({ + isPlatformBrokerRequest: true, + }); result = this.acquireTokenNative( validRequest, ApiId.ssoSilent ).catch((e: AuthError) => { + this.ssoSilentMeasurement?.add({ + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }); // If native token acquisition fails for availability reasons fallback to standard flow if (e instanceof NativeAuthError && isFatalNativeAuthError(e)) { this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt @@ -1013,9 +1023,9 @@ export class StandardController implements IController { this.ssoSilentMeasurement?.end( { success: true, - isNativeBroker: response.fromPlatformBroker, accessTokenSize: response.accessToken.length, idTokenSize: response.idToken.length, + accountType: getAccountType(response.account), }, undefined, response.account @@ -1101,9 +1111,9 @@ export class StandardController implements IController { atbcMeasurement.end( { success: true, - isNativeBroker: result.fromPlatformBroker, accessTokenSize: result.accessToken.length, idTokenSize: result.idToken.length, + accountType: getAccountType(result.account), }, undefined, result.account @@ -1139,6 +1149,9 @@ export class StandardController implements IController { if ( this.canUsePlatformBroker(request, request.nativeAccountId) ) { + atbcMeasurement.add({ + isPlatformBrokerRequest: true, + }); const result = await this.acquireTokenNative( { ...request, @@ -1154,6 +1167,10 @@ export class StandardController implements IController { ) { this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt } + atbcMeasurement.add({ + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }); throw e; }); atbcMeasurement.end( @@ -1221,7 +1238,6 @@ export class StandardController implements IController { this.acquireTokenByCodeAsyncMeasurement?.end({ success: true, fromCache: response.fromCache, - isNativeBroker: response.fromPlatformBroker, }); return response; }) @@ -1909,7 +1925,6 @@ export class StandardController implements IController { { success: true, fromCache: result.fromCache, - isNativeBroker: result.fromPlatformBroker, accessTokenSize: result.accessToken.length, idTokenSize: result.idToken.length, }, @@ -2148,7 +2163,6 @@ export class StandardController implements IController { this.performanceClient.addFields( { fromCache: response.fromCache, - isNativeBroker: response.fromPlatformBroker, }, request.correlationId ); @@ -2198,12 +2212,23 @@ export class StandardController implements IController { "acquireTokenSilent - attempting to acquire token from native platform", silentRequest.correlationId ); + this.performanceClient.addFields( + { isPlatformBrokerRequest: true }, + silentRequest.correlationId + ); return this.acquireTokenNative( silentRequest, ApiId.acquireTokenSilent_silentFlow, silentRequest.account.nativeAccountId, cacheLookupPolicy ).catch(async (e: AuthError) => { + this.performanceClient.addFields( + { + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }, + silentRequest.correlationId + ); // If native token acquisition fails for availability reasons fallback to web flow if (e instanceof NativeAuthError && isFatalNativeAuthError(e)) { this.logger.verbose( From b9882854ba2539237766f47cc4b0fea0f3aa3622 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:34:05 +0000 Subject: [PATCH 06/23] Add beachball change files for msal-v5 branch Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- change/@azure-msal-browser-v5-telemetry-cherry-pick.json | 7 +++++++ change/@azure-msal-common-v5-telemetry-cherry-pick.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@azure-msal-browser-v5-telemetry-cherry-pick.json create mode 100644 change/@azure-msal-common-v5-telemetry-cherry-pick.json diff --git a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json new file mode 100644 index 0000000000..cd5d14b03c --- /dev/null +++ b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) #7991", + "packageName": "@azure/msal-browser", + "email": "sameera.gajjarapu@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json new file mode 100644 index 0000000000..9dcc9a3a39 --- /dev/null +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) #7991", + "packageName": "@azure/msal-common", + "email": "sameera.gajjarapu@microsoft.com", + "dependentChangeType": "patch" +} From 5d2209ebd2bb3abf0943ac975ca519243c27ebe6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:38:59 +0000 Subject: [PATCH 07/23] Remove getAccountType function and restore original telemetry fields Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../src/controllers/StandardController.ts | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index d96189814c..5a486f9730 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -93,22 +93,6 @@ import { IPlatformAuthHandler } from "../broker/nativeBroker/IPlatformAuthHandle import { collectInstanceStats } from "../utils/MsalFrameStatsUtils.js"; import { HandleRedirectPromiseOptions } from "../request/HandleRedirectPromiseOptions.js"; -function getAccountType( - account?: AccountInfo -): "AAD" | "MSA" | "B2C" | undefined { - const idTokenClaims = account?.idTokenClaims; - if (idTokenClaims?.tfp || idTokenClaims?.acr) { - return "B2C"; - } - - if (!idTokenClaims?.tid) { - return undefined; - } else if (idTokenClaims?.tid === "9188040d-6c67-4c5b-b112-36a304b66dad") { - return "MSA"; - } - return "AAD"; -} - function preflightCheck( initialized: boolean, performanceEvent: InProgressPerformanceEvent, @@ -812,7 +796,7 @@ export class StandardController implements IController { atPopupMeasurement.end( { success: true, - accountType: getAccountType(response.account), + isNativeBroker: true, }, undefined, response.account @@ -1023,9 +1007,9 @@ export class StandardController implements IController { this.ssoSilentMeasurement?.end( { success: true, + isNativeBroker: response.fromPlatformBroker, accessTokenSize: response.accessToken.length, idTokenSize: response.idToken.length, - accountType: getAccountType(response.account), }, undefined, response.account @@ -1111,9 +1095,9 @@ export class StandardController implements IController { atbcMeasurement.end( { success: true, + isNativeBroker: result.fromPlatformBroker, accessTokenSize: result.accessToken.length, idTokenSize: result.idToken.length, - accountType: getAccountType(result.account), }, undefined, result.account From 67af560a4c6dcae3d06450302c390b8675b55b5c Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 04:13:03 -0800 Subject: [PATCH 08/23] Update lib/msal-browser/src/controllers/StandardController.ts --- lib/msal-browser/src/controllers/StandardController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index 5a486f9730..e11bc1de3d 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -812,7 +812,7 @@ export class StandardController implements IController { e instanceof NativeAuthError && isFatalNativeAuthError(e) ) { - this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to continuing to attempt platform broker calls + this.platformAuthProvider = undefined; // If extension gets uninstalled during session prevent future requests from continuing to attempt platform broker calls const popupClient = this.createPopupClient(correlationId); return popupClient.acquireToken(request, pkce); From 10bc6e791131ea5208e1a045f1ab6091faf4da08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 12:18:11 +0000 Subject: [PATCH 09/23] Update NativeAuthError test for PERSISTENT_ERROR behavior Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- lib/msal-browser/test/error/NativeAuthError.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/msal-browser/test/error/NativeAuthError.spec.ts b/lib/msal-browser/test/error/NativeAuthError.spec.ts index 60efbb3e58..6613e1628a 100644 --- a/lib/msal-browser/test/error/NativeAuthError.spec.ts +++ b/lib/msal-browser/test/error/NativeAuthError.spec.ts @@ -3,12 +3,12 @@ import { NativeAuthErrorCodes, createNativeAuthError, isFatalNativeAuthError, -} from "../../src/error/NativeAuthError"; +} from "../../src/error/NativeAuthError.js"; import { InteractionRequiredAuthError, InteractionRequiredAuthErrorCodes, } from "@azure/msal-common"; -import * as NativeStatusCode from "../../src/broker/nativeBroker/NativeStatusCodes"; +import * as NativeStatusCode from "../../src/broker/nativeBroker/NativeStatusCodes.js"; import { BrowserAuthErrorCodes, BrowserAuthError, @@ -17,7 +17,7 @@ import { describe("NativeAuthError Unit Tests", () => { describe("NativeAuthError", () => { describe("isFatal tests", () => { - it("should return true for isFatal when WAM status is PERSISTENT_ERROR", () => { + it("should return false for isFatal when WAM status is PERSISTENT_ERROR", () => { const error = new NativeAuthError( "testError", "testErrorDescription", @@ -28,7 +28,7 @@ describe("NativeAuthError Unit Tests", () => { status: NativeStatusCode.PERSISTENT_ERROR, } ); - expect(isFatalNativeAuthError(error)).toBe(true); + expect(isFatalNativeAuthError(error)).toBe(false); }); it("should return true for isFatal when WAM status is DISABLED", () => { From 92d576618cc7682f229051f60c65a662d1342afb Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 10:59:30 -0800 Subject: [PATCH 10/23] Update lib/msal-common/src/telemetry/performance/PerformanceEvent.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/telemetry/performance/PerformanceEvent.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts index 5146d0be0c..1e778a8708 100644 --- a/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts +++ b/lib/msal-common/src/telemetry/performance/PerformanceEvent.ts @@ -166,9 +166,21 @@ export type PerformanceEvent = { /** * Platform-specific fields, when calling STS and/or broker for token requests */ + /** + * Set to true when making authorize requests to STS with native broker parameter + */ isPlatformAuthorizeRequest?: boolean; + /** + * Set to true when making requests directly to the platform broker + */ isPlatformBrokerRequest?: boolean; + /** + * Error name when broker requests fail + */ brokerErrorName?: string; + /** + * Error code when broker requests fail + */ brokerErrorCode?: string; /** From 9f988d6303be3d73cf37cc0b28978a9668c144e6 Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 10:59:51 -0800 Subject: [PATCH 11/23] Update change/@azure-msal-browser-v5-telemetry-cherry-pick.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- change/@azure-msal-browser-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json index cd5d14b03c..f291e8ba65 100644 --- a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) #7991", + "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", "packageName": "@azure/msal-browser", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From 2e190fe4201b4a2beafbb62c2225df0ae16a78bb Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 10:59:59 -0800 Subject: [PATCH 12/23] Update change/@azure-msal-common-v5-telemetry-cherry-pick.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- change/@azure-msal-common-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json index 9dcc9a3a39..8092cd462e 100644 --- a/change/@azure-msal-common-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) #7991", + "comment": "Cherry-pick: Add Platform Telemetry [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", "packageName": "@azure/msal-common", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From 1d8f086de21cfb6e4db2e7e482003d88181a8e3a Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 11:00:25 -0800 Subject: [PATCH 13/23] Update lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/interaction_client/PlatformAuthInteractionClient.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index d491f212cf..426d647aef 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -249,6 +249,10 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { } nativeATMeasurement.end({ success: false, + ...(e instanceof NativeAuthError && { + brokerErrorName: e.name, + brokerErrorCode: e.errorCode, + }), }); throw e; } From 9e82d1b6e9bebd202e3bcdf6ffaa90f8a41a27fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:07:22 +0000 Subject: [PATCH 14/23] Remove misleading brokerErrorCode for cache miss scenario Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../src/interaction_client/PlatformAuthInteractionClient.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index 426d647aef..85121fb2ce 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -202,7 +202,6 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { ); nativeATMeasurement.end({ success: false, - brokerErrorCode: "cache_request_failed", }); throw e; } From 58cd7a266b2cc0841b4dabd465ada96a6182383a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:10:55 +0000 Subject: [PATCH 15/23] Add performance event validation tests to PlatformAuthInteractionClient Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../PlatformAuthInteractionClient.spec.ts | 279 +++++++++++++++++- 1 file changed, 277 insertions(+), 2 deletions(-) diff --git a/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts b/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts index 6f95f3e80d..f4db5f61c8 100644 --- a/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts @@ -15,16 +15,22 @@ import { InProgressPerformanceEvent, AccountEntityUtils, Constants, + CacheHelpers, + AuthError, } from "@azure/msal-common"; +import { OpenIdConfigResponse } from "../../../msal-common/src/authority/OpenIdConfigResponse.js"; import { PlatformAuthExtensionHandler } from "../../src/broker/nativeBroker/PlatformAuthExtensionHandler.js"; import { ApiId } from "../../src/utils/BrowserConstants.js"; import { PlatformAuthInteractionClient } from "../../src/interaction_client/PlatformAuthInteractionClient.js"; import { PublicClientApplication } from "../../src/app/PublicClientApplication.js"; import { + DEFAULT_OPENID_CONFIG_RESPONSE, + DEFAULT_TENANT_DISCOVERY_RESPONSE, ID_TOKEN_CLAIMS, RANDOM_TEST_GUID, TEST_CONFIG, TEST_DATA_CLIENT_INFO, + TEST_HASHES, TEST_TOKENS, } from "../utils/StringConstants.js"; import { NavigationClient } from "../../src/navigation/NavigationClient.js"; @@ -33,6 +39,7 @@ import { getDefaultErrorMessage, } from "../../src/error/BrowserAuthError.js"; import { + createNativeAuthError, NativeAuthError, NativeAuthErrorCodes, } from "../../src/error/NativeAuthError.js"; @@ -46,6 +53,7 @@ import { BrowserConstants } from "../../src/utils/BrowserConstants.js"; import * as NativeStatusCodes from "../../src/broker/nativeBroker/NativeStatusCodes.js"; import { PlatformAuthResponse } from "../../src/broker/nativeBroker/PlatformAuthResponse.js"; import { PlatformAuthDOMHandler } from "../../src/broker/nativeBroker/PlatformAuthDOMHandler.js"; +import * as SilentHandler from "../../src/interaction_handler/SilentHandler.js"; const MOCK_WAM_RESPONSE: PlatformAuthResponse = { access_token: TEST_TOKENS.ACCESS_TOKEN, @@ -120,7 +128,7 @@ describe("PlatformAuthInteractionClient Tests", () => { let wamProvider: PlatformAuthExtensionHandler; let postMessageSpy: jest.SpyInstance; - let mcPort: MessagePort; + let mcPort: MessagePort | undefined; let perfClient: IPerformanceClient; let perfMeasurement: InProgressPerformanceEvent; @@ -187,7 +195,7 @@ describe("PlatformAuthInteractionClient Tests", () => { ); // source property not set by jsdom window messaging APIs perfMeasurement = perfClient.startMeasurement( "test-measurement", - "test-correlation-id" + RANDOM_TEST_GUID ); }); @@ -1713,4 +1721,271 @@ describe("PlatformAuthInteractionClient Tests", () => { expect(nativeRequest.redirectUri).toEqual("localhost"); }); }); + + describe("Performance Event Validation", () => { + let performanceSpy: jest.SpyInstance; + let startMeasurementSpy: jest.SpyInstance; + + beforeEach(() => { + // Create mock measurement with spies + const mockMeasurement = { + add: jest.fn(), + end: jest.fn(), + increment: jest.fn(), + discard: jest.fn(), + }; + + // Spy on performance client methods + startMeasurementSpy = jest + .spyOn(perfClient, "startMeasurement") + .mockReturnValue(mockMeasurement as any); + performanceSpy = jest.spyOn(perfClient, "addFields"); + }); + + afterEach(() => { + performanceSpy.mockRestore(); + startMeasurementSpy.mockRestore(); + }); + + it("emits isNativeBroker=true for acquireToken success", async () => { + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.resolve(MOCK_WAM_RESPONSE); + }); + + // Mock successful token acquisition from cache first + const mockAuthResult = { + accessToken: "mock_access_token", + idToken: "mock_id_token", + account: { + homeAccountId: "test-home-account-id", + environment: "login.microsoftonline.com", + nativeAccountId: "test-native-account-id", + tenantId: "test-tenant-id", + username: "test@example.com", + localAccountId: "test-local-account-id", + name: "Test User", + idTokenClaims: {}, + }, + scopes: ["User.Read"], + expiresOn: new Date(Date.now() + 3600000), + tenantId: "test-tenant-id", + uniqueId: "test-unique-id", + tokenType: "Bearer" as const, + correlationId: RANDOM_TEST_GUID, + extExpiresOn: new Date(Date.now() + 7200000), + state: "", + fromCache: false, + }; + + // Mock the acquireTokensFromCache method to simulate a cache miss (rejects first), then WAM success (resolves) + jest.spyOn( + platformAuthInteractionClient as any, + "acquireTokensFromCache" + ) + // First call rejects (simulates cache miss), second call resolves (simulates WAM success) + .mockRejectedValueOnce(new Error("No cached tokens")) + .mockResolvedValue(mockAuthResult); + + await platformAuthInteractionClient.acquireToken({ + scopes: ["User.Read"], + account: mockAuthResult.account, + correlationId: RANDOM_TEST_GUID, + }); + + // Get the latest mock measurement object + const mockMeasurement = + startMeasurementSpy.mock.results[ + startMeasurementSpy.mock.results.length - 1 + ].value; + + // Verify isNativeBroker is set during successful completion + expect(mockMeasurement.end).toHaveBeenCalledWith( + expect.objectContaining({ + success: true, + isNativeBroker: true, + }) + ); + + // Verify the measurement was started with correct correlation ID + expect(startMeasurementSpy).toHaveBeenCalledWith( + expect.any(String), + RANDOM_TEST_GUID + ); + }); + + it("should emit isNativeBroker=true for handleRedirectPromise", async () => { + // @ts-ignore + pca.browserStorage.setInteractionInProgress(true); + // @ts-ignore + pca.browserStorage.setTemporaryCache( + "request.native", + JSON.stringify({ + scopes: ["User.Read"], + correlationId: RANDOM_TEST_GUID, + }), + true + ); + + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.resolve(MOCK_WAM_RESPONSE); + }); + + // Mock the protected handleNativeResponse method + jest.spyOn( + platformAuthInteractionClient as any, + "handleNativeResponse" + ).mockResolvedValue({ + accessToken: "mock_access_token", + idToken: "mock_id_token", + account: null, + scopes: ["User.Read"], + expiresOn: new Date(Date.now() + 3600000), + tenantId: "mock_tenant_id", + uniqueId: "mock_unique_id", + tokenType: "Bearer", + correlationId: RANDOM_TEST_GUID, + }); + + jest.spyOn( + platformAuthInteractionClient as any, + "initializeServerTelemetryManager" + ).mockReturnValue({ + clearNativeBrokerErrorCode: jest.fn(), + }); + + // Spy on performanceClient.addFields since handleRedirectPromise uses it directly + const addFieldsSpy = jest.spyOn(perfClient, "addFields"); + + await platformAuthInteractionClient.handleRedirectPromise( + perfClient, + RANDOM_TEST_GUID + ); + + // Verify that addFields was called with isNativeBroker: true + expect(addFieldsSpy).toHaveBeenCalledWith( + { isNativeBroker: true }, + RANDOM_TEST_GUID + ); + }); + + it("should use synchronized correlationId in performance measurements", async () => { + const customCorrelationId = "custom-correlation-123"; + + platformAuthInteractionClient = new PlatformAuthInteractionClient( + // @ts-ignore + pca.config, + // @ts-ignore + pca.browserStorage, + // @ts-ignore + pca.browserCrypto, + pca.getLogger(), + // @ts-ignore + pca.eventHandler, + // @ts-ignore + pca.navigationClient, + ApiId.acquireTokenRedirect, + perfClient, + wamProvider, + "nativeAccountId", + // @ts-ignore + pca.nativeInternalStorage, + customCorrelationId + ); + + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.resolve(MOCK_WAM_RESPONSE); + }); + + await platformAuthInteractionClient.acquireToken({ + scopes: ["User.Read"], + correlationId: customCorrelationId, + }); + + // Should use the synchronized correlationId, which should be the customCorrelationId + // since synchronizeCorrelationId should update this.correlationId + expect(startMeasurementSpy).toHaveBeenCalledWith( + expect.any(String), + customCorrelationId + ); + }); + + it("should emit success=false and errorCode for failed acquireToken", async () => { + // Mock sendMessage to succeed but handleNativeResponse to fail + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.resolve(MOCK_WAM_RESPONSE); + }); + + // Mock handleNativeResponse to throw an error + const authError = new AuthError("test_error", "Test error message"); + jest.spyOn( + platformAuthInteractionClient as any, + "handleNativeResponse" + ).mockRejectedValue(authError); + + try { + await platformAuthInteractionClient.acquireToken({ + scopes: ["User.Read"], + }); + } catch (error) { + // Expected to throw + } + + // Get the mock measurement object that was returned + const mockMeasurement = startMeasurementSpy.mock.results[0].value; + + // Check that measurement.end was called with success: false and errorCode + expect(mockMeasurement.end).toHaveBeenCalledWith( + expect.objectContaining({ + success: false, + errorCode: "test_error", + }) + ); + }); + + it("should emit success=false and errorCode when sendMessage fails", async () => { + // Test that measurement.end is now called when sendMessage fails (bug fix) + const nativeError = createNativeAuthError( + "ContentError", + "problem getting response from extension" + ); + + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.reject(nativeError); + }); + + try { + await platformAuthInteractionClient.acquireToken({ + scopes: ["User.Read"], + }); + } catch (error) { + // Expected to throw + expect(error).toBe(nativeError); + } + + // Get the mock measurement object that was returned + const mockMeasurement = startMeasurementSpy.mock.results[0].value; + + // After the fix, measurement.end should now be called when sendMessage fails + expect(mockMeasurement.end).toHaveBeenCalledWith( + expect.objectContaining({ + success: false, + }) + ); + }); + }); }); From ebd6f93b11df482ae3e5f46d92f9b17ad2e4b30f Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 11:32:17 -0800 Subject: [PATCH 16/23] Update change/@azure-msal-browser-v5-telemetry-cherry-pick.json --- change/@azure-msal-browser-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json index f291e8ba65..bed41c1810 100644 --- a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "Cherry-pick: Add Platform Telemetry (PR #7991) [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", + "comment": "[v5]Add Platform Telemetry [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", "packageName": "@azure/msal-browser", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From a7e8b6930f2cd2f130a3a6fee7b696b62c699ade Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 11:33:04 -0800 Subject: [PATCH 17/23] Update change/@azure-msal-browser-v5-telemetry-cherry-pick.json --- change/@azure-msal-browser-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json index bed41c1810..63b921d4b6 100644 --- a/change/@azure-msal-browser-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-browser-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "[v5]Add Platform Telemetry [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", + "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175)", "packageName": "@azure/msal-browser", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From 56cd9176c9aa2c9bb0f2f64a5c0fa115b7dd82ad Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 3 Dec 2025 11:33:57 -0800 Subject: [PATCH 18/23] Update change/@azure-msal-common-v5-telemetry-cherry-pick.json --- change/@azure-msal-common-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json index 8092cd462e..901104c0ae 100644 --- a/change/@azure-msal-common-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "Cherry-pick: Add Platform Telemetry [#7991](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7991)", + "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175)", "packageName": "@azure/msal-common", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From b889f1bd21008db6ff431aafe901b156d0b8643e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:42:09 +0000 Subject: [PATCH 19/23] Update changefile format to reference PR #7991 Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- change/@azure-msal-common-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json index 901104c0ae..b50c28c5b1 100644 --- a/change/@azure-msal-common-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175)", + "comment": "v5]Add Platform Telemetry [#7991", "packageName": "@azure/msal-common", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From 7331407036df0fe82e48d48d9ccf96d8b6ddcc32 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 20:28:44 +0000 Subject: [PATCH 20/23] Add telemetry validation to acquireTokenRedirect platform broker test Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../test/app/PublicClientApplication.spec.ts | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index 30019d4bc0..7bd512fcfb 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -116,6 +116,7 @@ import { PlatformAuthDOMHandler } from "../../src/broker/nativeBroker/PlatformAu import * as BrowserRootPerformanceEvents from "../../src/telemetry/BrowserRootPerformanceEvents.js"; import * as CacheKeys from "../../src/cache/CacheKeys.js"; import { getAccountKeysCacheKey } from "../../src/cache/CacheKeys.js"; +import exp from "constants"; const cacheConfig = { cacheLocation: BrowserCacheLocation.SessionStorage, @@ -1570,7 +1571,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { done(); }); }); - it("goes directly to the native broker if nativeAccountId is present", async () => { + it("goes directly to the platform broker if nativeAccountId is present and emits platform telemetry", async () => { const config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, @@ -1578,6 +1579,17 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { system: { allowPlatformBroker: true, }, + telemetry: { + client: new BrowserPerformanceClient({ + auth: { + clientId: TEST_CONFIG.MSAL_CLIENT_ID, + }, + }), + application: { + appName: TEST_CONFIG.applicationName, + appVersion: TEST_CONFIG.applicationVersion, + }, + }, }; pca = new PublicClientApplication(config); @@ -1588,6 +1600,21 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { pca = (pca as any).controller; const testAccount = BASIC_NATIVE_TEST_ACCOUNT_INFO; + const testTokenResponse: AuthenticationResult = { + authority: TEST_CONFIG.validAuthority, + uniqueId: testAccount.localAccountId, + tenantId: testAccount.tenantId, + scopes: TEST_CONFIG.DEFAULT_SCOPES, + idToken: "test-idToken", + idTokenClaims: {}, + accessToken: "test-accessToken", + fromCache: false, + correlationId: RANDOM_TEST_GUID, + expiresOn: TestTimeUtils.nowDateWithOffset(3600), + account: testAccount, + tokenType: Constants.AuthenticationScheme.BEARER, + fromNativeBroker: true, + }; jest.spyOn(BrowserCrypto, "createNewGuid").mockReturnValue( RANDOM_TEST_GUID @@ -1599,14 +1626,53 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { "acquireTokenRedirect" ) .mockResolvedValue(); + + const nativeHandleRedirectPromiseSpy = jest + .spyOn(StandardController.prototype, "handleRedirectPromise") + .mockImplementation( + async (req: any, measurementName?: string) => { + // Find existing performance event by correlationId and modify it + const performanceClient = + pca.getConfiguration().telemetry?.client; + if (performanceClient && measurementName) { + const events = (performanceClient as any) + .eventsByCorrelationId; + const existingEvent = events.get(req.correlationId); + if (existingEvent) { + existingEvent.isNativeBroker = true; + } + } + return testTokenResponse; + } + ); + const redirectSpy: jest.SpyInstance = jest .spyOn(RedirectClient.prototype, "acquireToken") .mockResolvedValue(); + + // Mock the acquireTokensFromCache method to simulate a cache miss (rejects first), then WAM success (resolves) + jest.spyOn( + PlatformAuthInteractionClient.prototype as any, + "acquireTokensFromCache" + ) + // First call rejects (simulates cache miss), second call resolves (simulates WAM success) + .mockRejectedValueOnce(new Error("No cached tokens")) + .mockResolvedValue(testTokenResponse); + + // Add performance callback + const callbackId = pca.addPerformanceCallback((events) => { + expect(events[0].isNativeBroker).toBe(true); + expect(events[0].isPlatformBrokerRequest).toBe(true); + pca.removePerformanceCallback(callbackId); + }); + await pca.acquireTokenRedirect({ scopes: ["User.Read"], account: testAccount, }); + const response = pca.handleRedirectPromise(); + expect(nativeAcquireTokenSpy).toHaveBeenCalledTimes(1); expect(redirectSpy).toHaveBeenCalledTimes(0); }); From 22ff8b61d009a17aad844581a75c78c4ab8eff48 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 4 Dec 2025 10:31:16 -0800 Subject: [PATCH 21/23] Update apiReview for common --- lib/msal-common/apiReview/msal-common.api.md | 50 ++++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/msal-common/apiReview/msal-common.api.md b/lib/msal-common/apiReview/msal-common.api.md index 4d501ed33f..cb11477c04 100644 --- a/lib/msal-common/apiReview/msal-common.api.md +++ b/lib/msal-common/apiReview/msal-common.api.md @@ -4861,32 +4861,32 @@ const X_MS_LIB_CAPABILITY_VALUE: string; // src/telemetry/performance/PerformanceEvent.ts:162: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:162:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" // src/telemetry/performance/PerformanceEvent.ts:162:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:169: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:169:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:169:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:176: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:176:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:176:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:182: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:182:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:182:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:202: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:189: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:189:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:189:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:196: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:196:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:196:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:202: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:202:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" // src/telemetry/performance/PerformanceEvent.ts:202:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:210: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:210:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:210:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:219: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:219:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:219:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:227: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:227:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:227:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:234: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:234:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:234:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration -// src/telemetry/performance/PerformanceEvent.ts:308: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:308:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/telemetry/performance/PerformanceEvent.ts:308:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:222: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:222:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:222:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:230: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:230:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:230:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:239: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:239:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:239:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:247: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:247:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:247:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:254: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:254:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:254:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration +// src/telemetry/performance/PerformanceEvent.ts:328: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:328:14 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/telemetry/performance/PerformanceEvent.ts:328:8 - (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration ``` From f04ba361ca664126e94e5763f08377be67c5ea82 Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Thu, 4 Dec 2025 14:04:17 -0800 Subject: [PATCH 22/23] Update change/@azure-msal-common-v5-telemetry-cherry-pick.json --- change/@azure-msal-common-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json index b50c28c5b1..13339f29d6 100644 --- a/change/@azure-msal-common-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "v5]Add Platform Telemetry [#7991", + "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175) "packageName": "@azure/msal-common", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch" From fe9508dfb6691912145dbae1e505f3d9f0341adc Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Thu, 4 Dec 2025 14:04:45 -0800 Subject: [PATCH 23/23] Update change/@azure-msal-common-v5-telemetry-cherry-pick.json --- change/@azure-msal-common-v5-telemetry-cherry-pick.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-common-v5-telemetry-cherry-pick.json b/change/@azure-msal-common-v5-telemetry-cherry-pick.json index 13339f29d6..ee992f4f33 100644 --- a/change/@azure-msal-common-v5-telemetry-cherry-pick.json +++ b/change/@azure-msal-common-v5-telemetry-cherry-pick.json @@ -1,6 +1,6 @@ { "type": "minor", - "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175) + "comment": "[v5]Add Platform Telemetry [#8175](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8175)" "packageName": "@azure/msal-common", "email": "sameera.gajjarapu@microsoft.com", "dependentChangeType": "patch"