Skip to content

Commit

Permalink
fix tfp double up in legacy b2c domains (#796)
Browse files Browse the repository at this point in the history
* check for tfp

* add more tests

* fix null ref in test
  • Loading branch information
jennyf19 authored Dec 1, 2020
1 parent 029e2c8 commit a4bde02
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 46 deletions.
14 changes: 12 additions & 2 deletions src/Microsoft.Identity.Web/TokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ public async Task RemoveAccountAsync(RedirectContext context)
/// <summary>
/// Creates an MSAL confidential client application, if needed.
/// </summary>
private async Task<IConfidentialClientApplication> GetOrBuildConfidentialClientApplicationAsync()
internal /* for testing */ async Task<IConfidentialClientApplication> GetOrBuildConfidentialClientApplicationAsync()
{
if (_application == null)
{
Expand All @@ -447,7 +447,7 @@ private async Task<IConfidentialClientApplication> BuildConfidentialClientApplic
_microsoftIdentityOptions.CallbackPath.Value ?? string.Empty);
}

_applicationOptions.Instance = _applicationOptions.Instance.TrimEnd('/') + "/";
PrepareAuthorityInstanceForMsal();

if (!string.IsNullOrEmpty(_microsoftIdentityOptions.ClientSecret))
{
Expand Down Expand Up @@ -505,6 +505,16 @@ private async Task<IConfidentialClientApplication> BuildConfidentialClientApplic
}
}

private void PrepareAuthorityInstanceForMsal()
{
if (_microsoftIdentityOptions.IsB2C && _applicationOptions.Instance.EndsWith("/tfp/"))
{
_applicationOptions.Instance = _applicationOptions.Instance.Replace("/tfp/", string.Empty).Trim();
}

_applicationOptions.Instance = _applicationOptions.Instance.TrimEnd('/') + "/";
}

private async Task<AuthenticationResult?> GetAuthenticationResultForWebApiToCallDownstreamApiAsync(
IConfidentialClientApplication application,
string authority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -24,6 +25,7 @@

<ItemGroup Condition="'$(TargetFramework)' == 'net472' ">
<Compile Remove="Mocks\LoggerMock.cs" />
<Compile Remove="Mocks\MockHttpContextAccessor.cs" />
<Compile Remove="TestHelpers\HttpContextUtilities.cs" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.0" />
Expand Down
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 Microsoft.AspNetCore.Http;
using NSubstitute;

namespace Microsoft.Identity.Web.Test.Common.Mocks
{
public static class MockHttpContextAccessor
{
public static IHttpContextAccessor CreateMockHttpContextAccessor()
{
var mockHttpContextAccessor = Substitute.For<IHttpContextAccessor>();
mockHttpContextAccessor.HttpContext = new DefaultHttpContext();
mockHttpContextAccessor.HttpContext.Request.Scheme = "https";
mockHttpContextAccessor.HttpContext.Request.Host = new HostString("IdentityDotNetSDKAutomation");
mockHttpContextAccessor.HttpContext.Request.PathBase = "/";

return mockHttpContextAccessor;
}
}
}
1 change: 1 addition & 0 deletions tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static class TestConstants
public const string B2CInstance = "https://fabrikamb2c.b2clogin.com";
public const string B2CInstance2 = "https://catb2c.b2clogin.com";
public const string B2CCustomDomainInstance = "https://catsAreAmazing.com";
public const string B2CLoginMicrosoft = "https://login.microsoftonline.com";
public const string ClientSecret = "catsarecool";

public const string B2CAuthority = B2CInstance + "/" + B2CTenant + "/" + B2CSignUpSignInUserFlow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web.Test.Common;
using Microsoft.Identity.Web.Test.Common.Mocks;
using Microsoft.Identity.Web.Test.Common.TestHelpers;
using Microsoft.Identity.Web.Test.LabInfrastructure;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
using NSubstitute;
using Xunit;
using Xunit.Abstractions;
using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;

namespace Microsoft.Identity.Web.Test.Integration
{
Expand Down Expand Up @@ -106,37 +104,18 @@ async Task result() =>

private void InitializeTokenAcquisitionObjects()
{
IOptions<MicrosoftIdentityOptions> microsoftIdentityOptions = _provider.GetService<IOptions<MicrosoftIdentityOptions>>();
IOptions<MsalMemoryTokenCacheOptions> tokenOptions = _provider.GetService<IOptions<MsalMemoryTokenCacheOptions>>();
IOptions<ConfidentialClientApplicationOptions> ccOptions = _provider.GetService<IOptions<ConfidentialClientApplicationOptions>>();
ILogger<TokenAcquisition> logger = _provider.GetService<ILogger<TokenAcquisition>>();
IHttpClientFactory httpClientFactory = _provider.GetService<IHttpClientFactory>();

IHttpContextAccessor httpContextAccessor = CreateMockHttpContextAccessor();

_msalTestTokenCacheProvider = new MsalTestTokenCacheProvider(
_provider.GetService<IMemoryCache>(),
tokenOptions);
_provider.GetService<IMemoryCache>(),
_provider.GetService<IOptions<MsalMemoryTokenCacheOptions>>());

_tokenAcquisition = new TokenAcquisition(
_msalTestTokenCacheProvider,
httpContextAccessor,
microsoftIdentityOptions,
ccOptions,
httpClientFactory,
logger,
_provider);
}

private static IHttpContextAccessor CreateMockHttpContextAccessor()
{
var mockHttpContextAccessor = Substitute.For<IHttpContextAccessor>();
mockHttpContextAccessor.HttpContext = new DefaultHttpContext();
mockHttpContextAccessor.HttpContext.Request.Scheme = "https";
mockHttpContextAccessor.HttpContext.Request.Host = new HostString("IdentityDotNetSDKAutomation");
mockHttpContextAccessor.HttpContext.Request.PathBase = "/";

return mockHttpContextAccessor;
_msalTestTokenCacheProvider,
MockHttpContextAccessor.CreateMockHttpContextAccessor(),
_provider.GetService<IOptions<MicrosoftIdentityOptions>>(),
_provider.GetService<IOptions<ConfidentialClientApplicationOptions>>(),
_provider.GetService<IHttpClientFactory>(),
_provider.GetService<ILogger<TokenAcquisition>>(),
_provider);
}

