From 64717b52ac1d5ebde358fe9b6cfdcce44a9d67c2 Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 10 Nov 2025 11:55:23 -0800 Subject: [PATCH 1/8] Remove extraQueryParameters and extraParameters fields from Request types --- lib/msal-node/src/client/DeviceCodeClient.ts | 32 +-------- .../src/client/PublicClientApplication.ts | 4 -- .../src/request/AuthorizationCodeRequest.ts | 4 +- .../src/request/AuthorizationUrlRequest.ts | 4 +- .../src/request/ClientCredentialRequest.ts | 2 - .../request/CommonClientCredentialRequest.ts | 7 +- .../src/request/CommonDeviceCodeRequest.ts | 7 +- .../src/request/CommonOnBehalfOfRequest.ts | 7 +- .../request/CommonUsernamePasswordRequest.ts | 7 +- .../src/request/DeviceCodeRequest.ts | 1 - .../src/request/InteractiveRequest.ts | 6 +- .../src/request/OnBehalfOfRequest.ts | 2 - .../src/request/RefreshTokenRequest.ts | 4 +- .../src/request/SilentFlowRequest.ts | 11 ++- .../src/request/UsernamePasswordRequest.ts | 2 - .../client/ClientCredentialClient.spec.ts | 39 ---------- .../test/client/DeviceCodeClient.spec.ts | 35 --------- .../test/client/OnBehalfOfClient.spec.ts | 40 ----------- .../client/PublicClientApplication.spec.ts | 71 ------------------- .../client/UsernamePasswordClient.spec.ts | 42 ----------- 20 files changed, 36 insertions(+), 291 deletions(-) diff --git a/lib/msal-node/src/client/DeviceCodeClient.ts b/lib/msal-node/src/client/DeviceCodeClient.ts index b36e16ca42..3a2b0b6346 100644 --- a/lib/msal-node/src/client/DeviceCodeClient.ts +++ b/lib/msal-node/src/client/DeviceCodeClient.ts @@ -78,11 +78,7 @@ export class DeviceCodeClient extends BaseClient { private async getDeviceCode( request: CommonDeviceCodeRequest ): Promise { - const queryParametersString = this.createExtraQueryParameters(request); - const endpoint = UrlString.appendQueryString( - this.authority.deviceCodeEndpoint, - queryParametersString - ); + const endpoint = this.authority.deviceCodeEndpoint; const queryString = this.createQueryString(request); const headers = this.createTokenRequestHeaders(); const thumbprint: RequestThumbprint = { @@ -106,25 +102,6 @@ export class DeviceCodeClient extends BaseClient { ); } - /** - * Creates query string for the device code request - * @param request - developer provided CommonDeviceCodeRequest - */ - public createExtraQueryParameters( - request: CommonDeviceCodeRequest - ): string { - const parameters = new Map(); - - if (request.extraQueryParameters) { - RequestParameterBuilder.addExtraParameters( - parameters, - request.extraQueryParameters - ); - } - - return UrlUtils.mapToQueryString(parameters); - } - /** * Executes POST request to device code endpoint * @param deviceCodeEndpoint - token endpoint @@ -182,13 +159,6 @@ export class DeviceCodeClient extends BaseClient { this.config.authOptions.clientId ); - if (request.extraQueryParameters) { - RequestParameterBuilder.addExtraParameters( - parameters, - request.extraQueryParameters - ); - } - if ( request.claims || (this.config.authOptions.clientCapabilities && diff --git a/lib/msal-node/src/client/PublicClientApplication.ts b/lib/msal-node/src/client/PublicClientApplication.ts index b46e564eae..0a3fce958d 100644 --- a/lib/msal-node/src/client/PublicClientApplication.ts +++ b/lib/msal-node/src/client/PublicClientApplication.ts @@ -165,8 +165,6 @@ export class PublicClientApplication authority: request.authority || this.config.auth.authority, correlationId: correlationId, extraParameters: { - ...remainingProperties.extraQueryParameters, - ...remainingProperties.extraParameters, [AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus, }, accountId: remainingProperties.account?.nativeAccountId, @@ -261,8 +259,6 @@ export class PublicClientApplication authority: request.authority || this.config.auth.authority, correlationId: correlationId, extraParameters: { - ...request.extraQueryParameters, - ...request.extraParameters, [AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus, }, accountId: request.account.nativeAccountId, diff --git a/lib/msal-node/src/request/AuthorizationCodeRequest.ts b/lib/msal-node/src/request/AuthorizationCodeRequest.ts index b3c8467a95..2f3dc2692d 100644 --- a/lib/msal-node/src/request/AuthorizationCodeRequest.ts +++ b/lib/msal-node/src/request/AuthorizationCodeRequest.ts @@ -13,8 +13,6 @@ import { CommonAuthorizationCodeRequest } from "@azure/msal-common/node"; * - authority: - URL of the authority, the security token service (STS) from which MSAL will acquire tokens. If authority is set on client application object, this will override that value. Overriding the value will cause for authority validation to happen each time. If the same authority will be used for all request, set on the application object instead of the requests. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - redirectUri - The redirect URI of your app, where the authority will redirect to after the user inputs credentials and consents. It must exactly match one of the redirect URIs you registered in the portal. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - code - The authorization_code that the user acquired in the first leg of the flow. * - codeVerifier - The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request.For more information, see the PKCE RFC: https://tools.ietf.org/html/rfc7636 * - state - Unique GUID generated by the user that is cached by the user and sent to the server during the first leg of the flow. This string is sent back by the server with the authorization code. The user cached state is then compared with the state received from the server to mitigate the risk of CSRF attacks. See https://datatracker.ietf.org/doc/html/rfc6819#section-3.6. @@ -30,6 +28,8 @@ export type AuthorizationCodeRequest = Partial< | "resourceRequestMethod" | "resourceRequestUri" | "storeInCache" + | "extraQueryParameters" + | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/AuthorizationUrlRequest.ts b/lib/msal-node/src/request/AuthorizationUrlRequest.ts index 55a1fa8915..2c69cc7025 100644 --- a/lib/msal-node/src/request/AuthorizationUrlRequest.ts +++ b/lib/msal-node/src/request/AuthorizationUrlRequest.ts @@ -28,8 +28,6 @@ import { CommonAuthorizationUrlRequest } from "@azure/msal-common/node"; * - loginHint - Can be used to pre-fill the username/email address field of the sign-in page for the user, if you know the username/email address ahead of time. Often apps use this parameter during re-authentication, having already extracted the username from a previous sign-in using the preferred_username claim. * - sid - Session ID, unique identifier for the session. Available as an optional claim on ID tokens. * - domainHint - Provides a hint about the tenant or domain that the user should use to sign in. The value of the domain hint is a registered domain for the tenant. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - nonce - A value included in the request that is returned in the id token. A randomly generated unique value is typically used to mitigate replay attacks. * @public */ @@ -42,6 +40,8 @@ export type AuthorizationUrlRequest = Partial< | "resourceRequestUri" | "authenticationScheme" | "storeInCache" + | "extraQueryParameters" + | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/ClientCredentialRequest.ts b/lib/msal-node/src/request/ClientCredentialRequest.ts index 9f2b41a521..d47c5bbc35 100644 --- a/lib/msal-node/src/request/ClientCredentialRequest.ts +++ b/lib/msal-node/src/request/ClientCredentialRequest.ts @@ -13,8 +13,6 @@ import { CommonClientCredentialRequest } from "./CommonClientCredentialRequest.j * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - skipCache - Skip token cache lookup and force request to authority to get a a new token. Defaults to false. * - clientAssertion - An assertion string or a callback function that returns an assertion string (both are Base64Url-encoded signed JWTs) used in the Client Credential flow - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * @public */ export type ClientCredentialRequest = Partial< diff --git a/lib/msal-node/src/request/CommonClientCredentialRequest.ts b/lib/msal-node/src/request/CommonClientCredentialRequest.ts index 6a43e5b3d7..b3565fa3a8 100644 --- a/lib/msal-node/src/request/CommonClientCredentialRequest.ts +++ b/lib/msal-node/src/request/CommonClientCredentialRequest.ts @@ -18,10 +18,11 @@ import { * - preferredAzureRegionOptions - Options of the user's preferred azure region * - clientAssertion - An assertion string or a callback function that returns an assertion string (both are Base64Url-encoded signed JWTs) used in the Client Credential flow * - azureRegion - Azure region to be used for regional authentication - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonClientCredentialRequest = BaseAuthRequest & { +export type CommonClientCredentialRequest = Omit< + BaseAuthRequest, + "extraQueryParameters" | "extraParameters" +> & { skipCache?: boolean; azureRegion?: AzureRegion; clientAssertion?: ClientAssertion; diff --git a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts index 1a5e2a1518..2bbc92cbd7 100644 --- a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts +++ b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts @@ -5,7 +5,6 @@ import { DeviceCodeResponse, - StringDict, BaseAuthRequest, } from "@azure/msal-common/node"; @@ -22,9 +21,11 @@ import { * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonDeviceCodeRequest = BaseAuthRequest & { +export type CommonDeviceCodeRequest = Omit< + BaseAuthRequest, + "extraQueryParameters" | "extraParameters" +> & { deviceCodeCallback: (response: DeviceCodeResponse) => void; cancel?: boolean; timeout?: number; - extraQueryParameters?: StringDict; }; diff --git a/lib/msal-node/src/request/CommonOnBehalfOfRequest.ts b/lib/msal-node/src/request/CommonOnBehalfOfRequest.ts index d610e88246..c107ab48bb 100644 --- a/lib/msal-node/src/request/CommonOnBehalfOfRequest.ts +++ b/lib/msal-node/src/request/CommonOnBehalfOfRequest.ts @@ -11,10 +11,11 @@ import { BaseAuthRequest } from "@azure/msal-common/node"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - oboAssertion - The access token that was sent to the middle-tier API. This token must have an audience of the app making this OBO request. * - skipCache - Skip token cache lookup and force request to authority to get a a new token. Defaults to false. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonOnBehalfOfRequest = BaseAuthRequest & { +export type CommonOnBehalfOfRequest = Omit< + BaseAuthRequest, + "extraQueryParameters" | "extraParameters" +> & { oboAssertion: string; skipCache?: boolean; }; diff --git a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts index f092fa6efe..338ab2068b 100644 --- a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts @@ -15,10 +15,11 @@ import { BaseAuthRequest } from "@azure/msal-common/node"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - username - username of the client * - password - credentials - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonUsernamePasswordRequest = BaseAuthRequest & { +export type CommonUsernamePasswordRequest = Omit< + BaseAuthRequest, + "extraQueryParameters" | "extraParameters" +> & { username: string; password: string; }; diff --git a/lib/msal-node/src/request/DeviceCodeRequest.ts b/lib/msal-node/src/request/DeviceCodeRequest.ts index af9ab93116..d1e5c8bbb6 100644 --- a/lib/msal-node/src/request/DeviceCodeRequest.ts +++ b/lib/msal-node/src/request/DeviceCodeRequest.ts @@ -13,7 +13,6 @@ import { CommonDeviceCodeRequest } from "./CommonDeviceCodeRequest.js"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - deviceCodeCallback - Callback containing device code response. Message should be shown to end user. End user can then navigate to the verification_uri, input the user_code, and input credentials. * - cancel - Boolean to cancel polling of device code endpoint. While the user authenticates on a separate device, MSAL polls the the token endpoint of security token service for the interval specified in the device code response (usually 15 minutes). To stop polling and cancel the request, set cancel=true. - * - extraQueryParameters - String to string map of custom query parameters added to the query string * @public */ export type DeviceCodeRequest = Partial< diff --git a/lib/msal-node/src/request/InteractiveRequest.ts b/lib/msal-node/src/request/InteractiveRequest.ts index 9dbe7debee..ca1102233f 100644 --- a/lib/msal-node/src/request/InteractiveRequest.ts +++ b/lib/msal-node/src/request/InteractiveRequest.ts @@ -20,7 +20,11 @@ import { ILoopbackClient } from "../network/ILoopbackClient.js"; export type InteractiveRequest = Partial< Omit< CommonAuthorizationUrlRequest, - "scopes" | "redirectUri" | "storeInCache" + | "scopes" + | "redirectUri" + | "storeInCache" + | "extraQueryParameters" + | "extraParameters" > > & { openBrowser: (url: string) => Promise; diff --git a/lib/msal-node/src/request/OnBehalfOfRequest.ts b/lib/msal-node/src/request/OnBehalfOfRequest.ts index 5d916c8c24..5ba1fb7876 100644 --- a/lib/msal-node/src/request/OnBehalfOfRequest.ts +++ b/lib/msal-node/src/request/OnBehalfOfRequest.ts @@ -11,8 +11,6 @@ import { CommonOnBehalfOfRequest } from "./CommonOnBehalfOfRequest.js"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - oboAssertion - The access token that was sent to the middle-tier API. This token must have an audience of the app making this OBO request. * - skipCache - Skip token cache lookup and force request to authority to get a a new token. Defaults to false. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * @public */ export type OnBehalfOfRequest = Partial< diff --git a/lib/msal-node/src/request/RefreshTokenRequest.ts b/lib/msal-node/src/request/RefreshTokenRequest.ts index 6c11a6deec..0d0657b583 100644 --- a/lib/msal-node/src/request/RefreshTokenRequest.ts +++ b/lib/msal-node/src/request/RefreshTokenRequest.ts @@ -12,8 +12,6 @@ import { CommonRefreshTokenRequest } from "@azure/msal-common/node"; * - authority - URL of the authority, the security token service (STS) from which MSAL will acquire tokens. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - refreshToken - A refresh token returned from a previous request to the Identity provider. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - forceCache - Force MSAL to cache a refresh token flow response when there is no account in the cache. Used for migration scenarios. * @public */ @@ -26,6 +24,8 @@ export type RefreshTokenRequest = Partial< | "resourceRequestMethod" | "resourceRequestUri" | "storeInCache" + | "extraQueryParameters" + | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/SilentFlowRequest.ts b/lib/msal-node/src/request/SilentFlowRequest.ts index 0b3b993ecb..1bcd1e4061 100644 --- a/lib/msal-node/src/request/SilentFlowRequest.ts +++ b/lib/msal-node/src/request/SilentFlowRequest.ts @@ -11,14 +11,19 @@ import { AccountInfo, CommonSilentFlowRequest } from "@azure/msal-common/node"; * - claims - A stringified claims request which will be added to all /authorize and /token calls. When included on a silent request, cache lookup will be skipped and token will be refreshed. * - authority - Url of the authority which the application acquires tokens from. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - account - Account entity to lookup the credentials. * - forceRefresh - Forces silent requests to make network calls if true. * @public */ export type SilentFlowRequest = Partial< - Omit + Omit< + CommonSilentFlowRequest, + | "account" + | "scopes" + | "storeInCache" + | "extraQueryParameters" + | "extraParameters" + > > & { account: AccountInfo; scopes: Array; diff --git a/lib/msal-node/src/request/UsernamePasswordRequest.ts b/lib/msal-node/src/request/UsernamePasswordRequest.ts index 37d562ea6a..86ae04180c 100644 --- a/lib/msal-node/src/request/UsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/UsernamePasswordRequest.ts @@ -15,8 +15,6 @@ import { CommonUsernamePasswordRequest } from "./CommonUsernamePasswordRequest.j * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - username - username of the client * - password - credentials - * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests - * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * @public */ export type UsernamePasswordRequest = Partial< diff --git a/lib/msal-node/test/client/ClientCredentialClient.spec.ts b/lib/msal-node/test/client/ClientCredentialClient.spec.ts index ba417fca1f..0b2c6a19c4 100644 --- a/lib/msal-node/test/client/ClientCredentialClient.spec.ts +++ b/lib/msal-node/test/client/ClientCredentialClient.spec.ts @@ -121,45 +121,6 @@ describe("ClientCredentialClient unit tests", () => { checkMockedNetworkRequest(returnVal, checks); }); - it("Adds extraQueryParameters to the /token request", async () => { - const badExecutePostToTokenEndpointMock = jest.spyOn( - ClientCredentialClient.prototype, - "executePostToTokenEndpoint" - ); - // no implementation has been mocked, the acquireToken call will fail - - const fakeConfig: ClientConfiguration = - await ClientTestUtils.createTestClientConfiguration(); - const client: ClientCredentialClient = new ClientCredentialClient( - fakeConfig - ); - - const clientCredentialRequest: CommonClientCredentialRequest = { - authority: TEST_CONFIG.validAuthority, - correlationId: TEST_CONFIG.CORRELATION_ID, - scopes: TEST_CONFIG.DEFAULT_GRAPH_SCOPE, - extraQueryParameters: { - testParam1: "testValue1", - testParam2: "", - testParam3: "testValue3", - }, - }; - - await expect( - client.acquireToken(clientCredentialRequest) - ).rejects.toThrow(); - - if (!badExecutePostToTokenEndpointMock.mock.lastCall) { - fail("executePostToTokenEndpointMock was not called"); - } - const url: string = badExecutePostToTokenEndpointMock.mock - .lastCall[0] as string; - expect( - url.includes("/token?testParam1=testValue1&testParam3=testValue3") - ).toBeTruthy(); - expect(!url.includes("/token?testParam2=")).toBeTruthy(); - }); - it("acquireToken's interactionRequiredAuthError error contains claims", async () => { const errorResponse = { error: "interaction_required", diff --git a/lib/msal-node/test/client/DeviceCodeClient.spec.ts b/lib/msal-node/test/client/DeviceCodeClient.spec.ts index e5d9ea9fcd..7d2465a721 100644 --- a/lib/msal-node/test/client/DeviceCodeClient.spec.ts +++ b/lib/msal-node/test/client/DeviceCodeClient.spec.ts @@ -154,41 +154,6 @@ describe("DeviceCodeClient unit tests", () => { checkMockedNetworkRequest(returnVal, returnValChecks); }, 6000); - it("Adds extraQueryParameters to the /devicecode url", async () => { - const deviceCodeRequest: CommonDeviceCodeRequest = { - authority: TEST_CONFIG.validAuthority, - correlationId: "test-correlationId", - scopes: [ - ...TEST_CONFIG.DEFAULT_GRAPH_SCOPE, - ...TEST_CONFIG.DEFAULT_SCOPES, - ], - extraQueryParameters: { - testParam1: "testValue1", - testParam2: "", - testParam3: "testValue3", - }, - deviceCodeCallback: () => {}, - }; - - const client = new DeviceCodeClient(config); - await client.acquireToken(deviceCodeRequest); - - if (!executePostRequestToDeviceCodeEndpointSpy.mock.lastCall) { - fail("executePostRequestToDeviceCodeEndpoint was not called"); - } - const deviceCodeUrl: string = - executePostRequestToDeviceCodeEndpointSpy.mock - .lastCall[0] as string; - expect( - deviceCodeUrl.includes( - "/devicecode?testParam1=testValue1&testParam3=testValue3" - ) - ).toBeTruthy(); - expect( - !deviceCodeUrl.includes("/devicecode?testParam2=") - ).toBeTruthy(); - }); - it("Adds claims to request", async () => { let deviceCodeResponse = null; const request: CommonDeviceCodeRequest = { diff --git a/lib/msal-node/test/client/OnBehalfOfClient.spec.ts b/lib/msal-node/test/client/OnBehalfOfClient.spec.ts index 987803421a..bf7dce39c8 100644 --- a/lib/msal-node/test/client/OnBehalfOfClient.spec.ts +++ b/lib/msal-node/test/client/OnBehalfOfClient.spec.ts @@ -231,46 +231,6 @@ describe("OnBehalfOf unit tests", () => { ); }); - it("Adds extraQueryParameters to the /token request", async () => { - const badExecutePostToTokenEndpointMock = jest.spyOn( - OnBehalfOfClient.prototype, - "executePostToTokenEndpoint" - ); - // no implementation has been mocked, the acquireToken call will fail - - const fakeConfig: ClientConfiguration = - await ClientTestUtils.createTestClientConfiguration(); - const client: OnBehalfOfClient = new OnBehalfOfClient(fakeConfig); - - const oboRequest: CommonOnBehalfOfRequest = { - scopes: [...TEST_CONFIG.DEFAULT_GRAPH_SCOPE], - authority: TEST_CONFIG.validAuthority, - correlationId: TEST_CONFIG.CORRELATION_ID, - oboAssertion: "user_assertion_hash", - skipCache: true, - claims: TEST_CONFIG.CLAIMS, - extraQueryParameters: { - testParam1: "testValue1", - testParam2: "", - testParam3: "testValue3", - }, - }; - - await expect(client.acquireToken(oboRequest)).rejects.toThrow(); - - if (!badExecutePostToTokenEndpointMock.mock.lastCall) { - fail("executePostToTokenEndpointMock was not called"); - } - const url: string = badExecutePostToTokenEndpointMock.mock - .lastCall[0] as string; - expect( - url.includes( - "/token?testParam1=testValue1&testParam3=testValue3" - ) - ).toBeTruthy(); - expect(!url.includes("/token?testParam2=")).toBeTruthy(); - }); - it("Does not add claims when empty object provided", async () => { const client = new OnBehalfOfClient(config); diff --git a/lib/msal-node/test/client/PublicClientApplication.spec.ts b/lib/msal-node/test/client/PublicClientApplication.spec.ts index 51a4a0a812..2e0c0664fc 100644 --- a/lib/msal-node/test/client/PublicClientApplication.spec.ts +++ b/lib/msal-node/test/client/PublicClientApplication.spec.ts @@ -559,77 +559,6 @@ describe("PublicClientApplication", () => { testAccessTokenEntity.clientId ); }); - - it("Adds extraQueryParameters to the /token request", (done) => { - AUTHENTICATION_RESULT.body.client_info = - TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; - jest.spyOn( - RefreshTokenClient.prototype, - "executePostToTokenEndpoint" - ) - // @ts-expect-error - .mockImplementation((url: string) => { - try { - expect( - url.includes( - "/token?testParam1=testValue1&testParam3=testValue3" - ) - ).toBeTruthy(); - expect( - !url.includes("/token?testParam2=") - ).toBeTruthy(); - done(); - return AUTHENTICATION_RESULT; - } catch (error) { - done(error); - return error; - } - }); - jest.spyOn( - Authority.prototype, - "getEndpointMetadataFromNetwork" - ).mockResolvedValue(DEFAULT_OPENID_CONFIG_RESPONSE.body); - testAccessTokenEntity.refreshOn = `${ - Number(testAccessTokenEntity.cachedAt) - 1 - }`; - testAccessTokenEntity.expiresOn = `${ - Number(testAccessTokenEntity.cachedAt) + - AUTHENTICATION_RESULT.body.expires_in - }`; - jest.spyOn(CacheManager.prototype, "getIdToken").mockReturnValue( - testIdToken - ); - jest.spyOn( - CacheManager.prototype, - "getAccessToken" - ).mockReturnValue(testAccessTokenEntity); - jest.spyOn( - CacheManager.prototype, - "getRefreshToken" - ).mockReturnValue(testRefreshTokenEntity); - jest.spyOn( - MockStorageClass.prototype, - "getAccount" - ).mockReturnValue(testAccountEntity); - - const silentFlowRequest: CommonSilentFlowRequest = { - scopes: TEST_CONFIG.DEFAULT_GRAPH_SCOPE, - account: testAccount, - authority: TEST_CONFIG.validAuthority, - correlationId: TEST_CONFIG.CORRELATION_ID, - forceRefresh: false, - extraQueryParameters: { - testParam1: "testValue1", - testParam2: "", - testParam3: "testValue3", - }, - }; - - const authApp = new PublicClientApplication(appConfig); - authApp.acquireTokenSilent(silentFlowRequest).catch(() => { - // Catch errors thrown after the function call this test is testing - }); - }); }); describe("acquireTokenInteractive tests", () => { diff --git a/lib/msal-node/test/client/UsernamePasswordClient.spec.ts b/lib/msal-node/test/client/UsernamePasswordClient.spec.ts index 220cb761ba..6390296672 100644 --- a/lib/msal-node/test/client/UsernamePasswordClient.spec.ts +++ b/lib/msal-node/test/client/UsernamePasswordClient.spec.ts @@ -116,48 +116,6 @@ describe("Username Password unit tests", () => { checkMockedNetworkRequest(returnVal, checks); }); - it("Adds extraQueryParameters to the /token request", async () => { - const badExecutePostToTokenEndpointMock = jest.spyOn( - UsernamePasswordClient.prototype, - "executePostToTokenEndpoint" - ); - // no implementation has been mocked, the acquireToken call will fail - - const fakeConfig: ClientConfiguration = - await ClientTestUtils.createTestClientConfiguration(); - const client: UsernamePasswordClient = new UsernamePasswordClient( - fakeConfig - ); - - const usernamePasswordRequest: CommonUsernamePasswordRequest = { - authority: Constants.DEFAULT_AUTHORITY, - scopes: TEST_CONFIG.DEFAULT_GRAPH_SCOPE, - username: MOCK_USERNAME, - password: MOCK_PASSWORD, - claims: TEST_CONFIG.CLAIMS, - correlationId: RANDOM_TEST_GUID, - extraQueryParameters: { - testParam1: "testValue1", - testParam2: "", - testParam3: "testValue3", - }, - }; - - await expect( - client.acquireToken(usernamePasswordRequest) - ).rejects.toThrow(); - - if (!badExecutePostToTokenEndpointMock.mock.lastCall) { - fail("executePostToTokenEndpointMock was not called"); - } - const url: string = badExecutePostToTokenEndpointMock.mock - .lastCall[0] as string; - expect( - url.includes("/token?testParam1=testValue1&testParam3=testValue3") - ).toBeTruthy(); - expect(!url.includes("/token?testParam2=")).toBeTruthy(); - }); - it("properly encodes special characters in emails (usernames)", async () => { const client = new UsernamePasswordClient(config); From 67b2a94e5d58feb3c41cf6cacedc4a59173c9792 Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 10 Nov 2025 12:05:16 -0800 Subject: [PATCH 2/8] Change files --- ...ure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json diff --git a/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json b/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json new file mode 100644 index 0000000000..48216c9820 --- /dev/null +++ b/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json @@ -0,0 +1,7 @@ +{ + "type": "major", + "comment": "Remove extraQueryParameters and extraParameters fields from Request types (#8136)", + "packageName": "@azure/msal-node", + "email": "avdunn@microsoft.com", + "dependentChangeType": "patch" +} From 620ab430f34fd57aa3deef39f5783288bb7995fd Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 10 Nov 2025 12:07:19 -0800 Subject: [PATCH 3/8] Ran apiExtractor --- lib/msal-node/apiReview/msal-node.api.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/msal-node/apiReview/msal-node.api.md b/lib/msal-node/apiReview/msal-node.api.md index f1c25a4c2b..549d91f20c 100644 --- a/lib/msal-node/apiReview/msal-node.api.md +++ b/lib/msal-node/apiReview/msal-node.api.md @@ -70,7 +70,6 @@ import { ServerError } from '@azure/msal-common/node'; import { ServerTelemetryEntity } from '@azure/msal-common/node'; import { ServerTelemetryManager } from '@azure/msal-common/node'; import { StaticAuthorityOptions } from '@azure/msal-common/node'; -import { StringDict } from '@azure/msal-common/node'; import { ThrottlingEntity } from '@azure/msal-common/node'; import { TokenCacheContext } from '@azure/msal-common/node'; import { TokenKeys } from '@azure/msal-common/node'; @@ -92,7 +91,7 @@ export { AuthErrorCodes } export { AuthorizationCodePayload } // @public -export type AuthorizationCodeRequest = Partial> & { +export type AuthorizationCodeRequest = Partial> & { scopes: Array; redirectUri: string; code: string; @@ -100,7 +99,7 @@ export type AuthorizationCodeRequest = Partial> & { +export type AuthorizationUrlRequest = Partial> & { scopes: Array; redirectUri: string; }; @@ -237,7 +236,6 @@ export class DeviceCodeClient extends BaseClient { constructor(configuration: ClientConfiguration); // Warning: (ae-forgotten-export) The symbol "CommonDeviceCodeRequest" needs to be exported by the entry point index.d.ts acquireToken(request: CommonDeviceCodeRequest): Promise; - createExtraQueryParameters(request: CommonDeviceCodeRequest): string; } // @public @@ -310,7 +308,7 @@ export { InteractionRequiredAuthError } export { InteractionRequiredAuthErrorCodes } // @public -export type InteractiveRequest = Partial> & { +export type InteractiveRequest = Partial> & { openBrowser: (url: string) => Promise; scopes?: Array; successTemplate?: string; @@ -492,7 +490,7 @@ export class PublicClientApplication extends ClientApplication implements IPubli } // @public -export type RefreshTokenRequest = Partial> & { +export type RefreshTokenRequest = Partial> & { scopes: Array; refreshToken: string; forceCache?: boolean; @@ -589,7 +587,7 @@ export type SignOutRequest = { }; // @public -export type SilentFlowRequest = Partial> & { +export type SilentFlowRequest = Partial> & { account: AccountInfo; scopes: Array; }; From 35546f57b665aaef4d5eaced62c1890b46e5aa98 Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 10 Nov 2025 16:10:55 -0800 Subject: [PATCH 4/8] Ran Prettier --- lib/msal-node/src/request/CommonDeviceCodeRequest.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts index 2bbc92cbd7..cefe2974ac 100644 --- a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts +++ b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts @@ -3,10 +3,7 @@ * Licensed under the MIT License. */ -import { - DeviceCodeResponse, - BaseAuthRequest, -} from "@azure/msal-common/node"; +import { DeviceCodeResponse, BaseAuthRequest } from "@azure/msal-common/node"; /** * Parameters for Oauth2 device code flow. From f0d97b9fdb97b9fefb251591991f0790b340b6c4 Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 14 Nov 2025 15:31:39 -0800 Subject: [PATCH 5/8] Revert changes to PCA scenarios --- lib/msal-node/src/client/DeviceCodeClient.ts | 25 ++++++- .../src/client/PublicClientApplication.ts | 2 + .../src/request/AuthorizationCodeRequest.ts | 4 +- .../src/request/AuthorizationUrlRequest.ts | 4 +- .../src/request/CommonDeviceCodeRequest.ts | 13 ++-- .../request/CommonUsernamePasswordRequest.ts | 8 +-- .../src/request/DeviceCodeRequest.ts | 1 + .../src/request/InteractiveRequest.ts | 6 +- .../src/request/RefreshTokenRequest.ts | 4 +- .../src/request/SilentFlowRequest.ts | 11 +-- .../src/request/UsernamePasswordRequest.ts | 2 + .../test/client/DeviceCodeClient.spec.ts | 35 +++++++++ .../client/PublicClientApplication.spec.ts | 71 +++++++++++++++++++ .../client/UsernamePasswordClient.spec.ts | 42 +++++++++++ 14 files changed, 199 insertions(+), 29 deletions(-) diff --git a/lib/msal-node/src/client/DeviceCodeClient.ts b/lib/msal-node/src/client/DeviceCodeClient.ts index 3a2b0b6346..408b1d7513 100644 --- a/lib/msal-node/src/client/DeviceCodeClient.ts +++ b/lib/msal-node/src/client/DeviceCodeClient.ts @@ -78,7 +78,11 @@ export class DeviceCodeClient extends BaseClient { private async getDeviceCode( request: CommonDeviceCodeRequest ): Promise { - const endpoint = this.authority.deviceCodeEndpoint; + const queryParametersString = this.createExtraQueryParameters(request); + const endpoint = UrlString.appendQueryString( + this.authority.deviceCodeEndpoint, + queryParametersString + ); const queryString = this.createQueryString(request); const headers = this.createTokenRequestHeaders(); const thumbprint: RequestThumbprint = { @@ -102,6 +106,25 @@ export class DeviceCodeClient extends BaseClient { ); } + /** + * Creates query string for the device code request + * @param request - developer provided CommonDeviceCodeRequest + */ + public createExtraQueryParameters( + request: CommonDeviceCodeRequest + ): string { + const parameters = new Map(); + + if (request.extraQueryParameters) { + RequestParameterBuilder.addExtraParameters( + parameters, + request.extraQueryParameters + ); + } + + return UrlUtils.mapToQueryString(parameters); + } + /** * Executes POST request to device code endpoint * @param deviceCodeEndpoint - token endpoint diff --git a/lib/msal-node/src/client/PublicClientApplication.ts b/lib/msal-node/src/client/PublicClientApplication.ts index 0a3fce958d..c00575c8a0 100644 --- a/lib/msal-node/src/client/PublicClientApplication.ts +++ b/lib/msal-node/src/client/PublicClientApplication.ts @@ -165,6 +165,8 @@ export class PublicClientApplication authority: request.authority || this.config.auth.authority, correlationId: correlationId, extraParameters: { + ...remainingProperties.extraQueryParameters, + ...remainingProperties.extraParameters, [AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus, }, accountId: remainingProperties.account?.nativeAccountId, diff --git a/lib/msal-node/src/request/AuthorizationCodeRequest.ts b/lib/msal-node/src/request/AuthorizationCodeRequest.ts index 2f3dc2692d..b3c8467a95 100644 --- a/lib/msal-node/src/request/AuthorizationCodeRequest.ts +++ b/lib/msal-node/src/request/AuthorizationCodeRequest.ts @@ -13,6 +13,8 @@ import { CommonAuthorizationCodeRequest } from "@azure/msal-common/node"; * - authority: - URL of the authority, the security token service (STS) from which MSAL will acquire tokens. If authority is set on client application object, this will override that value. Overriding the value will cause for authority validation to happen each time. If the same authority will be used for all request, set on the application object instead of the requests. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - redirectUri - The redirect URI of your app, where the authority will redirect to after the user inputs credentials and consents. It must exactly match one of the redirect URIs you registered in the portal. + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - code - The authorization_code that the user acquired in the first leg of the flow. * - codeVerifier - The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request.For more information, see the PKCE RFC: https://tools.ietf.org/html/rfc7636 * - state - Unique GUID generated by the user that is cached by the user and sent to the server during the first leg of the flow. This string is sent back by the server with the authorization code. The user cached state is then compared with the state received from the server to mitigate the risk of CSRF attacks. See https://datatracker.ietf.org/doc/html/rfc6819#section-3.6. @@ -28,8 +30,6 @@ export type AuthorizationCodeRequest = Partial< | "resourceRequestMethod" | "resourceRequestUri" | "storeInCache" - | "extraQueryParameters" - | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/AuthorizationUrlRequest.ts b/lib/msal-node/src/request/AuthorizationUrlRequest.ts index 2c69cc7025..55a1fa8915 100644 --- a/lib/msal-node/src/request/AuthorizationUrlRequest.ts +++ b/lib/msal-node/src/request/AuthorizationUrlRequest.ts @@ -28,6 +28,8 @@ import { CommonAuthorizationUrlRequest } from "@azure/msal-common/node"; * - loginHint - Can be used to pre-fill the username/email address field of the sign-in page for the user, if you know the username/email address ahead of time. Often apps use this parameter during re-authentication, having already extracted the username from a previous sign-in using the preferred_username claim. * - sid - Session ID, unique identifier for the session. Available as an optional claim on ID tokens. * - domainHint - Provides a hint about the tenant or domain that the user should use to sign in. The value of the domain hint is a registered domain for the tenant. + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - nonce - A value included in the request that is returned in the id token. A randomly generated unique value is typically used to mitigate replay attacks. * @public */ @@ -40,8 +42,6 @@ export type AuthorizationUrlRequest = Partial< | "resourceRequestUri" | "authenticationScheme" | "storeInCache" - | "extraQueryParameters" - | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts index cefe2974ac..4c01542814 100644 --- a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts +++ b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts @@ -3,7 +3,11 @@ * Licensed under the MIT License. */ -import { DeviceCodeResponse, BaseAuthRequest } from "@azure/msal-common/node"; +import { + DeviceCodeResponse, + StringDict, + BaseAuthRequest, +} from "@azure/msal-common/node"; /** * Parameters for Oauth2 device code flow. @@ -18,11 +22,10 @@ import { DeviceCodeResponse, BaseAuthRequest } from "@azure/msal-common/node"; * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonDeviceCodeRequest = Omit< - BaseAuthRequest, - "extraQueryParameters" | "extraParameters" -> & { +export type CommonDeviceCodeRequest = +BaseAuthRequest & { deviceCodeCallback: (response: DeviceCodeResponse) => void; cancel?: boolean; timeout?: number; + extraQueryParameters?: StringDict; }; diff --git a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts index 338ab2068b..1e85ea1273 100644 --- a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts @@ -15,11 +15,11 @@ import { BaseAuthRequest } from "@azure/msal-common/node"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - username - username of the client * - password - credentials + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonUsernamePasswordRequest = Omit< - BaseAuthRequest, - "extraQueryParameters" | "extraParameters" -> & { +export type CommonUsernamePasswordRequest = +BaseAuthRequest & { username: string; password: string; }; diff --git a/lib/msal-node/src/request/DeviceCodeRequest.ts b/lib/msal-node/src/request/DeviceCodeRequest.ts index d1e5c8bbb6..36207fa494 100644 --- a/lib/msal-node/src/request/DeviceCodeRequest.ts +++ b/lib/msal-node/src/request/DeviceCodeRequest.ts @@ -13,6 +13,7 @@ import { CommonDeviceCodeRequest } from "./CommonDeviceCodeRequest.js"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - deviceCodeCallback - Callback containing device code response. Message should be shown to end user. End user can then navigate to the verification_uri, input the user_code, and input credentials. * - cancel - Boolean to cancel polling of device code endpoint. While the user authenticates on a separate device, MSAL polls the the token endpoint of security token service for the interval specified in the device code response (usually 15 minutes). To stop polling and cancel the request, set cancel=true. + * - extraQueryParameters - String to string map of custom query parameters added to the query string * @public */ export type DeviceCodeRequest = Partial< diff --git a/lib/msal-node/src/request/InteractiveRequest.ts b/lib/msal-node/src/request/InteractiveRequest.ts index ca1102233f..9dbe7debee 100644 --- a/lib/msal-node/src/request/InteractiveRequest.ts +++ b/lib/msal-node/src/request/InteractiveRequest.ts @@ -20,11 +20,7 @@ import { ILoopbackClient } from "../network/ILoopbackClient.js"; export type InteractiveRequest = Partial< Omit< CommonAuthorizationUrlRequest, - | "scopes" - | "redirectUri" - | "storeInCache" - | "extraQueryParameters" - | "extraParameters" + "scopes" | "redirectUri" | "storeInCache" > > & { openBrowser: (url: string) => Promise; diff --git a/lib/msal-node/src/request/RefreshTokenRequest.ts b/lib/msal-node/src/request/RefreshTokenRequest.ts index 0d0657b583..6c11a6deec 100644 --- a/lib/msal-node/src/request/RefreshTokenRequest.ts +++ b/lib/msal-node/src/request/RefreshTokenRequest.ts @@ -12,6 +12,8 @@ import { CommonRefreshTokenRequest } from "@azure/msal-common/node"; * - authority - URL of the authority, the security token service (STS) from which MSAL will acquire tokens. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - refreshToken - A refresh token returned from a previous request to the Identity provider. + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - forceCache - Force MSAL to cache a refresh token flow response when there is no account in the cache. Used for migration scenarios. * @public */ @@ -24,8 +26,6 @@ export type RefreshTokenRequest = Partial< | "resourceRequestMethod" | "resourceRequestUri" | "storeInCache" - | "extraQueryParameters" - | "extraParameters" > > & { scopes: Array; diff --git a/lib/msal-node/src/request/SilentFlowRequest.ts b/lib/msal-node/src/request/SilentFlowRequest.ts index 1bcd1e4061..0b3b993ecb 100644 --- a/lib/msal-node/src/request/SilentFlowRequest.ts +++ b/lib/msal-node/src/request/SilentFlowRequest.ts @@ -11,19 +11,14 @@ import { AccountInfo, CommonSilentFlowRequest } from "@azure/msal-common/node"; * - claims - A stringified claims request which will be added to all /authorize and /token calls. When included on a silent request, cache lookup will be skipped and token will be refreshed. * - authority - Url of the authority which the application acquires tokens from. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - account - Account entity to lookup the credentials. * - forceRefresh - Forces silent requests to make network calls if true. * @public */ export type SilentFlowRequest = Partial< - Omit< - CommonSilentFlowRequest, - | "account" - | "scopes" - | "storeInCache" - | "extraQueryParameters" - | "extraParameters" - > + Omit > & { account: AccountInfo; scopes: Array; diff --git a/lib/msal-node/src/request/UsernamePasswordRequest.ts b/lib/msal-node/src/request/UsernamePasswordRequest.ts index 86ae04180c..c9466102e8 100644 --- a/lib/msal-node/src/request/UsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/UsernamePasswordRequest.ts @@ -14,6 +14,8 @@ import { CommonUsernamePasswordRequest } from "./CommonUsernamePasswordRequest.j * - authority - Url of the authority which the application acquires tokens from. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - username - username of the client + * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests + * - extraParameters - String to string map of custom query parameters added to outgoing token service requests * - password - credentials * @public */ diff --git a/lib/msal-node/test/client/DeviceCodeClient.spec.ts b/lib/msal-node/test/client/DeviceCodeClient.spec.ts index 7d2465a721..e5d9ea9fcd 100644 --- a/lib/msal-node/test/client/DeviceCodeClient.spec.ts +++ b/lib/msal-node/test/client/DeviceCodeClient.spec.ts @@ -154,6 +154,41 @@ describe("DeviceCodeClient unit tests", () => { checkMockedNetworkRequest(returnVal, returnValChecks); }, 6000); + it("Adds extraQueryParameters to the /devicecode url", async () => { + const deviceCodeRequest: CommonDeviceCodeRequest = { + authority: TEST_CONFIG.validAuthority, + correlationId: "test-correlationId", + scopes: [ + ...TEST_CONFIG.DEFAULT_GRAPH_SCOPE, + ...TEST_CONFIG.DEFAULT_SCOPES, + ], + extraQueryParameters: { + testParam1: "testValue1", + testParam2: "", + testParam3: "testValue3", + }, + deviceCodeCallback: () => {}, + }; + + const client = new DeviceCodeClient(config); + await client.acquireToken(deviceCodeRequest); + + if (!executePostRequestToDeviceCodeEndpointSpy.mock.lastCall) { + fail("executePostRequestToDeviceCodeEndpoint was not called"); + } + const deviceCodeUrl: string = + executePostRequestToDeviceCodeEndpointSpy.mock + .lastCall[0] as string; + expect( + deviceCodeUrl.includes( + "/devicecode?testParam1=testValue1&testParam3=testValue3" + ) + ).toBeTruthy(); + expect( + !deviceCodeUrl.includes("/devicecode?testParam2=") + ).toBeTruthy(); + }); + it("Adds claims to request", async () => { let deviceCodeResponse = null; const request: CommonDeviceCodeRequest = { diff --git a/lib/msal-node/test/client/PublicClientApplication.spec.ts b/lib/msal-node/test/client/PublicClientApplication.spec.ts index 2e0c0664fc..51a4a0a812 100644 --- a/lib/msal-node/test/client/PublicClientApplication.spec.ts +++ b/lib/msal-node/test/client/PublicClientApplication.spec.ts @@ -559,6 +559,77 @@ describe("PublicClientApplication", () => { testAccessTokenEntity.clientId ); }); + + it("Adds extraQueryParameters to the /token request", (done) => { + AUTHENTICATION_RESULT.body.client_info = + TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; + jest.spyOn( + RefreshTokenClient.prototype, + "executePostToTokenEndpoint" + ) + // @ts-expect-error + .mockImplementation((url: string) => { + try { + expect( + url.includes( + "/token?testParam1=testValue1&testParam3=testValue3" + ) + ).toBeTruthy(); + expect( + !url.includes("/token?testParam2=") + ).toBeTruthy(); + done(); + return AUTHENTICATION_RESULT; + } catch (error) { + done(error); + return error; + } + }); + jest.spyOn( + Authority.prototype, + "getEndpointMetadataFromNetwork" + ).mockResolvedValue(DEFAULT_OPENID_CONFIG_RESPONSE.body); + testAccessTokenEntity.refreshOn = `${ + Number(testAccessTokenEntity.cachedAt) - 1 + }`; + testAccessTokenEntity.expiresOn = `${ + Number(testAccessTokenEntity.cachedAt) + + AUTHENTICATION_RESULT.body.expires_in + }`; + jest.spyOn(CacheManager.prototype, "getIdToken").mockReturnValue( + testIdToken + ); + jest.spyOn( + CacheManager.prototype, + "getAccessToken" + ).mockReturnValue(testAccessTokenEntity); + jest.spyOn( + CacheManager.prototype, + "getRefreshToken" + ).mockReturnValue(testRefreshTokenEntity); + jest.spyOn( + MockStorageClass.prototype, + "getAccount" + ).mockReturnValue(testAccountEntity); + + const silentFlowRequest: CommonSilentFlowRequest = { + scopes: TEST_CONFIG.DEFAULT_GRAPH_SCOPE, + account: testAccount, + authority: TEST_CONFIG.validAuthority, + correlationId: TEST_CONFIG.CORRELATION_ID, + forceRefresh: false, + extraQueryParameters: { + testParam1: "testValue1", + testParam2: "", + testParam3: "testValue3", + }, + }; + + const authApp = new PublicClientApplication(appConfig); + authApp.acquireTokenSilent(silentFlowRequest).catch(() => { + // Catch errors thrown after the function call this test is testing + }); + }); }); describe("acquireTokenInteractive tests", () => { diff --git a/lib/msal-node/test/client/UsernamePasswordClient.spec.ts b/lib/msal-node/test/client/UsernamePasswordClient.spec.ts index 6390296672..220cb761ba 100644 --- a/lib/msal-node/test/client/UsernamePasswordClient.spec.ts +++ b/lib/msal-node/test/client/UsernamePasswordClient.spec.ts @@ -116,6 +116,48 @@ describe("Username Password unit tests", () => { checkMockedNetworkRequest(returnVal, checks); }); + it("Adds extraQueryParameters to the /token request", async () => { + const badExecutePostToTokenEndpointMock = jest.spyOn( + UsernamePasswordClient.prototype, + "executePostToTokenEndpoint" + ); + // no implementation has been mocked, the acquireToken call will fail + + const fakeConfig: ClientConfiguration = + await ClientTestUtils.createTestClientConfiguration(); + const client: UsernamePasswordClient = new UsernamePasswordClient( + fakeConfig + ); + + const usernamePasswordRequest: CommonUsernamePasswordRequest = { + authority: Constants.DEFAULT_AUTHORITY, + scopes: TEST_CONFIG.DEFAULT_GRAPH_SCOPE, + username: MOCK_USERNAME, + password: MOCK_PASSWORD, + claims: TEST_CONFIG.CLAIMS, + correlationId: RANDOM_TEST_GUID, + extraQueryParameters: { + testParam1: "testValue1", + testParam2: "", + testParam3: "testValue3", + }, + }; + + await expect( + client.acquireToken(usernamePasswordRequest) + ).rejects.toThrow(); + + if (!badExecutePostToTokenEndpointMock.mock.lastCall) { + fail("executePostToTokenEndpointMock was not called"); + } + const url: string = badExecutePostToTokenEndpointMock.mock + .lastCall[0] as string; + expect( + url.includes("/token?testParam1=testValue1&testParam3=testValue3") + ).toBeTruthy(); + expect(!url.includes("/token?testParam2=")).toBeTruthy(); + }); + it("properly encodes special characters in emails (usernames)", async () => { const client = new UsernamePasswordClient(config); From 6880a1c9655d48148cb23d8cfa505d07b5c6c22b Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 14 Nov 2025 15:35:06 -0800 Subject: [PATCH 6/8] Revert remaining PCA changes --- lib/msal-node/src/client/DeviceCodeClient.ts | 7 +++++++ lib/msal-node/src/client/PublicClientApplication.ts | 2 ++ lib/msal-node/src/request/CommonDeviceCodeRequest.ts | 3 +-- lib/msal-node/src/request/CommonUsernamePasswordRequest.ts | 3 +-- lib/msal-node/src/request/DeviceCodeRequest.ts | 2 +- lib/msal-node/src/request/UsernamePasswordRequest.ts | 2 +- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/msal-node/src/client/DeviceCodeClient.ts b/lib/msal-node/src/client/DeviceCodeClient.ts index 408b1d7513..b36e16ca42 100644 --- a/lib/msal-node/src/client/DeviceCodeClient.ts +++ b/lib/msal-node/src/client/DeviceCodeClient.ts @@ -182,6 +182,13 @@ export class DeviceCodeClient extends BaseClient { this.config.authOptions.clientId ); + if (request.extraQueryParameters) { + RequestParameterBuilder.addExtraParameters( + parameters, + request.extraQueryParameters + ); + } + if ( request.claims || (this.config.authOptions.clientCapabilities && diff --git a/lib/msal-node/src/client/PublicClientApplication.ts b/lib/msal-node/src/client/PublicClientApplication.ts index c00575c8a0..b46e564eae 100644 --- a/lib/msal-node/src/client/PublicClientApplication.ts +++ b/lib/msal-node/src/client/PublicClientApplication.ts @@ -261,6 +261,8 @@ export class PublicClientApplication authority: request.authority || this.config.auth.authority, correlationId: correlationId, extraParameters: { + ...request.extraQueryParameters, + ...request.extraParameters, [AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus, }, accountId: request.account.nativeAccountId, diff --git a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts index 4c01542814..1a5e2a1518 100644 --- a/lib/msal-node/src/request/CommonDeviceCodeRequest.ts +++ b/lib/msal-node/src/request/CommonDeviceCodeRequest.ts @@ -22,8 +22,7 @@ import { * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonDeviceCodeRequest = -BaseAuthRequest & { +export type CommonDeviceCodeRequest = BaseAuthRequest & { deviceCodeCallback: (response: DeviceCodeResponse) => void; cancel?: boolean; timeout?: number; diff --git a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts index 1e85ea1273..f092fa6efe 100644 --- a/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/CommonUsernamePasswordRequest.ts @@ -18,8 +18,7 @@ import { BaseAuthRequest } from "@azure/msal-common/node"; * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests * - extraParameters - String to string map of custom query parameters added to outgoing token service requests */ -export type CommonUsernamePasswordRequest = -BaseAuthRequest & { +export type CommonUsernamePasswordRequest = BaseAuthRequest & { username: string; password: string; }; diff --git a/lib/msal-node/src/request/DeviceCodeRequest.ts b/lib/msal-node/src/request/DeviceCodeRequest.ts index 36207fa494..af9ab93116 100644 --- a/lib/msal-node/src/request/DeviceCodeRequest.ts +++ b/lib/msal-node/src/request/DeviceCodeRequest.ts @@ -13,7 +13,7 @@ import { CommonDeviceCodeRequest } from "./CommonDeviceCodeRequest.js"; * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - deviceCodeCallback - Callback containing device code response. Message should be shown to end user. End user can then navigate to the verification_uri, input the user_code, and input credentials. * - cancel - Boolean to cancel polling of device code endpoint. While the user authenticates on a separate device, MSAL polls the the token endpoint of security token service for the interval specified in the device code response (usually 15 minutes). To stop polling and cancel the request, set cancel=true. - * - extraQueryParameters - String to string map of custom query parameters added to the query string + * - extraQueryParameters - String to string map of custom query parameters added to the query string * @public */ export type DeviceCodeRequest = Partial< diff --git a/lib/msal-node/src/request/UsernamePasswordRequest.ts b/lib/msal-node/src/request/UsernamePasswordRequest.ts index c9466102e8..37d562ea6a 100644 --- a/lib/msal-node/src/request/UsernamePasswordRequest.ts +++ b/lib/msal-node/src/request/UsernamePasswordRequest.ts @@ -14,9 +14,9 @@ import { CommonUsernamePasswordRequest } from "./CommonUsernamePasswordRequest.j * - authority - Url of the authority which the application acquires tokens from. * - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. * - username - username of the client + * - password - credentials * - extraQueryParameters - String to string map of custom query parameters added to outgoing token service requests * - extraParameters - String to string map of custom query parameters added to outgoing token service requests - * - password - credentials * @public */ export type UsernamePasswordRequest = Partial< From 21bfd1ee9071a3b8dc7c6bd35f64080339390fbe Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 14 Nov 2025 15:40:42 -0800 Subject: [PATCH 7/8] Ran API Extractor and Prettier --- lib/msal-node/apiReview/msal-node.api.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/msal-node/apiReview/msal-node.api.md b/lib/msal-node/apiReview/msal-node.api.md index 549d91f20c..f1c25a4c2b 100644 --- a/lib/msal-node/apiReview/msal-node.api.md +++ b/lib/msal-node/apiReview/msal-node.api.md @@ -70,6 +70,7 @@ import { ServerError } from '@azure/msal-common/node'; import { ServerTelemetryEntity } from '@azure/msal-common/node'; import { ServerTelemetryManager } from '@azure/msal-common/node'; import { StaticAuthorityOptions } from '@azure/msal-common/node'; +import { StringDict } from '@azure/msal-common/node'; import { ThrottlingEntity } from '@azure/msal-common/node'; import { TokenCacheContext } from '@azure/msal-common/node'; import { TokenKeys } from '@azure/msal-common/node'; @@ -91,7 +92,7 @@ export { AuthErrorCodes } export { AuthorizationCodePayload } // @public -export type AuthorizationCodeRequest = Partial> & { +export type AuthorizationCodeRequest = Partial> & { scopes: Array; redirectUri: string; code: string; @@ -99,7 +100,7 @@ export type AuthorizationCodeRequest = Partial> & { +export type AuthorizationUrlRequest = Partial> & { scopes: Array; redirectUri: string; }; @@ -236,6 +237,7 @@ export class DeviceCodeClient extends BaseClient { constructor(configuration: ClientConfiguration); // Warning: (ae-forgotten-export) The symbol "CommonDeviceCodeRequest" needs to be exported by the entry point index.d.ts acquireToken(request: CommonDeviceCodeRequest): Promise; + createExtraQueryParameters(request: CommonDeviceCodeRequest): string; } // @public @@ -308,7 +310,7 @@ export { InteractionRequiredAuthError } export { InteractionRequiredAuthErrorCodes } // @public -export type InteractiveRequest = Partial> & { +export type InteractiveRequest = Partial> & { openBrowser: (url: string) => Promise; scopes?: Array; successTemplate?: string; @@ -490,7 +492,7 @@ export class PublicClientApplication extends ClientApplication implements IPubli } // @public -export type RefreshTokenRequest = Partial> & { +export type RefreshTokenRequest = Partial> & { scopes: Array; refreshToken: string; forceCache?: boolean; @@ -587,7 +589,7 @@ export type SignOutRequest = { }; // @public -export type SilentFlowRequest = Partial> & { +export type SilentFlowRequest = Partial> & { account: AccountInfo; scopes: Array; }; From 736e3e5a746fde3d3ae5271db92821d082e82bc6 Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:48:03 -0800 Subject: [PATCH 8/8] Update change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json b/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json index 48216c9820..03282a0233 100644 --- a/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json +++ b/change/@azure-msal-node-ad99a1d3-bdf2-499d-a4a2-7b4ef4153d6a.json @@ -1,6 +1,6 @@ { "type": "major", - "comment": "Remove extraQueryParameters and extraParameters fields from Request types (#8136)", + "comment": "Remove extraQueryParameters and extraParameters fields from Request types [#8136](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8136)", "packageName": "@azure/msal-node", "email": "avdunn@microsoft.com", "dependentChangeType": "patch"