Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect user-provided authority queries and add to options.ExtraQueryParams #2848

Merged
merged 8 commits into from
May 28, 2024
22 changes: 22 additions & 0 deletions src/Microsoft.Identity.Web/AuthorityHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Web;
using Microsoft.AspNetCore.Http;

namespace Microsoft.Identity.Web
Expand Down Expand Up @@ -51,5 +53,25 @@ internal static string EnsureAuthorityIsV2(string authority)
preserveAuthority = true;
return authority;
}

internal static void AddAuthorityQueryToOptions(MicrosoftIdentityOptions options)
{
if (!string.IsNullOrEmpty(options.Authority))
{
int queryIndex = options.Authority.IndexOf('?', StringComparison.Ordinal);
if (queryIndex > -1)
{
options.ExtraQueryParameters ??= new Dictionary<string, string>();
var queryParams = HttpUtility.ParseQueryString(options.Authority[queryIndex..].TrimStart('?'));
for (int i = 0; i < queryParams.Count; i++)
{
var key = queryParams.GetKey(i);
var value = queryParams.Get(i);
if (key != null && value != null)
options.ExtraQueryParameters[key] = value;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ private static void AddMicrosoftIdentityWebApiImplementation(
{
mergedOptions.Authority = AuthorityHelpers.BuildCiamAuthorityIfNeeded(mergedOptions.Authority, out bool preserveAuthority);
mergedOptions.PreserveAuthority = preserveAuthority;
AuthorityHelpers.AddAuthorityQueryToOptions(mergedOptions);
options.Authority = mergedOptions.Authority;
}

Expand Down
202 changes: 201 additions & 1 deletion tests/Microsoft.Identity.Web.Test/AuthorityHelpersTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Web.Test.Common;
using Xunit;

Expand Down Expand Up @@ -118,5 +118,205 @@ public void EnsureAuthorityIsV2(string initialAuthority, string expectedAuthorit
options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority);
Assert.Equal(expectedAuthority, options.Authority);
}

[Theory]
[MemberData(nameof(AddAuthorityQueryToOptionsTheoryData))]
public void AddAuthorityQueryToOptions(AuthorityHelpersTheoryData theoryData)
{
// arrange
MicrosoftIdentityOptions options = new MicrosoftIdentityOptions
{
Authority = theoryData.Authority,
};

// act
AuthorityHelpers.AddAuthorityQueryToOptions(options);

// assert
Assert.NotNull(options.ExtraQueryParameters);
Assert.Equal(theoryData.ExpectedExtraQueryParameters.Count, options.ExtraQueryParameters.Count);
foreach (var key in theoryData.ExpectedExtraQueryParameters.Keys)
{
Assert.True(options.ExtraQueryParameters.ContainsKey(key));
Assert.Equal(theoryData.ExpectedExtraQueryParameters[key], options.ExtraQueryParameters[key]);
}
}

public static TheoryData<AuthorityHelpersTheoryData> AddAuthorityQueryToOptionsTheoryData()
{
var singleQuery = "?key1=value1";
var multipleQueries = "?key1=value1&key2=value2";
var emptyQuery = "?";
var queryNoValue = "?key1";

var singleExpectedExtraQueryParams = new Dictionary<string, string>
{
{ "key1", "value1" }
};

var multipleExpectedExtraQueryParams = new Dictionary<string, string>
{
{ "key1", "value1" },
{ "key2", "value2" }
};

var emptyExpectedExtraQueryParams = new Dictionary<string, string>();

var theoryData = new TheoryData<AuthorityHelpersTheoryData>
{
new("AuthorityCommonTenant_SingleQuery")
{
Authority = TestConstants.AuthorityCommonTenant + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("AuthorityCommonTenant_MultipleQueries")
{
Authority = TestConstants.AuthorityCommonTenant + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("AuthorityCommonTenant_EmptyQuery")
{
Authority = TestConstants.AuthorityCommonTenant + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("AuthorityCommonTenant_QueryNoValue")
{
Authority = TestConstants.AuthorityCommonTenant + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("AuthorityOrganizationsUSTenant_SingleQuery")
{
Authority = TestConstants.AuthorityOrganizationsUSTenant + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("AuthorityOrganizationsUSTenant_MultipleQueries")
{
Authority = TestConstants.AuthorityOrganizationsUSTenant + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("AuthorityOrganizationsUSTenant_EmptyQuery")
{
Authority = TestConstants.AuthorityOrganizationsUSTenant + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("AuthorityOrganizationsUSTenant_QueryNoValue")
{
Authority = TestConstants.AuthorityOrganizationsUSTenant + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("AuthorityCommonTenantWithV2_SingleQuery")
{
Authority = TestConstants.AuthorityCommonTenantWithV2 + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("AuthorityCommonTenantWithV2_MultipleQueries")
{
Authority = TestConstants.AuthorityCommonTenantWithV2 + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("AuthorityCommonTenantWithV2_EmptyQuery")
{
Authority = TestConstants.AuthorityCommonTenantWithV2 + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("AuthorityCommonTenantWithV2_QueryNoValue")
{
Authority = TestConstants.AuthorityCommonTenantWithV2 + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CAuthorityWithV2_SingleQuery")
{
Authority = TestConstants.B2CAuthorityWithV2 + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("B2CAuthorityWithV2_MultipleQueries")
{
Authority = TestConstants.B2CAuthorityWithV2 + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("B2CAuthorityWithV2_EmptyQuery")
{
Authority = TestConstants.B2CAuthorityWithV2 + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CAuthorityWithV2_QueryNoValue")
{
Authority = TestConstants.B2CAuthorityWithV2 + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CCustomDomainAuthorityWithV2_SingleQuery")
{
Authority = TestConstants.B2CCustomDomainAuthorityWithV2 + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("B2CCustomDomainAuthorityWithV2_MultipleQueries")
{
Authority = TestConstants.B2CCustomDomainAuthorityWithV2 + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("B2CCustomDomainAuthorityWithV2_EmptyQuery")
{
Authority = TestConstants.B2CCustomDomainAuthorityWithV2 + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CCustomDomainAuthorityWithV2_QueryNoValue")
{
Authority = TestConstants.B2CCustomDomainAuthorityWithV2 + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CAuthority_SingleQuery")
{
Authority = TestConstants.B2CAuthority + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("B2CAuthority_MultipleQueries")
{
Authority = TestConstants.B2CAuthority + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("B2CAuthority_EmptyQuery")
{
Authority = TestConstants.B2CAuthority + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CAuthority_QueryNoValue")
{
Authority = TestConstants.B2CAuthority + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CCustomDomainAuthority_SingleQuery")
{
Authority = TestConstants.B2CCustomDomainAuthority + singleQuery,
ExpectedExtraQueryParameters = singleExpectedExtraQueryParams
},
new("B2CCustomDomainAuthority_MultipleQueries")
{
Authority = TestConstants.B2CCustomDomainAuthority + multipleQueries,
ExpectedExtraQueryParameters = multipleExpectedExtraQueryParams
},
new("B2CCustomDomainAuthority_EmptyQuery")
{
Authority = TestConstants.B2CCustomDomainAuthority + emptyQuery,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
},
new("B2CCustomDomainAuthority_QueryNoValue")
{
Authority = TestConstants.B2CCustomDomainAuthority + queryNoValue,
ExpectedExtraQueryParameters = emptyExpectedExtraQueryParams
}
};

return theoryData;
}
}
public class AuthorityHelpersTheoryData : TheoryDataBase
{
public AuthorityHelpersTheoryData(string testId) : base(testId)
{
}

public string Authority { get; set; } = string.Empty;

public IDictionary<string,string> ExpectedExtraQueryParameters { get; set; } = new Dictionary<string, string>();
}
}
22 changes: 22 additions & 0 deletions tests/Microsoft.Identity.Web.Test/TheoryDataBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Xunit;

namespace Microsoft.Identity.Web.Test
{
public class TheoryDataBase : TheoryData
{
public TheoryDataBase(string testId)
{
TestId = testId;
}

public string TestId { get; set; } = string.Empty;

public override string ToString()
{
return TestId;
}
}
}
Loading