private void BuildTheRequiredServices()
Expand Down
75 changes: 62 additions & 13 deletions tests/Microsoft.Identity.Web.Test/TokenAcquisitionAuthorityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
// Licensed under the MIT License.

using System.Globalization;
using System.Net.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web.Test.Common;
using Microsoft.Identity.Web.Test.Common.Mocks;
using Microsoft.Identity.Web.Test.Common.TestHelpers;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
using Xunit;

namespace Microsoft.Identity.Web.Test
Expand All @@ -15,38 +21,36 @@ public class TokenAcquisitionAuthorityTests
private TokenAcquisition _tokenAcquisition;
private ServiceProvider _provider;
private ConfidentialClientApplicationOptions _applicationOptions;
private MicrosoftIdentityOptions _microsoftIdentityOptions;

private void InitializeTokenAcquisitionObjects()
{
_tokenAcquisition = new TokenAcquisition(
null,
null,
new MsalTestTokenCacheProvider(
_provider.GetService<IMemoryCache>(),
_provider.GetService<IOptions<MsalMemoryTokenCacheOptions>>()),
MockHttpContextAccessor.CreateMockHttpContextAccessor(),
_provider.GetService<IOptions<MicrosoftIdentityOptions>>(),
_provider.GetService<IOptions<ConfidentialClientApplicationOptions>>(),
null,
null,
_provider.GetService<IHttpClientFactory>(),
_provider.GetService<ILogger<TokenAcquisition>>(),
_provider);
}

private void BuildTheRequiredServices()
private void BuildTheRequiredServices(string instance = TestConstants.AadInstance)
{
var services = new ServiceCollection();

_applicationOptions = new ConfidentialClientApplicationOptions
{
Instance = TestConstants.AadInstance,
Instance = instance,
ClientId = TestConstants.ConfidentialClientId,
ClientSecret = "cats",
ClientSecret = TestConstants.ClientSecret,
};

services.AddTokenAcquisition();
services.AddTransient(
provider => Options.Create(new MicrosoftIdentityOptions
{
Authority = TestConstants.AuthorityCommonTenant,
ClientId = TestConstants.ConfidentialClientId,
CallbackPath = string.Empty,
}));
provider => Options.Create(_microsoftIdentityOptions));
services.AddTransient(
provider => Options.Create(_applicationOptions));
_provider = services.BuildServiceProvider();
Expand All @@ -59,6 +63,13 @@ private void BuildTheRequiredServices()
[InlineData("")]
public void VerifyCorrectAuthorityUsedInTokenAcquisitionTests(string tenant)
{
_microsoftIdentityOptions = new MicrosoftIdentityOptions
{
Authority = TestConstants.AuthorityCommonTenant,
ClientId = TestConstants.ConfidentialClientId,
CallbackPath = string.Empty,
};

BuildTheRequiredServices();
InitializeTokenAcquisitionObjects();
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
Expand All @@ -79,5 +90,43 @@ public void VerifyCorrectAuthorityUsedInTokenAcquisitionTests(string tenant)
Assert.Equal(app.Authority, _tokenAcquisition.CreateAuthorityBasedOnTenantIfProvided(app, tenant));
}
}

[Theory]
[InlineData(TestConstants.B2CInstance)]
[InlineData(TestConstants.B2CLoginMicrosoft)]
[InlineData(TestConstants.B2CInstance, true)]
[InlineData(TestConstants.B2CLoginMicrosoft, true)]
public async void VerifyCorrectAuthorityUsedInTokenAcquisition_B2CAuthorityTestsAsync(
string authorityInstance,
bool withTfp = false)
{
_microsoftIdentityOptions = new MicrosoftIdentityOptions
{
SignUpSignInPolicyId = TestConstants.B2CSignUpSignInUserFlow,
Domain = TestConstants.B2CTenant,
};

if (withTfp)
{
BuildTheRequiredServices(authorityInstance + "/tfp/");
}
else
{
BuildTheRequiredServices(authorityInstance);
}

InitializeTokenAcquisitionObjects();

IConfidentialClientApplication app = await _tokenAcquisition.GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false);

string expectedAuthority = string.Format(
CultureInfo.InvariantCulture,
"{0}/tfp/{1}/{2}/",
authorityInstance,
TestConstants.B2CTenant,
TestConstants.B2CSignUpSignInUserFlow);

Assert.Equal(expectedAuthority, app.Authority);
}
}
}

0 comments on commit a4bde02

Please sign in to comment.