Skip to content

Commit 3e2555d

Browse files
Avery-DunnbgavrilMSCopilot
authored
Adjust WithExtraQueryParameters APIs and cache key behavior (#5536)
* Add new WithExtraQueryParameters API and behavior affects cache keys, deprecate existing WithExtraQueryParameters APIs * Fix tests * Fix test * More test fixes * Test fix * Update src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs Co-authored-by: Bogdan Gavril <[email protected]> * PR feedback * Update tests/Microsoft.Identity.Test.Unit/PublicApiTests/ExtraQueryParametersTests.cs Co-authored-by: Copilot <[email protected]> * PR feedback * PR feedback --------- Co-authored-by: Bogdan Gavril <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 7bf0a28 commit 3e2555d

File tree

26 files changed

+776
-59
lines changed

26 files changed

+776
-59
lines changed

src/client/Microsoft.Identity.Client/ApiConfig/AbstractAcquireTokenParameterBuilder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public T WithClaims(string claims)
6767
/// The string needs to be properly URL-encoded and ready to send as a string of segments of the form <c>key=value</c> separated by an ampersand character.
6868
/// </param>
6969
/// <returns>The builder to chain .With methods.</returns>
70+
[Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary<string, (string value, bool includeInCacheKey)>) method instead, which provides control over which parameters are included in the cache key.", false)]
7071
public T WithExtraQueryParameters(string extraQueryParameters)
7172
{
7273
if (!string.IsNullOrWhiteSpace(extraQueryParameters))

src/client/Microsoft.Identity.Client/ApiConfig/BaseAbstractAcquireTokenParameterBuilder.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,49 @@ public T WithCorrelationId(Guid correlationId)
8989
/// as a string of segments of the form <c>key=value</c> separated by an ampersand character.
9090
/// The parameter can be null.</param>
9191
/// <returns>The builder to chain the .With methods.</returns>
92+
[Obsolete("This method is deprecated. Use the WithExtraQueryParameters(IDictionary<string, (string value, bool includeInCacheKey)>) method instead, which provides control over which parameters are included in the cache key.", false)]
9293
public T WithExtraQueryParameters(Dictionary<string, string> extraQueryParameters)
9394
{
94-
CommonParameters.ExtraQueryParameters = extraQueryParameters ??
95-
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
95+
return WithExtraQueryParameters(CoreHelpers.ConvertToTupleParameters(extraQueryParameters));
96+
}
97+
98+
/// <summary>
99+
/// Sets Extra Query Parameters for the query string in the HTTP authentication request with control over which parameters are included in the cache key
100+
/// </summary>
101+
/// <param name="extraQueryParameters">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.
102+
/// Each dictionary entry maps a parameter name to a tuple containing:
103+
/// - Value: The parameter value that will be appended to the query string
104+
/// - IncludeInCacheKey: Whether this parameter should be included when computing the token's cache key.
105+
/// 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).
106+
/// The parameter can be null.</param>
107+
/// <returns>The builder to chain .With methods.</returns>
108+
public T WithExtraQueryParameters(IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters)
109+
{
110+
if (extraQueryParameters == null)
111+
{
112+
CommonParameters.ExtraQueryParameters = null;
113+
return this as T;
114+
}
115+
116+
CommonParameters.ExtraQueryParameters = CommonParameters.ExtraQueryParameters ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
117+
118+
// Add each parameter to ExtraQueryParameters and, if requested, to CacheKeyComponents
119+
foreach (var kvp in extraQueryParameters)
120+
{
121+
CommonParameters.ExtraQueryParameters[kvp.Key] = kvp.Value.Value;
122+
123+
if (kvp.Value.IncludeInCacheKey)
124+
{
125+
CommonParameters.CacheKeyComponents = CommonParameters.CacheKeyComponents ?? new SortedList<string, Func<CancellationToken, Task<string>>>();
126+
127+
// Capture the value in a local to avoid closure issues
128+
string valueToCache = kvp.Value.Value;
129+
130+
// Add to cache key components - uses a func that returns the value as a task
131+
CommonParameters.CacheKeyComponents[kvp.Key] = (CancellationToken _) => Task.FromResult(valueToCache);
132+
}
133+
}
134+
96135
return this as T;
97136
}
98137

src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,10 @@ protected T WithOptions(ApplicationOptions applicationOptions)
292292
/// as a string of segments of the form <c>key=value</c> separated by an ampersand character.
293293
/// The parameter can be null.</param>
294294
/// <returns>The builder to chain the .With methods</returns>
295+
[Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary<string, (string value, bool includeInCacheKey)>) method instead, which provides control over which parameters are included in the cache key.", false)]
295296
public T WithExtraQueryParameters(IDictionary<string, string> extraQueryParameters)
296297
{
297-
Config.ExtraQueryParameters = extraQueryParameters;
298-
return this as T;
298+
return WithExtraQueryParameters(CoreHelpers.ConvertToTupleParameters(extraQueryParameters));
299299
}
300300

301301
/// <summary>
@@ -305,6 +305,7 @@ public T WithExtraQueryParameters(IDictionary<string, string> extraQueryParamete
305305
/// The string needs to be properly URL-encoded and ready to send as a string of segments of the form <c>key=value</c> separated by an ampersand character.
306306
/// </param>
307307
/// <returns></returns>
308+
[Obsolete("This method is deprecated. Please use the WithExtraQueryParameters(IDictionary<string, (string value, bool includeInCacheKey)>) method instead, which provides control over which parameters are included in the cache key.", false)]
308309
public T WithExtraQueryParameters(string extraQueryParameters)
309310
{
310311
if (!string.IsNullOrWhiteSpace(extraQueryParameters))
@@ -314,6 +315,44 @@ public T WithExtraQueryParameters(string extraQueryParameters)
314315
return this as T;
315316
}
316317

318+
/// <summary>
319+
/// Sets Extra Query Parameters for the query string in the HTTP authentication request with control over which parameters are included in the cache key
320+
/// </summary>
321+
/// <param name="extraQueryParameters">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.
322+
/// Each dictionary entry maps a parameter name to a tuple containing:
323+
/// - Value: The parameter value that will be appended to the query string
324+
/// - IncludeInCacheKey: Whether this parameter should be included when computing the token's cache key.
325+
/// 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).
326+
/// The parameter can be null.</param>
327+
/// <returns>The builder to chain .With methods.</returns>
328+
public T WithExtraQueryParameters(IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters)
329+
{
330+
if (extraQueryParameters == null)
331+
{
332+
Config.ExtraQueryParameters = null;
333+
return this as T;
334+
}
335+
336+
// Add each parameter to ExtraQueryParameters and, if requested, to CacheKeyComponents
337+
foreach (var kvp in extraQueryParameters)
338+
{
339+
Config.ExtraQueryParameters = Config.ExtraQueryParameters ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
340+
341+
Config.ExtraQueryParameters[kvp.Key] = kvp.Value.Value;
342+
343+
if (kvp.Value.IncludeInCacheKey)
344+
{
345+
// Initialize the cache key components if needed
346+
Config.CacheKeyComponents = Config.CacheKeyComponents ?? new SortedList<string, string>();
347+
348+
// Add to cache key components - uses a func that returns the value as a task
349+
Config.CacheKeyComponents[kvp.Key] = kvp.Value.Value;
350+
}
351+
}
352+
353+
return this as T;
354+
}
355+
317356
/// <summary>
318357
/// Microsoft Identity specific OIDC extension that allows resource challenges to be resolved without interaction.
319358
/// Allows configuration of one or more client capabilities, e.g. "llt"

src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154

155155
<ItemGroup>
156156
<PackageReference Include="Microsoft.IdentityModel.Abstractions" />
157+
<PackageReference Include="System.ValueTuple" />
157158
</ItemGroup>
158159

159160
<ItemGroup Label="For public api analyzer support">

src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
@@ -7,4 +9,4 @@ Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Securi
79
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
810
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
911
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, string> extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
10-
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
12+
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void

src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string

src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string
@@ -7,4 +9,4 @@ Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Securi
79
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
810
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
911
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, string> extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
10-
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
12+
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void

src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string

src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string

src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Microsoft.Identity.Client.AbstractApplicationBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
2+
Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder<T>.WithExtraQueryParameters(System.Collections.Generic.IDictionary<string, (string Value, bool IncludeInCacheKey)> extraQueryParameters) -> T
13
const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPreview = "cannot_switch_between_imds_versions_for_preview" -> string
24
const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string
35
const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string

0 commit comments

Comments
 (0)