diff --git a/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationInformation.cs b/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationInformation.cs index 9e1868905..a14e2113e 100644 --- a/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationInformation.cs +++ b/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationInformation.cs @@ -16,18 +16,18 @@ namespace Microsoft.Identity.Web public static class AppServicesAuthenticationInformation { // Environment variables. - private const string AppServicesAuthEnabledEnvironmentVariable = "WEBSITE_AUTH_ENABLED"; // True - private const string AppServicesAuthOpenIdIssuerEnvironmentVariable = "WEBSITE_AUTH_OPENID_ISSUER"; // for instance https://sts.windows.net// - private const string AppServicesAuthClientIdEnvironmentVariable = "WEBSITE_AUTH_CLIENT_ID"; // A GUID - private const string AppServicesAuthClientSecretEnvironmentVariable = "WEBSITE_AUTH_CLIENT_SECRET"; // A string - private const string AppServicesAuthLogoutPathEnvironmentVariable = "WEBSITE_AUTH_LOGOUT_PATH"; // /.auth/logout - private const string AppServicesAuthIdentityProviderEnvironmentVariable = "WEBSITE_AUTH_DEFAULT_PROVIDER"; // AzureActiveDirectory - private const string AppServicesAuthAzureActiveDirectory = "AzureActiveDirectory"; - private const string AppServicesAuthIdTokenHeader = "X-MS-TOKEN-AAD-ID-TOKEN"; + internal const string AppServicesAuthEnabledEnvironmentVariable = "WEBSITE_AUTH_ENABLED"; // True + internal const string AppServicesAuthOpenIdIssuerEnvironmentVariable = "WEBSITE_AUTH_OPENID_ISSUER"; // for instance https://sts.windows.net// + internal const string AppServicesAuthClientIdEnvironmentVariable = "WEBSITE_AUTH_CLIENT_ID"; // A GUID + internal const string AppServicesAuthClientSecretEnvironmentVariable = "WEBSITE_AUTH_CLIENT_SECRET"; // A string + internal const string AppServicesAuthLogoutPathEnvironmentVariable = "WEBSITE_AUTH_LOGOUT_PATH"; // /.auth/logout + internal const string AppServicesAuthIdentityProviderEnvironmentVariable = "WEBSITE_AUTH_DEFAULT_PROVIDER"; // AzureActiveDirectory + internal const string AppServicesAuthAzureActiveDirectory = "AzureActiveDirectory"; + internal const string AppServicesAuthIdTokenHeader = "X-MS-TOKEN-AAD-ID-TOKEN"; private const string AppServicesAuthIdpTokenHeader = "X-MS-CLIENT-PRINCIPAL-IDP"; // Artificially added by Microsoft.Identity.Web to help debugging App Services. See the Debug controller of the test app - private const string AppServicesAuthDebugHeadersEnvironmentVariable = "APP_SERVICES_AUTH_LOCAL_DEBUG"; + internal const string AppServicesAuthDebugHeadersEnvironmentVariable = "APP_SERVICES_AUTH_LOCAL_DEBUG"; /// /// Is App Services authentication enabled?. @@ -36,8 +36,17 @@ public static bool IsAppServicesAadAuthenticationEnabled { get { - return (Environment.GetEnvironmentVariable(AppServicesAuthEnabledEnvironmentVariable) == Constants.True) - && Environment.GetEnvironmentVariable(AppServicesAuthIdentityProviderEnvironmentVariable) == AppServicesAuthAzureActiveDirectory; + return + + string.Equals( + Environment.GetEnvironmentVariable(AppServicesAuthEnabledEnvironmentVariable), + Constants.True, + StringComparison.OrdinalIgnoreCase) && + + string.Equals( + Environment.GetEnvironmentVariable(AppServicesAuthIdentityProviderEnvironmentVariable), + AppServicesAuthAzureActiveDirectory, + StringComparison.OrdinalIgnoreCase); } } @@ -93,7 +102,7 @@ internal static string? Issuer { string? headerPlusValue = Environment.GetEnvironmentVariable(AppServicesAuthDebugHeadersEnvironmentVariable) ?.Split(';') - ?.FirstOrDefault(h => h.StartsWith(header)); + ?.FirstOrDefault(h => h.StartsWith(header, StringComparison.OrdinalIgnoreCase)); return headerPlusValue?.Substring(header.Length + 1); } #endif diff --git a/tests/Microsoft.Identity.Web.Test/AppServicesAuthenticationInformationTests.cs b/tests/Microsoft.Identity.Web.Test/AppServicesAuthenticationInformationTests.cs new file mode 100644 index 000000000..718e51ca9 --- /dev/null +++ b/tests/Microsoft.Identity.Web.Test/AppServicesAuthenticationInformationTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.Identity.Web.Test +{ + public class AppServicesAuthenticationInformationTests + { + [Fact] + public void SimulateGetttingHeaderFromDebugEnvironmentVariable() + { + try + { + Environment.SetEnvironmentVariable( + AppServicesAuthenticationInformation.AppServicesAuthDebugHeadersEnvironmentVariable, + $"a;{AppServicesAuthenticationInformation.AppServicesAuthIdTokenHeader}:xyz"); + + var res = AppServicesAuthenticationInformation.GetIdToken( + new Dictionary() + { + { AppServicesAuthenticationInformation.AppServicesAuthIdTokenHeader, new StringValues(string.Empty) }, + }); + +#if DEBUG + Assert.Equal("xyz", res); +#else + Assert.Equal(string.Empty, res); +#endif + } + finally + { + Environment.SetEnvironmentVariable( + AppServicesAuthenticationInformation.AppServicesAuthDebugHeadersEnvironmentVariable, + string.Empty); + } + } + } +} diff --git a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs index 53025e861..a17e1a500 100644 --- a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs @@ -59,10 +59,21 @@ public class WebAppExtensionsTests public WebAppExtensionsTests() { + ResetAppServiceEnv(); _configSection = GetConfigSection(ConfigSectionName); _env = new HostingEnvironment { EnvironmentName = Environments.Development }; } + private void ResetAppServiceEnv() + { + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthEnabledEnvironmentVariable, string.Empty); + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthIdentityProviderEnvironmentVariable, string.Empty); + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthClientIdEnvironmentVariable, string.Empty); + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthClientSecretEnvironmentVariable, string.Empty); + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthLogoutPathEnvironmentVariable, string.Empty); + Environment.SetEnvironmentVariable(AppServicesAuthenticationInformation.AppServicesAuthIdentityProviderEnvironmentVariable, string.Empty); + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -411,6 +422,39 @@ public void AddMicrosoftIdentityWebApp_AddsInMemoryTokenCaches() Assert.Contains(services, s => s.ServiceType == typeof(IMsalTokenCacheProvider)); } + [Theory] + [InlineData("tRue", "azureactivedirectory")] + [InlineData("true", "azureactivedirectory")] + [InlineData("tRue", AppServicesAuthenticationInformation.AppServicesAuthAzureActiveDirectory)] + [InlineData("true", AppServicesAuthenticationInformation.AppServicesAuthAzureActiveDirectory)] + // Regression for https://github.com/AzureAD/microsoft-identity-web/issues/1163 + public void AppServices_EnvironmentTest(string appServicesEnvEnabledValue, string idpEnvValue) + { + try + { + // Arrange + Environment.SetEnvironmentVariable( + AppServicesAuthenticationInformation.AppServicesAuthEnabledEnvironmentVariable, appServicesEnvEnabledValue); + + Environment.SetEnvironmentVariable( + AppServicesAuthenticationInformation.AppServicesAuthIdentityProviderEnvironmentVariable, + idpEnvValue); + + var services = new ServiceCollection(); + + // Act + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(_configureMsOptions); + + // Assert + Assert.Contains(services, s => s.ServiceType == typeof(AppServicesAuthenticationHandler)); + } + finally + { + ResetAppServiceEnv(); + } + } + [Fact] public void AddMicrosoftGraphUsingFactoryFunction() { @@ -552,6 +596,8 @@ private void AddMicrosoftIdentityWebApp_TestCommon(IServiceCollection services, Assert.Contains(services, s => s.ServiceType == typeof(IConfigureOptions)); Assert.Contains(services, s => s.ServiceType == typeof(IConfigureOptions)); Assert.Contains(services, s => s.ServiceType == typeof(IPostConfigureOptions)); + Assert.DoesNotContain(services, s => s.ServiceType == typeof(AppServicesAuthenticationHandler)); + Assert.Equal(ServiceLifetime.Singleton, services.First(s => s.ServiceType == typeof(MicrosoftIdentityIssuerValidatorFactory)).Lifetime); // Assert properties set @@ -634,7 +680,7 @@ private async Task AddMicrosoftIdentityWebApp_TestB2cSpecificSetup(IServiceColle await remoteFailureFuncMock.ReceivedWithAnyArgs().Invoke(Arg.Any()).ConfigureAwait(false); // Assert issuer is updated to non-default user flow - Assert.Contains(TestConstants.B2CEditProfileUserFlow, redirectContext.ProtocolMessage.IssuerAddress); + Assert.Contains(TestConstants.B2CEditProfileUserFlow, redirectContext.ProtocolMessage.IssuerAddress, System.StringComparison.OrdinalIgnoreCase); Assert.NotNull(redirectContext.ProtocolMessage.Parameters[ClaimConstants.ClientInfo]); Assert.Equal(Constants.One, redirectContext.ProtocolMessage.Parameters[ClaimConstants.ClientInfo].ToString(CultureInfo.InvariantCulture)); }