From 8cde6f5149ba4de6cd063e987e227cfd4faf6321 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 15 Jan 2019 16:21:15 -0800 Subject: [PATCH 1/3] Add regression test for #4384 --- src/Azure/Azure.sln | 15 +++++ .../FunctionalTests/WebAuthenticationTests.cs | 59 ++++++++++++++++++- .../AzureAD.WebSite/AzureAD.WebSite.csproj | 12 +--- .../testassets/AzureAD.WebSite/Program.cs | 53 +---------------- 4 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/Azure/Azure.sln b/src/Azure/Azure.sln index b26a534edd66..b6435b508151 100644 --- a/src/Azure/Azure.sln +++ b/src/Azure/Azure.sln @@ -73,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.TestHo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization", "..\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj", "{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.OpenIdConnect", "..\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj", "{F44054A2-DAC9-467F-B899-F35F9DCDAE9C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -383,6 +385,18 @@ Global {C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x64.Build.0 = Release|Any CPU {C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x86.ActiveCfg = Release|Any CPU {C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x86.Build.0 = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x64.ActiveCfg = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x64.Build.0 = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x86.ActiveCfg = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x86.Build.0 = Debug|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|Any CPU.Build.0 = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x64.ActiveCfg = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x64.Build.0 = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.ActiveCfg = Release|Any CPU + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -418,6 +432,7 @@ Global {38027842-48A7-4A64-A44F-004BAF0AB450} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} {C520D48E-87A0-463D-B4CF-3E6B68F8F4D0} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} {C57DFBC2-A887-44B4-A149-7ABFA6D98F7E} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} + {F44054A2-DAC9-467F-B899-F35F9DCDAE9C} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {81AADD49-473B-43ED-8A08-F6B7A058AA39} diff --git a/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs b/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs index dc8e5f827196..d724ee2561fd 100644 --- a/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs +++ b/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using Microsoft.AspNetCore.Authorization; using System.Net; @@ -11,7 +11,9 @@ using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Xunit; @@ -158,5 +160,60 @@ public async Task ADB2CEndpoints_AreAvailable_When_Authentication_IsAdded(string // Assert Assert.Equal(expectedStatusCode, response.StatusCode); } + + [Fact] + public async Task ADB2C_EndToEnd_PasswordReset() + { + var client = Factory.WithWebHostBuilder(builder => builder.ConfigureTestServices( + services => + { + services + .AddAuthentication(AzureADB2CDefaults.AuthenticationScheme) + .AddAzureADB2C(o => + { + o.Instance = "https://login.microsoftonline.com/tfp/"; + o.ClientId = "ClientId"; + o.CallbackPath = "/signin-oidc"; + o.Domain = "test.onmicrosoft.com"; + o.SignUpSignInPolicyId = "B2C_1_SiUpIn"; + o.ResetPasswordPolicyId = "B2C_1_SSPR"; + o.EditProfilePolicyId = "B2C_1_SiPe"; + }); + + services.Configure(AzureADB2CDefaults.OpenIdScheme, o => + { + o.Configuration = new OpenIdConnectConfiguration() + { + Issuer = "https://www.example.com", + TokenEndpoint = "https://www.example.com/token", + AuthorizationEndpoint = "https://www.example.com/authorize", + EndSessionEndpoint = "https://www.example.com/logout" + }; + // CookieContainer doesn't allow cookies from other paths + o.CorrelationCookie.Path = "/"; + o.NonceCookie.Path = "/"; + }); + + services.AddMvc(o => o.Filters.Add( + new AuthorizeFilter(new AuthorizationPolicyBuilder(new[] { AzureADB2CDefaults.AuthenticationScheme }) + .RequireAuthenticatedUser().Build()))); + })).CreateClient(new WebApplicationFactoryClientOptions() { AllowAutoRedirect = false }); + + var response = await client.GetAsync("/api/get"); + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + + var location = response.Headers.Location; + Assert.StartsWith("https://www.example.com/authorize", location.AbsoluteUri); + var queryString = location.Query; + var query = QueryHelpers.ParseQuery(queryString); + var state = query["state"]; + Assert.False(StringValues.IsNullOrEmpty(state)); + + // Mock Authorization response + response = await client.GetAsync($"/signin-oidc?error=access_denied&error_description=AADB2C90118&state={state}"); + + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal("/AzureADB2C/Account/ResetPassword/AzureADB2C", response.Headers.Location.OriginalString); + } } } diff --git a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj index 73b832bf22a7..25e2626cfe21 100644 --- a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj +++ b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -9,16 +9,8 @@ - + - - - - - - - - diff --git a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs index 01046c73a0e4..998a9ab3243b 100644 --- a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs +++ b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -23,55 +23,8 @@ public static void Main(string[] args) public static IWebHostBuilder CreateWebHostBuilder(string[] args) { - var builder = new WebHostBuilder() - .UseKestrel((builderContext, options) => - { - options.Configure(builderContext.Configuration.GetSection("Kestrel")); - }) - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - var env = hostingContext.HostingEnvironment; - - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - - if (env.IsDevelopment()) - { - var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); - if (appAssembly != null) - { - config.AddUserSecrets(appAssembly, optional: true); - } - } - - config.AddEnvironmentVariables(); - - if (args != null) - { - config.AddCommandLine(args); - } - }) - .ConfigureLogging((hostingContext, logging) => - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - }) - .UseIISIntegration() - .UseDefaultServiceProvider((context, options) => - { - options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); - }); - - if (args != null) - { - builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); - } - - builder.UseStartup(); - - return builder; + return WebHost.CreateDefaultBuilder() + .UseStartup(); } } } From f204ee3b5a3dfe15d33a6d5aae08587abc0a94f9 Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 7 Dec 2018 14:40:08 -0800 Subject: [PATCH 2/3] Fix up how OIDC errors flow #4384 --- .../Authentication/Core/src/HandleRequestResult.cs | 7 ++++++- .../Authentication/Core/src/RemoteAuthenticationHandler.cs | 4 ++-- src/Security/Authentication/OAuth/src/OAuthHandler.cs | 4 +++- .../OpenIdConnect/src/OpenIdConnectHandler.cs | 6 +++++- src/Security/Authentication/Twitter/src/TwitterHandler.cs | 6 ++++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Security/Authentication/Core/src/HandleRequestResult.cs b/src/Security/Authentication/Core/src/HandleRequestResult.cs index da9b6ea01cf4..0a9a179db3d8 100644 --- a/src/Security/Authentication/Core/src/HandleRequestResult.cs +++ b/src/Security/Authentication/Core/src/HandleRequestResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -92,5 +92,10 @@ public static HandleRequestResult SkipHandler() { return new HandleRequestResult() { Skipped = true }; } + + public new static HandleRequestResult NoResult() + { + return new HandleRequestResult() { None = true }; + } } } diff --git a/src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs b/src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs index 95ededd3492d..a8975f11a027 100644 --- a/src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs +++ b/src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs @@ -283,7 +283,7 @@ protected virtual async Task HandleAccessDeniedErrorAsync(A return HandleRequestResult.Handle(); } - return HandleRequestResult.Fail("Access was denied by the resource owner or by the remote server.", properties); + return HandleRequestResult.NoResult(); } } -} \ No newline at end of file +} diff --git a/src/Security/Authentication/OAuth/src/OAuthHandler.cs b/src/Security/Authentication/OAuth/src/OAuthHandler.cs index 8bbe34f4b017..a84f8a485ad6 100644 --- a/src/Security/Authentication/OAuth/src/OAuthHandler.cs +++ b/src/Security/Authentication/OAuth/src/OAuthHandler.cs @@ -71,7 +71,9 @@ protected override async Task HandleRemoteAuthenticateAsync // Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. if (StringValues.Equals(error, "access_denied")) { - return await HandleAccessDeniedErrorAsync(properties); + var result = await HandleAccessDeniedErrorAsync(properties); + return !result.None ? result + : HandleRequestResult.Fail("Access was denied by the resource owner or by the remote server.", properties); } var failureMessage = new StringBuilder(); diff --git a/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs b/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs index 40e0eacbb846..982376387307 100644 --- a/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs +++ b/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs @@ -560,7 +560,11 @@ protected override async Task HandleRemoteAuthenticateAsync // Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. if (string.Equals(authorizationResponse.Error, "access_denied", StringComparison.Ordinal)) { - return await HandleAccessDeniedErrorAsync(properties); + var result = await HandleAccessDeniedErrorAsync(properties); + if (!result.None) + { + return result; + } } return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null), properties); diff --git a/src/Security/Authentication/Twitter/src/TwitterHandler.cs b/src/Security/Authentication/Twitter/src/TwitterHandler.cs index cc8591ed3200..01f80dc72f9f 100644 --- a/src/Security/Authentication/Twitter/src/TwitterHandler.cs +++ b/src/Security/Authentication/Twitter/src/TwitterHandler.cs @@ -62,7 +62,9 @@ protected override async Task HandleRemoteAuthenticateAsync // approve the authorization demand requested by the remote authorization server. // Since it's a frequent scenario (that is not caused by incorrect configuration), // denied errors are handled differently using HandleAccessDeniedErrorAsync(). - return await HandleAccessDeniedErrorAsync(properties); + var result = await HandleAccessDeniedErrorAsync(properties); + return !result.None ? result + : HandleRequestResult.Fail("Access was denied by the resource owner or by the remote server.", properties); } var returnedToken = query["oauth_token"]; @@ -311,4 +313,4 @@ private string ComputeSignature(string consumerSecret, string tokenSecret, strin } } } -} \ No newline at end of file +} From 647fd02ff292346170299060e2f7e9bbb7934849 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 15 Jan 2019 16:30:27 -0800 Subject: [PATCH 3/3] Fixup sln for debugging --- src/Azure/Azure.sln | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Azure/Azure.sln b/src/Azure/Azure.sln index b6435b508151..f3724c7bc774 100644 --- a/src/Azure/Azure.sln +++ b/src/Azure/Azure.sln @@ -75,6 +75,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Author EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.OpenIdConnect", "..\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj", "{F44054A2-DAC9-467F-B899-F35F9DCDAE9C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.OAuth", "..\Security\Authentication\OAuth\src\Microsoft.AspNetCore.Authentication.OAuth.csproj", "{406DF28A-0B58-408E-96B0-2D373EE36352}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication", "..\Security\Authentication\Core\src\Microsoft.AspNetCore.Authentication.csproj", "{A5E7BA46-B76B-467A-88FA-38E04D0A42FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -397,6 +401,30 @@ Global {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x64.Build.0 = Release|Any CPU {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.ActiveCfg = Release|Any CPU {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.Build.0 = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|Any CPU.Build.0 = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x64.ActiveCfg = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x64.Build.0 = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x86.ActiveCfg = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x86.Build.0 = Debug|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|Any CPU.ActiveCfg = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|Any CPU.Build.0 = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x64.ActiveCfg = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x64.Build.0 = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x86.ActiveCfg = Release|Any CPU + {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x86.Build.0 = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x64.Build.0 = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x86.Build.0 = Debug|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|Any CPU.Build.0 = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x64.ActiveCfg = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x64.Build.0 = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x86.ActiveCfg = Release|Any CPU + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -433,6 +461,8 @@ Global {C520D48E-87A0-463D-B4CF-3E6B68F8F4D0} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} {C57DFBC2-A887-44B4-A149-7ABFA6D98F7E} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} {F44054A2-DAC9-467F-B899-F35F9DCDAE9C} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} + {406DF28A-0B58-408E-96B0-2D373EE36352} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} + {A5E7BA46-B76B-467A-88FA-38E04D0A42FC} = {84622717-F98A-4DE2-806E-1EF89C45C0EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {81AADD49-473B-43ED-8A08-F6B7A058AA39}