From c7b445c1ae135a40afaffd92ba56cb3cf858a5fd Mon Sep 17 00:00:00 2001 From: Jennyf19 Date: Tue, 11 Apr 2023 11:57:37 -0700 Subject: [PATCH] ciam fix, initial commit --- Directory.Build.props | 2 +- .../Constants.cs | 1 + .../MergedOptions.cs | 11 +++ .../MicrosoftIdentityOptions.cs | 5 ++ .../TokenAcquisition.cs | 46 +++++++++-- ...tyWebApiAuthenticationBuilderExtensions.cs | 4 + ...tyWebAppAuthenticationBuilderExtensions.cs | 14 +++- .../TokenAcquisitionAuthorityTests.cs | 78 +++++++++++++++++++ 8 files changed, 152 insertions(+), 9 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 22d9c284d..529106c31 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -69,7 +69,7 @@ 4.34.0 4.50.0-preview 3.1.3 - 3.0.1 + 3.1.0 4.7.2 diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs b/src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs index dbcbfd362..0fd7c6d76 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs @@ -129,6 +129,7 @@ public static class Constants internal const string InvalidClient = "invalid_client"; internal const string InvalidKeyError = "AADSTS700027"; internal const string CiamAuthoritySuffix = ".ciamlogin.com"; + internal const string TestSlice = "dc"; // Blazor challenge URI internal const string BlazorChallengeUri = "MicrosoftIdentity/Account/Challenge?redirectUri="; diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/MergedOptions.cs b/src/Microsoft.Identity.Web.TokenAcquisition/MergedOptions.cs index 2160ea508..62a5ba03a 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/MergedOptions.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/MergedOptions.cs @@ -296,6 +296,11 @@ internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftId mergedOptions.ClientCredentialsUsingManagedIdentity ??= microsoftIdentityOptions.ClientCredentialsUsingManagedIdentity; mergedOptions._confidentialClientApplicationOptions = null; + + if ((mergedOptions.ExtraQueryParameters == null || !mergedOptions.ExtraQueryParameters.Any()) && microsoftIdentityOptions.ExtraQueryParameters != null) + { + mergedOptions.ExtraQueryParameters = microsoftIdentityOptions.ExtraQueryParameters; + } } internal static void UpdateMergedOptionsFromConfidentialClientApplicationOptions(ConfidentialClientApplicationOptions confidentialClientApplicationOptions, MergedOptions mergedOptions) @@ -518,6 +523,12 @@ public static void UpdateMergedOptionsFromMicrosoftIdentityApplicationOptions(Mi { mergedOptions.TokenDecryptionCredentials = microsoftIdentityApplicationOptions.TokenDecryptionCredentials; } + + if ((mergedOptions.ExtraQueryParameters == null || !mergedOptions.ExtraQueryParameters.Any()) && microsoftIdentityApplicationOptions.ExtraQueryParameters != null) + { + mergedOptions.ExtraQueryParameters = microsoftIdentityApplicationOptions.ExtraQueryParameters; + } + } private static IEnumerable ComputeFromLegacyClientCredentials(MicrosoftIdentityOptions microsoftIdentityOptions) diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityOptions.cs b/src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityOptions.cs index 07fdfc115..a0e68d2cc 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityOptions.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityOptions.cs @@ -208,5 +208,10 @@ internal bool HasClientCredentials /// which is the value used by Microsoft.Identity.Web.UI. /// public PathString? ErrorPath { get; set; } = Constants.ErrorPath; + + /// + /// Sets query parameters for the query string in the HTTP request to the IdP. + /// + public IDictionary? ExtraQueryParameters { get; set; } } } diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs index 9e576bd27..c812b6482 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs @@ -133,6 +133,11 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsyn .WithCcsRoutingHint(backUpAuthRoutingHint) .WithSpaAuthorizationCode(mergedOptions.WithSpaAuthCode); + if (mergedOptions.ExtraQueryParameters != null) + { + builder.WithExtraQueryParameters((Dictionary)mergedOptions.ExtraQueryParameters); + } + if (!string.IsNullOrEmpty(authCodeRedemptionParameters.Tenant)) { builder.WithTenantId(authCodeRedemptionParameters.Tenant); @@ -352,9 +357,11 @@ public Task GetAuthenticationResultForAppAsync( if (tokenAcquisitionOptions != null) { - if (tokenAcquisitionOptions.ExtraQueryParameters != null) + var dict = MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + if (dict != null) { - builder.WithExtraQueryParameters(new Dictionary(tokenAcquisitionOptions.ExtraQueryParameters)); + builder.WithExtraQueryParameters(dict); } if (tokenAcquisitionOptions.ExtraHeadersParameters != null) { @@ -597,7 +604,7 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged { builder.WithClientCredentials( mergedOptions.ClientCredentials!, - _logger, + _logger, _credentialsLoader, new CredentialSourceLoaderParameters(mergedOptions.ClientId!, authority)); } @@ -707,9 +714,11 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged } if (tokenAcquisitionOptions != null) { - if (tokenAcquisitionOptions.ExtraQueryParameters != null) + var dict = MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + if (dict != null) { - builder.WithExtraQueryParameters(new Dictionary(tokenAcquisitionOptions.ExtraQueryParameters)); + builder.WithExtraQueryParameters(dict); } if (tokenAcquisitionOptions.ExtraHeadersParameters != null) { @@ -842,9 +851,11 @@ private Task GetAuthenticationResultForWebAppWithAccountFr if (tokenAcquisitionOptions != null) { - if (tokenAcquisitionOptions.ExtraQueryParameters != null) + var dict = MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + if (dict != null) { - builder.WithExtraQueryParameters(new Dictionary(tokenAcquisitionOptions.ExtraQueryParameters)); + builder.WithExtraQueryParameters(dict); } if (tokenAcquisitionOptions.ExtraHeadersParameters != null) { @@ -884,6 +895,27 @@ private Task GetAuthenticationResultForWebAppWithAccountFr return builder.ExecuteAsync(tokenAcquisitionOptions != null ? tokenAcquisitionOptions.CancellationToken : CancellationToken.None); } + internal static Dictionary? MergeExtraQueryParameters( + MergedOptions mergedOptions, + TokenAcquisitionOptions tokenAcquisitionOptions) + { + if (tokenAcquisitionOptions.ExtraQueryParameters != null) + { + var mergedDict = new Dictionary(tokenAcquisitionOptions.ExtraQueryParameters); + if (mergedOptions.ExtraQueryParameters != null) + { + foreach (var pair in mergedOptions!.ExtraQueryParameters) + { + if (!mergedDict!.ContainsKey(pair.Key)) + mergedDict.Add(pair.Key, pair.Value); + } + } + return mergedDict; + } + + return (Dictionary?)mergedOptions.ExtraQueryParameters; + } + protected static bool AcceptedTokenVersionMismatch(MsalUiRequiredException msalServiceException) { // Normally app developers should not make decisions based on the internal AAD code diff --git a/src/Microsoft.Identity.Web/WebApiExtensions/MicrosoftIdentityWebApiAuthenticationBuilderExtensions.cs b/src/Microsoft.Identity.Web/WebApiExtensions/MicrosoftIdentityWebApiAuthenticationBuilderExtensions.cs index 1501dca57..4c65e7f3e 100644 --- a/src/Microsoft.Identity.Web/WebApiExtensions/MicrosoftIdentityWebApiAuthenticationBuilderExtensions.cs +++ b/src/Microsoft.Identity.Web/WebApiExtensions/MicrosoftIdentityWebApiAuthenticationBuilderExtensions.cs @@ -168,6 +168,10 @@ private static void AddMicrosoftIdentityWebApiImplementation( if (mergedOptions.Authority != null) { mergedOptions.Authority = AuthorityHelpers.BuildCiamAuthorityIfNeeded(mergedOptions.Authority); + if (mergedOptions.ExtraQueryParameters != null) + { + options.MetadataAddress = mergedOptions.Authority + "/.well-known/openid-configuration?" + string.Join("&", mergedOptions.ExtraQueryParameters.Select(p => $"{p.Key}={p.Value}")); + } } MergedOptionsValidation.Validate(mergedOptions); diff --git a/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilderExtensions.cs b/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilderExtensions.cs index 273b7f353..f3932d70a 100644 --- a/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilderExtensions.cs +++ b/src/Microsoft.Identity.Web/WebAppExtensions/MicrosoftIdentityWebAppAuthenticationBuilderExtensions.cs @@ -226,7 +226,7 @@ private static void AddMicrosoftIdentityWebAppInternal( { builder.Services.AddSingleton, MicrosoftIdentityOptionsMerger>(); } - + builder.Services.Configure(openIdConnectScheme, configureMicrosoftIdentityOptions); builder.Services.AddSingleton(); builder.Services.AddHttpClient(); @@ -293,6 +293,10 @@ private static void AddMicrosoftIdentityWebAppInternal( if (mergedOptions.Authority != null) { mergedOptions.Authority = AuthorityHelpers.BuildCiamAuthorityIfNeeded(mergedOptions.Authority); + if (mergedOptions.ExtraQueryParameters != null) + { + options.MetadataAddress = mergedOptions.Authority + "/.well-known/openid-configuration?" + string.Join("&", mergedOptions.ExtraQueryParameters.Select(p => $"{p.Key}={p.Value}")); + } } PopulateOpenIdOptionsFromMergedOptions(options, mergedOptions); @@ -375,6 +379,14 @@ private static void AddMicrosoftIdentityWebAppInternal( additionClaims); } + if (mergedOptions.ExtraQueryParameters != null) + { + foreach (var ExtraQP in mergedOptions.ExtraQueryParameters) + { + context.ProtocolMessage.SetParameter(ExtraQP.Key, ExtraQP.Value); + } + } + if (mergedOptions.IsB2C) { // When a new Challenge is returned using any B2C user flow different than susi, we must change diff --git a/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs b/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs index 16f5ae260..c41c95a29 100644 --- a/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs +++ b/tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; using System.Globalization; using System.Net.Http; using Microsoft.AspNetCore.Authentication; @@ -231,5 +232,82 @@ public void TestParseAuthorityIfNecessary() Assert.Equal(TestConstants.AadInstance, mergedOptions.Instance); Assert.Equal(TestConstants.TenantIdAsGuid, mergedOptions.TenantId); } + + [Fact] + public void MergeExtraQueryParametersTest() + { + // Arrange + var mergedOptions = new MergedOptions + { + ExtraQueryParameters = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + } + }; + var tokenAcquisitionOptions = new TokenAcquisitionOptions + { + ExtraQueryParameters = new Dictionary + { + { "key1", "newvalue1" }, + { "key3", "value3" } + } + }; + + // Act + var mergedDict = TokenAcquisition.MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + + // Assert + Assert.Equal(3, mergedDict!.Count); + Assert.Equal("newvalue1", mergedDict["key1"]); + Assert.Equal("value2", mergedDict["key2"]); + Assert.Equal("value3", mergedDict["key3"]); + } + + [Fact] + public void MergeExtraQueryParameters_TokenAcquisitionOptionsNull_Test() + { + // Arrange + var mergedOptions = new MergedOptions + { + ExtraQueryParameters = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + } + }; + var tokenAcquisitionOptions = new TokenAcquisitionOptions + { + ExtraQueryParameters = null, + }; + + // Act + var mergedDict = TokenAcquisition.MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + // Assert + Assert.Equal("value1", mergedDict!["key1"]); + Assert.Equal("value2", mergedDict["key2"]); + } + + [Fact] + public void MergeExtraQueryParameters_MergedOptionsNull_Test() + { + // Arrange + var mergedOptions = new MergedOptions + { + ExtraQueryParameters = null, + }; + var tokenAcquisitionOptions = new TokenAcquisitionOptions + { + ExtraQueryParameters = null, + }; + + // Act + var mergedDict = TokenAcquisition.MergeExtraQueryParameters(mergedOptions, tokenAcquisitionOptions); + + // Assert + Assert.Null(mergedDict); + } } }