diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
index febea0d99e..e4343ecb78 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs
@@ -67,6 +67,7 @@ public T WithClaims(string claims)
/// The string needs to be properly URL-encoded and ready to send as a string of segments of the form key=value separated by an ampersand character.
///
/// The builder to chain .With methods.
+ [Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary) method instead, which provides control over which parameters are included in the cache key.", false)]
public T WithExtraQueryParameters(string extraQueryParameters)
{
if (!string.IsNullOrWhiteSpace(extraQueryParameters))
diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs
index 7871a7eafc..c2f50c33cc 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs
@@ -89,10 +89,49 @@ public T WithCorrelationId(Guid correlationId)
/// as a string of segments of the form key=value separated by an ampersand character.
/// The parameter can be null.
/// The builder to chain the .With methods.
+ [Obsolete("This method is deprecated. Use the WithExtraQueryParameters(IDictionary) method instead, which provides control over which parameters are included in the cache key.", false)]
public T WithExtraQueryParameters(Dictionary extraQueryParameters)
{
- CommonParameters.ExtraQueryParameters = extraQueryParameters ??
- new Dictionary(StringComparer.OrdinalIgnoreCase);
+ return WithExtraQueryParameters(CoreHelpers.ConvertToTupleParameters(extraQueryParameters));
+ }
+
+ ///
+ /// Sets Extra Query Parameters for the query string in the HTTP authentication request with control over which parameters are included in the cache key
+ ///
+ /// This parameter will be appended as is to the query string in the HTTP authentication request to the authority, and merged with those added to the application-level WithExtraQueryParameters API.
+ /// Each dictionary entry maps a parameter name to a tuple containing:
+ /// - Value: The parameter value that will be appended to the query string
+ /// - IncludeInCacheKey: Whether this parameter should be included when computing the token's cache key.
+ /// To help ensure the correct token is returned from the cache, IncludeInCacheKey should be true if the parameter affects token content or validity (e.g., resource-specific claims or parameters).
+ /// The parameter can be null.
+ /// The builder to chain .With methods.
+ public T WithExtraQueryParameters(IDictionary extraQueryParameters)
+ {
+ if (extraQueryParameters == null)
+ {
+ CommonParameters.ExtraQueryParameters = null;
+ return this as T;
+ }
+
+ CommonParameters.ExtraQueryParameters = CommonParameters.ExtraQueryParameters ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ // Add each parameter to ExtraQueryParameters and, if requested, to CacheKeyComponents
+ foreach (var kvp in extraQueryParameters)
+ {
+ CommonParameters.ExtraQueryParameters[kvp.Key] = kvp.Value.Value;
+
+ if (kvp.Value.IncludeInCacheKey)
+ {
+ CommonParameters.CacheKeyComponents = CommonParameters.CacheKeyComponents ?? new SortedList>>();
+
+ // Capture the value in a local to avoid closure issues
+ string valueToCache = kvp.Value.Value;
+
+ // Add to cache key components - uses a func that returns the value as a task
+ CommonParameters.CacheKeyComponents[kvp.Key] = (CancellationToken _) => Task.FromResult(valueToCache);
+ }
+ }
+
return this as T;
}
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs b/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
index 4d7d57e2c8..bd8a79f47b 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
@@ -292,10 +292,10 @@ protected T WithOptions(ApplicationOptions applicationOptions)
/// as a string of segments of the form key=value separated by an ampersand character.
/// The parameter can be null.
/// The builder to chain the .With methods
+ [Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary) method instead, which provides control over which parameters are included in the cache key.", false)]
public T WithExtraQueryParameters(IDictionary extraQueryParameters)
{
- Config.ExtraQueryParameters = extraQueryParameters;
- return this as T;
+ return WithExtraQueryParameters(CoreHelpers.ConvertToTupleParameters(extraQueryParameters));
}
///
@@ -305,6 +305,7 @@ public T WithExtraQueryParameters(IDictionary extraQueryParamete
/// The string needs to be properly URL-encoded and ready to send as a string of segments of the form key=value separated by an ampersand character.
///
///
+ [Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary) method instead, which provides control over which parameters are included in the cache key.", false)]
public T WithExtraQueryParameters(string extraQueryParameters)
{
if (!string.IsNullOrWhiteSpace(extraQueryParameters))
@@ -314,6 +315,44 @@ public T WithExtraQueryParameters(string extraQueryParameters)
return this as T;
}
+ ///
+ /// Sets Extra Query Parameters for the query string in the HTTP authentication request with control over which parameters are included in the cache key
+ ///
+ /// This parameter will be appended as is to the query string in the HTTP authentication request to the authority, and merged with those added to the request-level WithExtraQueryParameters API.
+ /// Each dictionary entry maps a parameter name to a tuple containing:
+ /// - Value: The parameter value that will be appended to the query string
+ /// - IncludeInCacheKey: Whether this parameter should be included when computing the token's cache key.
+ /// To help ensure the correct token is returned from the cache, IncludeInCacheKey should be true if the parameter affects token content or validity (e.g., resource-specific claims or parameters).
+ /// The parameter can be null.
+ /// The builder to chain .With methods.
+ public T WithExtraQueryParameters(IDictionary extraQueryParameters)
+ {
+ if (extraQueryParameters == null)
+ {
+ Config.ExtraQueryParameters = null;
+ return this as T;
+ }
+
+ // Add each parameter to ExtraQueryParameters and, if requested, to CacheKeyComponents
+ foreach (var kvp in extraQueryParameters)
+ {
+ Config.ExtraQueryParameters = Config.ExtraQueryParameters ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ Config.ExtraQueryParameters[kvp.Key] = kvp.Value.Value;
+
+ if (kvp.Value.IncludeInCacheKey)
+ {
+ // Initialize the cache key components if needed
+ Config.CacheKeyComponents = Config.CacheKeyComponents ?? new SortedList();
+
+ // Add to cache key components - uses a func that returns the value as a task
+ Config.CacheKeyComponents[kvp.Key] = kvp.Value.Value;
+ }
+ }
+
+ return this as T;
+ }
+
///
/// Microsoft Identity specific OIDC extension that allows resource challenges to be resolved without interaction.
/// Allows configuration of one or more client capabilities, e.g. "llt"
diff --git a/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj b/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
index 3279f0338a..7bbdb0ba3e 100644
--- a/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
+++ b/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
@@ -154,6 +154,7 @@
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
index 4971f00461..503e324407 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
@@ -5,4 +7,4 @@ const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mt
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
-static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
+static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
\ No newline at end of file
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
index 4971f00461..0fbe3511dd 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
index 4971f00461..503e324407 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
@@ -5,4 +7,4 @@ const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mt
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
-static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
+static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
\ No newline at end of file
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
index 4971f00461..0fbe3511dd 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
index 4971f00461..0fbe3511dd 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
index 4971f00461..0fbe3511dd 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
+Microsoft.Identity.Client.AbstractApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
+Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> T
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
diff --git a/src/client/Microsoft.Identity.Client/Utils/CoreHelpers.cs b/src/client/Microsoft.Identity.Client/Utils/CoreHelpers.cs
index fd53a2ee0c..09a3b36a24 100644
--- a/src/client/Microsoft.Identity.Client/Utils/CoreHelpers.cs
+++ b/src/client/Microsoft.Identity.Client/Utils/CoreHelpers.cs
@@ -140,6 +140,24 @@ public static Dictionary ParseKeyValueList(string input, char de
return ParseKeyValueList(input, delimiter, urlDecode, true, requestContext);
}
+ // Helper method intended to help deprecate some WithExtraQueryParameters APIs.
+ // Convert from Dictionary to Dictionary,
+ // with all includeInCacheKey set to false by default to maintain existing behavior of those older APIs.
+ internal static IDictionary ConvertToTupleParameters(IDictionary parameters)
+ {
+ if (parameters == null)
+ {
+ return null;
+ }
+
+ var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var kvp in parameters)
+ {
+ result[kvp.Key] = (kvp.Value, false); // Exclude all parameters from cache key by default
+ }
+ return result;
+ }
+
internal static IReadOnlyList SplitWithQuotes(string input, char delimiter)
{
if (string.IsNullOrWhiteSpace(input))
diff --git a/tests/Microsoft.Identity.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Test.Common/TestConstants.cs
index bf31b71dc3..541d1b862d 100644
--- a/tests/Microsoft.Identity.Test.Common/TestConstants.cs
+++ b/tests/Microsoft.Identity.Test.Common/TestConstants.cs
@@ -246,6 +246,18 @@ public static IDictionary ExtraQueryParameters
};
}
}
+ public static IDictionary ExtraQueryParametersNoAffectOnCacheKeys
+ {
+ get
+ {
+ return new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ { "extra", ("qp", false) },
+ { "key1", ("value1%20with%20encoded%20space", false) },
+ { "key2", ("value2", false) }
+ };
+ }
+ }
public const string MsalCCAKeyVaultUri = "https://id4skeyvault.vault.azure.net/secrets/AzureADIdentityDivisionTestAgentSecret/";
diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/Agentic.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/Agentic.cs
index fde8d13181..2ec6b5a9cf 100644
--- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/Agentic.cs
+++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/Agentic.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
@@ -58,7 +59,7 @@ private static async Task AgentUserIdentityGetsTokenForGraphAsync()
.WithAuthority("https://login.microsoftonline.com/", TenantId)
.WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
.WithExperimentalFeatures(true)
- .WithExtraQueryParameters("slice=first")
+ .WithExtraQueryParameters(new Dictionary { { "slice", ("first", false) } })
.WithClientAssertion((AssertionRequestOptions _) => GetAppCredentialAsync(AgentIdentity))
.Build();
diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs
index cde824190f..4996fda4a7 100644
--- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs
+++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/FmiIntegrationTests.cs
@@ -11,6 +11,7 @@
using Microsoft.Identity.Test.Common.Core.Helpers;
using System;
using System.IdentityModel.Tokens.Jwt;
+using System.Collections.Generic;
namespace Microsoft.Identity.Test.Integration.NetCore.HeadlessTests
{
@@ -26,6 +27,7 @@ public class FmiIntegrationTests
{
private byte[] _serializedCache;
private const string Testslice = "dc=ESTSR-PUB-WUS-LZ1-TEST"; //Updated slice for regional tests
+ private Dictionary TestsliceQueryParam = new Dictionary { { "dc", ("ESTSR-PUB-WUS-LZ1-TEST", false) } };
private const string AzureRegion = "westus3";
private const string TenantId = "f645ad92-e38d-4d1a-b510-d1b09a74a8ca"; //Tenant Id for the test app
@@ -45,7 +47,7 @@ public async Task Flow1_Credential_From_Cert()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/", TenantId)
- .WithExtraQueryParameters(Testslice) //Enables MSAL to target ESTS Test slice
+ .WithExtraQueryParameters(TestsliceQueryParam) //Enables MSAL to target ESTS Test slice
.WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows
.WithAzureRegion(AzureRegion)
.BuildConcrete();
@@ -90,7 +92,7 @@ public async Task Flow2_Token_From_CertTest()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/", TenantId)
- .WithExtraQueryParameters(Testslice)
+ .WithExtraQueryParameters(TestsliceQueryParam)
.WithCertificate(cert, sendX5C: true)
.WithAzureRegion(AzureRegion)
.BuildConcrete();
@@ -130,7 +132,7 @@ public async Task Flow3_FmiCredential_From_AnotherFmiCredential()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/", TenantId)
- .WithExtraQueryParameters(Testslice)
+ .WithExtraQueryParameters(TestsliceQueryParam)
.WithClientAssertion((options) => GetFmiCredentialFromRma(options, Testslice))
.WithAzureRegion(AzureRegion)
.BuildConcrete();
@@ -171,7 +173,7 @@ public async Task Flow4_SubRma_FIC_From_FmiCredential()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/", TenantId)
- .WithExtraQueryParameters(Testslice)
+ .WithExtraQueryParameters(TestsliceQueryParam)
.WithClientAssertion((options) => GetFmiCredentialFromRma(options, Testslice))
.WithAzureRegion(AzureRegion)
.BuildConcrete();
@@ -211,7 +213,7 @@ public async Task Flow5_FmiToken_From_FmiCred()
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/", TenantId)
- .WithExtraQueryParameters(Testslice)
+ .WithExtraQueryParameters(TestsliceQueryParam)
.WithClientAssertion((options) => GetFmiCredentialFromRma(options, Testslice))
.WithAzureRegion(AzureRegion)
.BuildConcrete();
@@ -246,6 +248,7 @@ private static async Task GetFmiCredentialFromRma(AssertionRequestOption
X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName);
+#pragma warning disable CS0618 // Type or member is obsolete
//Create application
var confidentialApp = ConfidentialClientApplicationBuilder
.Create(clientId)
@@ -254,6 +257,7 @@ private static async Task GetFmiCredentialFromRma(AssertionRequestOption
.WithCertificate(cert, sendX5C: true) //sendX5c enables SN+I auth which is required for FMI flows
.WithAzureRegion(AzureRegion)
.BuildConcrete();
+#pragma warning restore CS0618 // Type or member is obsolete
//Acquire Token
var authResult = await confidentialApp.AcquireTokenForClient(new[] { scope })
diff --git a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AcquireTokenInteractiveBuilderTests.cs b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AcquireTokenInteractiveBuilderTests.cs
index 633ac1afbe..c2d677c1e8 100644
--- a/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AcquireTokenInteractiveBuilderTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ApiConfigTests/AcquireTokenInteractiveBuilderTests.cs
@@ -91,7 +91,7 @@ public async Task TestAcquireTokenInteractiveBuilderWithPromptAndExtraQueryParam
{
await AcquireTokenInteractiveParameterBuilder.Create(_harness.Executor, TestConstants.s_scope)
.WithLoginHint(TestConstants.DisplayableId)
- .WithExtraQueryParameters("domain_hint=mydomain.com")
+ .WithExtraQueryParameters(new Dictionary { { "domain_hint", ("mydomain.com", false) } })
.ExecuteAsync()
.ConfigureAwait(false);
diff --git a/tests/Microsoft.Identity.Test.Unit/OAuthClientTests.cs b/tests/Microsoft.Identity.Test.Unit/OAuthClientTests.cs
index f9f48b95be..e298a3a046 100644
--- a/tests/Microsoft.Identity.Test.Unit/OAuthClientTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/OAuthClientTests.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -197,7 +198,7 @@ out Arg.Any())
AuthenticationResult result = await pca
.AcquireTokenInteractive(TestConstants.s_scope)
- .WithExtraQueryParameters("qp1=v1")
+ .WithExtraQueryParameters(new Dictionary{{ "qp1", ("v1", false) }})
.ExecuteAsync()
.ConfigureAwait(false);
// Assert that the endpoint sent to the device auth manager doesnt not have query params
diff --git a/tests/Microsoft.Identity.Test.Unit/ParallelRequestsTests.cs b/tests/Microsoft.Identity.Test.Unit/ParallelRequestsTests.cs
index 2c5b1b44ac..89f7b7fd16 100644
--- a/tests/Microsoft.Identity.Test.Unit/ParallelRequestsTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ParallelRequestsTests.cs
@@ -37,10 +37,10 @@ public override void TestInitialize()
[TestMethod]
public async Task ExtraQP()
{
- Dictionary extraQp = new()
+ Dictionary extraQp = new()
{
- { "key1", "1" },
- { "key2", "2" }
+ { "key1", ("1", false) },
+ { "key2", ("2", false) }
};
// Arrange
diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/CacheKeyExtensionTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/CacheKeyExtensionTests.cs
index 3591be8c3e..cb7659f732 100644
--- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/CacheKeyExtensionTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/CacheKeyExtensionTests.cs
@@ -370,6 +370,142 @@ public async Task CacheExtEnsureInputKeysAddedCorrectlyTestAsync()
}
}
+ [TestMethod]
+ public async Task CacheExt_WithExtraQueryParameters_NoConflictTestAsync()
+ {
+ using (var httpManager = new MockHttpManager())
+ {
+ var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
+ .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
+ .WithRedirectUri(TestConstants.RedirectUri)
+ .WithClientSecret(TestConstants.ClientSecret)
+ .WithHttpManager(httpManager)
+ .BuildConcrete();
+
+ var appCacheAccess = app.AppTokenCache.RecordAccess();
+
+ httpManager.AddInstanceDiscoveryMockHandler();
+
+ // Test 1: Use WithAdditionalCacheKeyComponents only
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_additional_components");
+ var result1 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync1)
+ .ExecuteAsync(CancellationToken.None)
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_additional_components", result1.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result1.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 2: Use tuple-based WithExtraQueryParameters only
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_tuple_params");
+ var result2 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param1", ("value1", true) },
+ { "param2", ("value2", true) }
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_tuple_params", result2.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result2.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(2, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 3: Use both APIs together - should create a different cache entry
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_both_apis");
+ var result3 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync1)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param1", ("value1", true) },
+ { "param2", ("value2", true) }
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_both_apis", result3.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result3.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(3, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 4: Retrieve from cache using the same combination
+ var result4 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync1)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param1", ("value1", true) },
+ { "param2", ("value2", true) }
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_both_apis", result4.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result4.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(3, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 5: Test with non-cached parameters
+ var result5 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync1)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param1", ("value1", true) },
+ { "param2", ("value2", true) },
+ { "non_cached_param", ("some_value", false) } // This should not affect cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_both_apis", result5.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result5.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(3, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 6: Change a parameter that is included in cache key - should get a new token
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_changed_param");
+ var result6 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync1)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param1", ("different_value", true) }, // Changed value with includeInCacheKey=true
+ { "param2", ("value2", true) }
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_changed_param", result6.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result6.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(4, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 7: Now try with includeInCacheKey=false for a parameter
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_non_cached_param");
+ var result7 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync2) // Different additional components
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param3", ("value3", false) } // Not included in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_non_cached_param", result7.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result7.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(5, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+
+ // Test 8: Repeat with same config but change the non-cached parameter value
+ var result8 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithAdditionalCacheKeyComponents(_additionalCacheKeysAsync2)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "param3", ("different_value3", false) } // Changed value but not in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_non_cached_param", result8.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result8.AuthenticationResultMetadata.TokenSource);
+ Assert.AreEqual(5, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+ }
+ }
+
private void BeforeCacheAccess(TokenCacheNotificationArgs args)
{
args.TokenCache.DeserializeMsalV3(_serializedCache);
diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs
index b2ab2b6bfb..d59090be02 100644
--- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs
@@ -404,7 +404,7 @@ public async Task ClientCreds_And_AAD_LogRequestUri_OnServerError_Async()
.WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExtraQueryParameters("parameter=x")
+ .WithExtraQueryParameters(TestConstants.ExtraQueryParametersNoAffectOnCacheKeys)
.BuildConcrete();
var appCacheAccess = cca.AppTokenCache.RecordAccess();
var userCacheAccess = cca.UserTokenCache.RecordAccess();
@@ -432,7 +432,7 @@ public async Task ClientCreds_And_ADFS_LogRequestUri_OnServerError_Async()
.WithAuthority(new Uri(TestConstants.OnPremiseAuthority), false)
.WithClientSecret(TestConstants.ClientSecret)
.WithHttpManager(httpManager)
- .WithExtraQueryParameters("parameter=x")
+ .WithExtraQueryParameters(TestConstants.ExtraQueryParametersNoAffectOnCacheKeys)
.BuildConcrete();
var appCacheAccess = cca.AppTokenCache.RecordAccess();
var userCacheAccess = cca.UserTokenCache.RecordAccess();
@@ -1239,7 +1239,7 @@ public async Task GetAuthorizationRequestUrlDuplicateParamsTestAsync()
var uri = await app
.GetAuthorizationRequestUrl(TestConstants.s_scope)
.WithLoginHint(TestConstants.DisplayableId)
- .WithExtraQueryParameters("login_hint=some@value.com")
+ .WithExtraQueryParameters(new Dictionary { { "login_hint", ("some@value.com", false) } })
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ExtraQueryParametersTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ExtraQueryParametersTests.cs
new file mode 100644
index 0000000000..f02ac29a29
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ExtraQueryParametersTests.cs
@@ -0,0 +1,286 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Test.Common.Core.Mocks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.Identity.Test.Unit.PublicApiTests
+{
+ [TestClass]
+ public class ExtraQueryParametersTests
+ {
+
+ ///
+ /// Tests that the older WithExtraQueryParameters methods do not affect token caching behavior. This is meant to demonstrate an
+ /// issue in those methods: the parameters could change the contents of a token however they wer not included in the cache keys,
+ /// leading to potentially invalid tokens being returned from cache when a new request contained different extra query parameters.
+ ///
+ /// This test will no longer be needed when the older APIs are removed.
+ ///
+ [TestMethod]
+ public async Task WithExtraQueryParameters_DeprecatedDoNotAffectTokenCaching_TestAsync()
+ {
+ using (var httpManager = new MockHttpManager())
+ {
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ // Create a confidential client application with a default extra query parameter
+ var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
+ .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
+ .WithClientSecret(TestConstants.ClientSecret)
+ .WithExtraQueryParameters("app_param=app_value")
+ .WithHttpManager(httpManager)
+ .BuildConcrete();
+
+ // Step 1: Make a token request with a specific extra query parameter
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_request_param");
+ var result1 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters("request_param=request_value")
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_request_param", result1.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result1.AuthenticationResultMetadata.TokenSource);
+
+ // Step 2: Make another token request with the same extra query parameter
+ // Should retrieve token from cache without network call
+ var result2 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters("request_param=request_value")
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_request_param", result2.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result2.AuthenticationResultMetadata.TokenSource);
+
+ // Step 3: Make a token request with different extra query parameters
+ // Should find the cached token, as older WithExtraQueryParameters APIs do not affect caching
+ var result3 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters("different_param=different_value")
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_request_param", result3.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result3.AuthenticationResultMetadata.TokenSource);
+
+ // Step 4: Make a token request with the default app-level extra query parameters
+ // Using authorization code flow to populate user token cache for AcquireTokenSilent test
+ httpManager.AddMockHandler(
+ new MockHttpMessageHandler
+ {
+ ExpectedMethod = HttpMethod.Post,
+ ResponseMessage = MockHelpers.CreateSuccessResponseMessage(MockHelpers.GetDefaultTokenResponse("token_for_silent_flow"))
+ });
+
+ var result4 = await app.AcquireTokenByAuthorizationCode(
+ TestConstants.s_scope,
+ "some-auth-code")
+ .WithExtraQueryParameters("param_for_silent_flow=silent_value")
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_for_silent_flow", result4.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result4.AuthenticationResultMetadata.TokenSource);
+
+ // Get the account that was cached
+ var accounts = await app.GetAccountsAsync().ConfigureAwait(false);
+ var account = accounts.FirstOrDefault();
+ Assert.IsNotNull(account, "An account should be present in the cache");
+
+ // Step 5: Test AcquireTokenSilent with the cached account
+ // Should retrieve token from cache without network call
+ var silentResult = await app.AcquireTokenSilent(TestConstants.s_scope, account)
+ .WithExtraQueryParameters("param_for_silent_flow=silent_value")
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ Assert.AreEqual("token_for_silent_flow", silentResult.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, silentResult.AuthenticationResultMetadata.TokenSource);
+
+ // Verify expected cache state
+ Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count);
+ }
+ }
+
+ ///
+ /// Tests the new tuple-based WithExtraQueryParameters method that allows control over which parameters are used in cache keys.
+ ///
+ [TestMethod]
+ public async Task WithExtraQueryParameters_TupleVersion_ControlsCaching_TestAsync()
+ {
+ using (var httpManager = new MockHttpManager())
+ {
+ // Create a confidential client application with a default extra query parameter
+ // Using the new tuple-based API, specifying that app_param should be included in the cache key
+ var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
+ .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
+ .WithClientSecret(TestConstants.ClientSecret)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "app_param", ("app_value", true) }
+ })
+ .WithHttpManager(httpManager)
+ .BuildConcrete();
+
+ // Step 1: Make a token request with a specific extra query parameter included in the cache key
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_cache_param");
+ var result1 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "req_param", ("req_value", true) } // Include in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_cache_param", result1.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result1.AuthenticationResultMetadata.TokenSource);
+
+ // Step 2: Same parameter with includeInCacheKey=true, should use cache
+ var result2 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "req_param", ("req_value", true) } // Same as before, should hit cache
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_cache_param", result2.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result2.AuthenticationResultMetadata.TokenSource);
+
+ // Step 3: Using the same parameter but NOT including it in the cache key
+ // This will create a different cache key (without this parameter) and won't match previous entries
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_without_cache_param");
+ var result3 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "req_param", ("req_value", false) } // NOT included in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ // Should get a new token since cache key is different (doesn't include req_param)
+ Assert.AreEqual("token_without_cache_param", result3.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result3.AuthenticationResultMetadata.TokenSource);
+
+ // Step 4: Reusing the same configuration as Step 3 (parameter not in cache key)
+ // Should now use the cache since we've stored a token with this cache key
+ var result4 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "req_param", ("req_value", false) } // NOT included in cache key, same as Step 3
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_without_cache_param", result4.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result4.AuthenticationResultMetadata.TokenSource);
+
+ // Step 5: Using a different value but still not including in cache key
+ // Should still hit the same cache entry because the cache key is the same
+ var result5 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "req_param", ("different_value", false) } // Different value but not in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_without_cache_param", result5.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result5.AuthenticationResultMetadata.TokenSource);
+
+ // Step 6: Multiple parameters with different cache inclusion settings
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_mixed_params");
+ var result6 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "cache_param", ("cache_value", true) }, // Include in cache key
+ { "non_cache_param", ("value1", false) } // Don't include in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_mixed_params", result6.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result6.AuthenticationResultMetadata.TokenSource);
+
+ // Step 7: Same cache key parameter but different non-cache parameter, should use cache
+ var result7 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "cache_param", ("cache_value", true) }, // Same cached parameter
+ { "non_cache_param", ("value2", false) } // Different value, not in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_mixed_params", result7.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, result7.AuthenticationResultMetadata.TokenSource);
+
+ // Step 8: Different cache key parameter, should make a new request
+ httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(token: "token_with_different_cache_param");
+ var result8 = await app.AcquireTokenForClient(TestConstants.s_scope)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "cache_param", ("different_value", true) }, // Different value, included in cache key
+ { "non_cache_param", ("value1", false) } // Same as before, not in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_with_different_cache_param", result8.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result8.AuthenticationResultMetadata.TokenSource);
+
+ // Step 9: Use AcquireTokenByAuthorizationCode with tuple-based query parameters
+ httpManager.AddMockHandler(
+ new MockHttpMessageHandler
+ {
+ ExpectedMethod = HttpMethod.Post,
+ ResponseMessage = MockHelpers.CreateSuccessResponseMessage(MockHelpers.GetDefaultTokenResponse("token_from_auth_code"))
+ });
+
+ var result9 = await app.AcquireTokenByAuthorizationCode(TestConstants.s_scope, "some-auth-code")
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "auth_code_param", ("auth_code_value", true) }, // Include in cache key
+ { "transient_param", ("transient_value", false) } // Don't include in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.AreEqual("token_from_auth_code", result9.AccessToken);
+ Assert.AreEqual(TokenSource.IdentityProvider, result9.AuthenticationResultMetadata.TokenSource);
+
+ // Get the account that was cached
+ var accounts = await app.GetAccountsAsync().ConfigureAwait(false);
+ var account = accounts.FirstOrDefault();
+ Assert.IsNotNull(account, "An account should be present in the cache after auth code flow");
+
+ // Step 10: Test AcquireTokenSilent with the same cache key parameters but different non-cache parameters
+ var silentResult1 = await app.AcquireTokenSilent(TestConstants.s_scope, account)
+ .WithExtraQueryParameters(new Dictionary
+ {
+ { "auth_code_param", ("auth_code_value", true) }, // Same as auth code flow
+ { "transient_param", ("different_value", false) } // Different value, not in cache key
+ })
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ // Should get token from cache since cache key parameters match
+ Assert.AreEqual("token_from_auth_code", silentResult1.AccessToken);
+ Assert.AreEqual(TokenSource.Cache, silentResult1.AuthenticationResultMetadata.TokenSource);
+
+ // Verify final cache state
+ Assert.AreEqual(4, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count, "Should have 4 app tokens in cache");
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count, "Should have 1 user token in cache");
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count, "Should have 1 refresh token in cache");
+ }
+ }
+ }
+}
diff --git a/tests/Microsoft.Identity.Test.Unit/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs b/tests/Microsoft.Identity.Test.Unit/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
index e2b824ea72..a0d36cb412 100644
--- a/tests/Microsoft.Identity.Test.Unit/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
@@ -295,7 +295,7 @@ public async Task AcquireTokenByIntegratedWindowsAuth3rdPartyIDPTestAsync(string
PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId)
.WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
.WithHttpManager(httpManager)
- .WithExtraQueryParameters(TestConstants.ExtraQueryParameters)
+ .WithExtraQueryParameters(TestConstants.ExtraQueryParametersNoAffectOnCacheKeys)
.BuildConcrete();
AuthenticationResult result = await app
@@ -339,7 +339,7 @@ public async Task AcquireTokenByIntegratedWindowsAuthMetadataTestAsync()
PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId)
.WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
.WithHttpManager(httpManager)
- .WithExtraQueryParameters(TestConstants.ExtraQueryParameters)
+ .WithExtraQueryParameters(TestConstants.ExtraQueryParametersNoAffectOnCacheKeys)
.BuildConcrete();
AuthenticationResult result = await app
@@ -392,7 +392,7 @@ public async Task AcquireTokenByIntegratedWindowsAuthInvalidClientTestAsync()
PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId)
.WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
.WithHttpManager(httpManager)
- .WithExtraQueryParameters(TestConstants.ExtraQueryParameters)
+ .WithExtraQueryParameters(TestConstants.ExtraQueryParametersNoAffectOnCacheKeys)
.BuildConcrete();
MsalServiceException result = await AssertException.TaskThrowsAsync(
diff --git a/tests/Microsoft.Identity.Test.Unit/TelemetryTests/HttpTelemetryTests.cs b/tests/Microsoft.Identity.Test.Unit/TelemetryTests/HttpTelemetryTests.cs
index 299862a69b..2f8abee773 100644
--- a/tests/Microsoft.Identity.Test.Unit/TelemetryTests/HttpTelemetryTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/TelemetryTests/HttpTelemetryTests.cs
@@ -375,9 +375,9 @@ public async Task CallerSdkDetailsTestAsync()
var cca = CreateConfidentialClientApp();
await cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary {
- { "caller-sdk-id", "testApiId" },
- { "caller-sdk-ver", "testSdkVersion"} })
+ .WithExtraQueryParameters(new Dictionary {
+ { "caller-sdk-id", ("testApiId", false) },
+ { "caller-sdk-ver", ("testSdkVersion", false)} })
.ExecuteAsync().ConfigureAwait(false);
AssertCurrentTelemetry(
@@ -403,9 +403,9 @@ public async Task CallerSdkDetails_ConstraintsTestAsync()
var cca = CreateConfidentialClientApp();
await cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary {
- { "caller-sdk-id", callerSdkId },
- { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(new Dictionary {
+ { "caller-sdk-id", (callerSdkId, false) },
+ { "caller-sdk-ver", (callerSdkVersion, false) } })
.ExecuteAsync().ConfigureAwait(false);
AssertCurrentTelemetry(
diff --git a/tests/Microsoft.Identity.Test.Unit/TelemetryTests/OTelInstrumentationTests.cs b/tests/Microsoft.Identity.Test.Unit/TelemetryTests/OTelInstrumentationTests.cs
index 30d2d42b0e..d9b0a1958a 100644
--- a/tests/Microsoft.Identity.Test.Unit/TelemetryTests/OTelInstrumentationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/TelemetryTests/OTelInstrumentationTests.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -36,8 +37,13 @@ public class OTelInstrumentationTests : TestBase
private const string callerSdkId = "123";
private const string callerSdkVersion = "1.1.1.1";
+ private Dictionary extraQueryParams = new()
+ {
+ { "caller-sdk-id", (callerSdkId, false) },
+ { "caller-sdk-ver", (callerSdkVersion, false) }
+ };
- [TestCleanup]
+[TestCleanup]
public override void TestCleanup()
{
s_meterProvider?.Dispose();
@@ -101,7 +107,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_ClientCredential_Async()
_harness.HttpManager.AddAllMocks(TokenResponseType.Valid_ClientCredentials);
AuthenticationResult result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
// Assert
@@ -116,7 +122,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_ClientCredential_Async()
// Act
Trace.WriteLine("4. ATS - should perform an RT refresh");
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -132,7 +138,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_ClientCredential_Async()
Trace.WriteLine("5. ATS - should not perform an RT refresh, as the token is still valid");
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -174,7 +180,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_MSI_Async()
ManagedIdentitySource.AppService);
AuthenticationResult result = await mi.AcquireTokenForManagedIdentity(resource)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -191,7 +197,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_MSI_Async()
// Act
Trace.WriteLine("4. ATM - should perform an RT refresh");
result = await mi.AcquireTokenForManagedIdentity(resource)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -208,7 +214,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_MSI_Async()
Assert.AreEqual(refreshOn, result.AuthenticationResultMetadata.RefreshOn);
result = await mi.AcquireTokenForManagedIdentity(resource)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -240,7 +246,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_OBO_Async()
string oboCacheKey = "obo-cache-key";
var result = await cca.InitiateLongRunningProcessInWebApi(TestConstants.s_scope, TestConstants.DefaultAccessToken, ref oboCacheKey)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync().ConfigureAwait(false);
Assert.AreEqual(TestConstants.ATSecret, result.AccessToken);
@@ -253,7 +259,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_OBO_Async()
Trace.WriteLine("3. Configure AAD to respond with a valid token");
result = await cca.AcquireTokenInLongRunningProcess(TestConstants.s_scope, oboCacheKey)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync().ConfigureAwait(false);
Assert.AreEqual(TestConstants.ATSecret, result.AccessToken);
@@ -265,7 +271,7 @@ public async Task ProactiveTokenRefresh_ValidResponse_OBO_Async()
Trace.WriteLine("4. Fetch token from cache");
result = await cca.AcquireTokenInLongRunningProcess(TestConstants.s_scope, oboCacheKey)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync().ConfigureAwait(false);
Assert.AreEqual(TestConstants.ATSecret, result.AccessToken);
@@ -299,7 +305,7 @@ public async Task ProactiveTokenRefresh_AadUnavailableResponse_Async()
// Act
AuthenticationResult result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
@@ -313,7 +319,7 @@ public async Task ProactiveTokenRefresh_AadUnavailableResponse_Async()
_harness.HttpManager.AddTokenResponse(TokenResponseType.Valid_ClientCredentials);
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync()
.ConfigureAwait(false);
Assert.IsNotNull(result);
@@ -342,13 +348,13 @@ private async Task AcquireTokenSuccessAsync(bool withExtension = false)
// Acquire token for client with scope
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.WithAuthenticationExtension(authExtension)
.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsNotNull(result);
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.WithAuthenticationExtension(authExtension)
.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsNotNull(result);
@@ -358,13 +364,13 @@ private async Task AcquireTokenSuccessAsync(bool withExtension = false)
_harness.HttpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage();
// Acquire token for client with scope
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsNotNull(result);
// Acquire token from the cache
result = await _cca.AcquireTokenForClient(TestConstants.s_scope)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsNotNull(result);
}
@@ -377,7 +383,7 @@ private async Task AcquireTokenMsalServiceExceptionAsync()
//Test for MsalServiceException
MsalServiceException ex = await AssertException.TaskThrowsAsync(
() => _cca.AcquireTokenForClient(TestConstants.s_scopeForAnotherResource)
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.WithTenantId(TestConstants.Utid)
.ExecuteAsync(CancellationToken.None)).ConfigureAwait(false);
@@ -390,7 +396,7 @@ private async Task AcquireTokenMsalClientExceptionAsync()
//Test for MsalClientException
MsalClientException exClient = await AssertException.TaskThrowsAsync(
() => _cca.AcquireTokenForClient(null) // null scope -> client exception
- .WithExtraQueryParameters(new Dictionary { { "caller-sdk-id", callerSdkId }, { "caller-sdk-ver", callerSdkVersion } })
+ .WithExtraQueryParameters(extraQueryParams)
.WithTenantId(TestConstants.Utid)
.ExecuteAsync(CancellationToken.None)).ConfigureAwait(false);
diff --git a/tests/devapps/DesktopTestApp/PublicClientHandler.cs b/tests/devapps/DesktopTestApp/PublicClientHandler.cs
index f09cb5af62..0ca4057790 100644
--- a/tests/devapps/DesktopTestApp/PublicClientHandler.cs
+++ b/tests/devapps/DesktopTestApp/PublicClientHandler.cs
@@ -13,7 +13,8 @@ namespace DesktopTestApp
class PublicClientHandler
{
private const string _clientName = "DesktopTestApp";
- private const string _ciamExtraQParams = "dc=ESTS-PUB-EUS-AZ1-FD000-TEST1";
+ private Dictionary _ciamExtraQParams = new Dictionary { { "dc", ("ESTS-PUB-EUS-AZ1-FD000-TEST1", false) } };
+
private const string _ciamRedirectUri = "http://localhost";
public PublicClientHandler(string clientId, LogCallback logCallback)
@@ -44,6 +45,7 @@ public async Task AcquireTokenInteractiveAsync(
CreateOrUpdatePublicClientApp(InteractiveAuthority, ApplicationId);
AuthenticationResult result;
+#pragma warning disable CS0618 // Type or member is obsolete
if (CurrentUser != null)
{
result = await PublicClientApplication
@@ -64,6 +66,7 @@ public async Task AcquireTokenInteractiveAsync(
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
}
+#pragma warning restore CS0618 // Type or member is obsolete
CurrentUser = result.Account;
return result;
@@ -77,6 +80,7 @@ public async Task AcquireTokenInteractiveWithAuthorityAsyn
CreateOrUpdatePublicClientApp(InteractiveAuthority, ApplicationId);
AuthenticationResult result;
+#pragma warning disable CS0618 // Type or member is obsolete
if (CurrentUser != null)
{
result = await PublicClientApplication
@@ -99,6 +103,7 @@ public async Task AcquireTokenInteractiveWithAuthorityAsyn
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
}
+#pragma warning restore CS0618 // Type or member is obsolete
CurrentUser = result.Account;
return result;
@@ -127,6 +132,8 @@ public async Task AcquireTokenInteractiveWithB2CAuthorityA
{
CreateOrUpdatePublicClientApp(b2cAuthority, ApplicationId);
AuthenticationResult result;
+
+#pragma warning disable CS0618 // Type or member is obsolete
result = await PublicClientApplication
.AcquireTokenInteractive(scopes)
.WithAccount(CurrentUser)
@@ -135,6 +142,7 @@ public async Task AcquireTokenInteractiveWithB2CAuthorityA
.WithB2CAuthority(b2cAuthority)
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(false);
+#pragma warning restore CS0618 // Type or member is obsolete
CurrentUser = result.Account;
return result;
diff --git a/tests/devapps/NetCoreTestApp/Program.cs b/tests/devapps/NetCoreTestApp/Program.cs
index f9d312fad3..47e03b9b7a 100644
--- a/tests/devapps/NetCoreTestApp/Program.cs
+++ b/tests/devapps/NetCoreTestApp/Program.cs
@@ -22,10 +22,10 @@ namespace NetCoreTestApp
{
public class Program
{
- internal /* for test */ static Dictionary CallerSDKDetails { get; } = new()
+ internal /* for test */ static Dictionary CallerSDKDetails { get; } = new()
{
- { "caller-sdk-id", "IdWeb_1" },
- { "caller-sdk-ver", "123" }
+ { "caller-sdk-id", ("IdWeb_1", false) },
+ { "caller-sdk-ver", ("123", false) }
};
// This app has http://localhost redirect uri registered
@@ -387,7 +387,10 @@ 0. Exit App
var resultX1 = await cca1.AcquireTokenForClient(GraphAppScope)
.WithMtlsProofOfPossession()
- .WithExtraQueryParameters("dc=ESTSR-PUB-WUS3-AZ1-TEST1&slice=TestSlice") //Feature in test slice
+ .WithExtraQueryParameters(new Dictionary() {
+ { "dc", ("ESTSR-PUB-WUS3-AZ1-TEST1", false)},
+ { "slice", ("TestSlice", false)}
+ }) //Feature in test slice
.ExecuteAsync()
.ConfigureAwait(false);
diff --git a/tests/devapps/NetFxConsoleTestApp/Program.cs b/tests/devapps/NetFxConsoleTestApp/Program.cs
index 71edf11089..990a708b7d 100644
--- a/tests/devapps/NetFxConsoleTestApp/Program.cs
+++ b/tests/devapps/NetFxConsoleTestApp/Program.cs
@@ -378,10 +378,10 @@ x. Exit app
CancellationTokenSource cts = new CancellationTokenSource();
result = await pca.AcquireTokenInteractive(s_scopes)
.WithUseEmbeddedWebView(false)
- .WithExtraQueryParameters(new Dictionary() {
- { "dc", "prod-wst-test1"},
- { "slice", "test"},
- { "sshcrt", "true" }
+ .WithExtraQueryParameters(new Dictionary() {
+ { "dc", ("prod-wst-test1", false)},
+ { "slice", ("test", false)},
+ { "sshcrt", ("true", false) }
})
.WithSSHCertificateAuthenticationScheme(jwk, "1")
.WithSystemWebViewOptions(new SystemWebViewOptions()
@@ -676,11 +676,11 @@ private static string GetPasswordFromConsole()
return pwd;
}
- private static Dictionary GetTestSliceParams()
+ private static Dictionary GetTestSliceParams()
{
- return new Dictionary()
+ return new Dictionary()
{
- { "dc", "prod-wst-test1" },
+ { "dc", ("prod-wst-test1", false) },
};
}
}