From 3b9ac35a7e66bb5c2703939df47f3083c5ae568c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 05:11:08 +0000 Subject: [PATCH 1/6] Initial plan From 20739ae70582d7eedefa58d8352675c7595e2d8a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 05:46:06 +0000 Subject: [PATCH 2/6] Add prompt=select_account support for native flows in msal-v5 (#8062) Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../@azure-msal-browser-prompt-select-account-msal-v5.json | 7 +++++++ lib/msal-browser/src/controllers/StandardController.ts | 1 + .../interaction_client/PlatformAuthInteractionClient.ts | 1 + 3 files changed, 9 insertions(+) create mode 100644 change/@azure-msal-browser-prompt-select-account-msal-v5.json diff --git a/change/@azure-msal-browser-prompt-select-account-msal-v5.json b/change/@azure-msal-browser-prompt-select-account-msal-v5.json new file mode 100644 index 0000000000..cf5b56b0f1 --- /dev/null +++ b/change/@azure-msal-browser-prompt-select-account-msal-v5.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Add prompt=select_account support for native flows in msal-v5 (#8062)", + "packageName": "@azure/msal-browser", + "email": "github-copilot@microsoft.com", + "dependentChangeType": "patch" +} \ No newline at end of file diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index 9759f668ca..1e6727d8e6 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -1587,6 +1587,7 @@ export class StandardController implements IController { case Constants.PromptValue.NONE: case Constants.PromptValue.CONSENT: case Constants.PromptValue.LOGIN: + case Constants.PromptValue.SELECT_ACCOUNT: this.logger.trace( "canUsePlatformBroker: prompt is compatible with platform broker flow" ); diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index 05a807ca4e..d66bd2adfb 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -1044,6 +1044,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { case Constants.PromptValue.NONE: case Constants.PromptValue.CONSENT: case Constants.PromptValue.LOGIN: + case Constants.PromptValue.SELECT_ACCOUNT: this.logger.trace( "initializeNativeRequest: prompt is compatible with native flow" ); From e3244a17fd8c95c5428e2c1220e479a36770c224 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 05:54:25 +0000 Subject: [PATCH 3/6] Add missing tests for prompt=select_account native flow support Co-authored-by: sameerag <21958742+sameerag@users.noreply.github.com> --- .../test/app/PublicClientApplication.spec.ts | 97 +++++++++++++++---- .../PlatformAuthInteractionClient.spec.ts | 42 ++++---- 2 files changed, 103 insertions(+), 36 deletions(-) diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index 7ac7521250..3c49c6587e 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -1689,7 +1689,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { pca.initialize(); }); - it("falls back to web flow if prompt is select_account", async () => { + it("Does not fall back to web flow if prompt is select_account", async () => { const config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, @@ -1700,29 +1700,28 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { }; pca = new PublicClientApplication(config); - await pca.initialize(); stubExtensionProvider(config); + await pca.initialize(); - //Implementation of PCA was moved to controller. + // Implementation of PCA was moved to controller. + // eslint-disable-next-line @typescript-eslint/no-explicit-any pca = (pca as any).controller; const testAccount = BASIC_NATIVE_TEST_ACCOUNT_INFO; - const nativeAcquireTokenSpy = jest.spyOn( - PlatformAuthInteractionClient.prototype, - "acquireTokenRedirect" - ); - const redirectSpy: jest.SpyInstance = jest - .spyOn(RedirectClient.prototype, "acquireToken") - .mockImplementation(); + const nativeAcquireTokenSpy: jest.SpyInstance = jest + .spyOn( + PlatformAuthInteractionClient.prototype, + "acquireTokenRedirect" + ) + .mockResolvedValue(); await pca.acquireTokenRedirect({ scopes: ["User.Read"], account: testAccount, prompt: "select_account", }); - expect(nativeAcquireTokenSpy).toHaveBeenCalledTimes(0); - expect(redirectSpy).toHaveBeenCalledTimes(1); + expect(nativeAcquireTokenSpy).toHaveBeenCalledTimes(1); }); it("falls back to web flow if native broker call fails due to fatal error", async () => { @@ -2658,7 +2657,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { expect(popupSpy).toHaveBeenCalledTimes(0); }); - it("falls back to web flow if prompt is select_account", async () => { + it("Does not fall back to web flow if prompt is select_account and emits platform telemetry", async () => { const config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, @@ -2666,12 +2665,26 @@ 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); stubExtensionProvider(config); await pca.initialize(); + //Implementation of PCA was moved to controller. + pca = (pca as any).controller; + const testAccount = BASIC_NATIVE_TEST_ACCOUNT_INFO; const testTokenResponse: AuthenticationResult = { authority: TEST_CONFIG.validAuthority, @@ -2688,13 +2701,59 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { tokenType: Constants.AuthenticationScheme.BEARER, }; - const nativeAcquireTokenSpy: jest.SpyInstance = jest.spyOn( - PlatformAuthInteractionClient.prototype, - "acquireToken" + jest.spyOn(BrowserCrypto, "createNewGuid").mockReturnValue( + RANDOM_TEST_GUID ); + + const nativeAcquireTokenSpy: jest.SpyInstance = jest + .spyOn(PlatformAuthInteractionClient.prototype, "acquireToken") + .mockImplementation(async function ( + this: PlatformAuthInteractionClient, + request + ) { + expect(request.correlationId).toBe(RANDOM_TEST_GUID); + + // Add isNativeBroker to the measurement that was started by StandardController + // This simulates what the real PlatformAuthInteractionClient does + if ( + this.performanceClient && + (this.performanceClient as any).eventsByCorrelationId + ) { + const eventMap = (this.performanceClient as any) + .eventsByCorrelationId; + const existingEvent = eventMap.get(this.correlationId); + if (existingEvent) { + existingEvent.isNativeBroker = true; + } + } + + return testTokenResponse; + }); + const popupSpy: jest.SpyInstance = jest .spyOn(PopupClient.prototype, "acquireToken") - .mockResolvedValue(testTokenResponse); + .mockImplementation(function ( + this: PopupClient, + request: PopupRequest + ): Promise { + const eventMap = (this.performanceClient as any) + .eventsByCorrelationId; + const existingEvent = eventMap.get( + request.correlationId || RANDOM_TEST_GUID + ); + if (existingEvent) { + existingEvent.isPlatformAuthorizeRequest = true; + } + + return Promise.resolve(testTokenResponse); + }); + + // Add performance callback + const callbackId = pca.addPerformanceCallback((events) => { + expect(events[0].isNativeBroker).toBe(true); + pca.removePerformanceCallback(callbackId); + }); + const response = await pca.acquireTokenPopup({ scopes: ["User.Read"], account: testAccount, @@ -2702,8 +2761,8 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { }); expect(response).toBe(testTokenResponse); - expect(nativeAcquireTokenSpy).toHaveBeenCalledTimes(0); - expect(popupSpy).toHaveBeenCalledTimes(1); + expect(nativeAcquireTokenSpy).toHaveBeenCalledTimes(1); + expect(popupSpy).toHaveBeenCalledTimes(0); }); it("falls back to web flow if native broker call fails due to fatal error", async () => { diff --git a/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts b/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts index a296408819..b9d5f07571 100644 --- a/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/PlatformAuthInteractionClient.spec.ts @@ -617,23 +617,31 @@ describe("PlatformAuthInteractionClient Tests", () => { expect(response.expiresOn).toBeDefined(); }); - it("throws if prompt: select_account", (done) => { - platformAuthInteractionClient - .acquireToken({ - scopes: ["User.Read"], - prompt: Constants.PromptValue.SELECT_ACCOUNT, - }) - .catch((e) => { - expect(e.errorCode).toBe( - BrowserAuthErrorCodes.nativePromptNotSupported - ); - expect(e.errorMessage).toBe( - getDefaultErrorMessage( - BrowserAuthErrorCodes.nativePromptNotSupported - ) - ); - done(); - }); + it("prompt: select_account succeeds", async () => { + jest.spyOn( + PlatformAuthExtensionHandler.prototype, + "sendMessage" + ).mockImplementation((): Promise => { + return Promise.resolve(MOCK_WAM_RESPONSE); + }); + const response = await platformAuthInteractionClient.acquireToken({ + scopes: ["User.Read"], + prompt: Constants.PromptValue.SELECT_ACCOUNT, + }); + expect(response.accessToken).toEqual( + MOCK_WAM_RESPONSE.access_token + ); + expect(response.idToken).toEqual(MOCK_WAM_RESPONSE.id_token); + expect(response.uniqueId).toEqual(ID_TOKEN_CLAIMS.oid); + expect(response.tenantId).toEqual(ID_TOKEN_CLAIMS.tid); + expect(response.idTokenClaims).toEqual(ID_TOKEN_CLAIMS); + expect(response.authority).toEqual(TEST_CONFIG.validAuthority); + expect(response.scopes).toContain(MOCK_WAM_RESPONSE.scope); + expect(response.correlationId).toEqual(RANDOM_TEST_GUID); + expect(response.account).toEqual(TEST_ACCOUNT_INFO); + expect(response.tokenType).toEqual( + Constants.AuthenticationScheme.BEARER + ); }); it("throws if prompt: create", (done) => { From ad9770a23073e967ae86695f44d20fa44a07e3dc Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 22 Oct 2025 15:00:34 -0700 Subject: [PATCH 4/6] Update lib/msal-browser/test/app/PublicClientApplication.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/msal-browser/test/app/PublicClientApplication.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index e2f26b1f6e..9c215a6413 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -2632,7 +2632,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { stubExtensionProvider(config); await pca.initialize(); - //Implementation of PCA was moved to controller. + // Implementation of PCA was moved to controller. pca = (pca as any).controller; const testAccount = BASIC_NATIVE_TEST_ACCOUNT_INFO; From 155b2561559f91ff457f7b37348b2f84855eddbc Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 22 Oct 2025 15:00:57 -0700 Subject: [PATCH 5/6] Update change/@azure-msal-browser-prompt-select-account-msal-v5.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- change/@azure-msal-browser-prompt-select-account-msal-v5.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-browser-prompt-select-account-msal-v5.json b/change/@azure-msal-browser-prompt-select-account-msal-v5.json index cf5b56b0f1..4259b4d38a 100644 --- a/change/@azure-msal-browser-prompt-select-account-msal-v5.json +++ b/change/@azure-msal-browser-prompt-select-account-msal-v5.json @@ -1,6 +1,6 @@ { "type": "patch", - "comment": "Add prompt=select_account support for native flows in msal-v5 (#8062)", + "comment": "Add prompt=select_account support for native flows in msal-v5 [#8062](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8062)", "packageName": "@azure/msal-browser", "email": "github-copilot@microsoft.com", "dependentChangeType": "patch" From f7e4b11918d1107a324d7322e4a16d152930d884 Mon Sep 17 00:00:00 2001 From: Sameera Gajjarapu Date: Wed, 22 Oct 2025 19:06:33 -0700 Subject: [PATCH 6/6] Apply suggestion from @sameerag --- change/@azure-msal-browser-prompt-select-account-msal-v5.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-browser-prompt-select-account-msal-v5.json b/change/@azure-msal-browser-prompt-select-account-msal-v5.json index 4259b4d38a..c2fdc9974c 100644 --- a/change/@azure-msal-browser-prompt-select-account-msal-v5.json +++ b/change/@azure-msal-browser-prompt-select-account-msal-v5.json @@ -1,6 +1,6 @@ { "type": "patch", - "comment": "Add prompt=select_account support for native flows in msal-v5 [#8062](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8062)", + "comment": "Add prompt=select_account support for native flows in msal-v5 [#8062](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8063)", "packageName": "@azure/msal-browser", "email": "github-copilot@microsoft.com", "dependentChangeType": "patch"