diff --git a/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs index ef7dd95b92..bdbe417934 100644 --- a/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs @@ -5,12 +5,14 @@ using System; using System.ComponentModel.DataAnnotations; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(ForgotPasswordModel<>))] public abstract class ForgotPasswordModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs index d8a55a740a..0fac6f2506 100644 --- a/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -7,12 +7,14 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(LoginModel<>))] public abstract class LoginModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs index cd605793fd..b9ef568f57 100644 --- a/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs @@ -4,12 +4,14 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(LoginWith2faModel<>))] public abstract class LoginWith2faModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs index 128771ae78..ea91bec3ab 100644 --- a/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs @@ -4,12 +4,14 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(LoginWithRecoveryCodeModel<>))] public abstract class LoginWithRecoveryCodeModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs index 1ca9dbee37..adf9edced2 100644 --- a/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -3,12 +3,14 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(LogoutModel<>))] public abstract class LogoutModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs index 57f7eacc4e..ade3184c6a 100644 --- a/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; @@ -12,6 +13,7 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal { + [AllowAnonymous] [IdentityDefaultUI(typeof(RegisterModel<>))] public abstract class RegisterModel : PageModel { diff --git a/src/UI/Areas/Identity/Pages/Error.cshtml.cs b/src/UI/Areas/Identity/Pages/Error.cshtml.cs index 51cf80c2bd..4be543c0b8 100644 --- a/src/UI/Areas/Identity/Pages/Error.cshtml.cs +++ b/src/UI/Areas/Identity/Pages/Error.cshtml.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace Microsoft.AspNetCore.Identity.UI.Pages { + [AllowAnonymous] public class ErrorModel : PageModel { public string RequestId { get; set; } diff --git a/test/Identity.FunctionalTests/AuthorizationTests.cs b/test/Identity.FunctionalTests/AuthorizationTests.cs index 0d77d80f98..12be3ec943 100644 --- a/test/Identity.FunctionalTests/AuthorizationTests.cs +++ b/test/Identity.FunctionalTests/AuthorizationTests.cs @@ -31,7 +31,6 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests "/Identity/Account/Manage/SetPassword", "/Identity/Account/Manage/ShowRecoveryCodes", "/Identity/Account/Manage/TwoFactorAuthentication", - "/Identity/Account/Logout", }; [Theory] @@ -78,7 +77,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests // Arrange var client = ServerFactory.CreateDefaultClient(); await UserStories.RegisterNewUserAsync(client); - + // Act var response = await client.GetAsync(url); @@ -117,5 +116,31 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests // Assert await ResponseAssert.IsHtmlDocumentAsync(response); } + + public static TheoryData UnauthorizedPagesAllowAnonymous => + new TheoryData + { + "/Identity/Error", + "/Identity/Account/Register", + "/Identity/Account/Login", + "/Identity/Account/ForgotPassword", + "/Identity/Account/Logout" + }; + + [Theory] + [MemberData(nameof(UnauthorizedPagesAllowAnonymous))] + public async Task AnonymousUserAllowedAccessToPages_WithGlobalAuthorizationFilter(string url) + { + // Arrange + var server = ServerFactory.CreateServer(builder => + builder.ConfigureServices(services => services.SetupGlobalAuthorizeFilter())); + var client = ServerFactory.CreateDefaultClient(server); + + // Act + var response = await client.GetAsync(url); + + // Assert + await ResponseAssert.IsHtmlDocumentAsync(response); + } } } diff --git a/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs b/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs index 00d4877082..dd87f0088e 100644 --- a/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs +++ b/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Identity.DefaultUI.WebSite; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -25,5 +27,15 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests public static IServiceCollection SetupEmailRequired(this IServiceCollection services) => services.Configure(o => o.SignIn.RequireConfirmedEmail = true); + + public static IServiceCollection SetupGlobalAuthorizeFilter(this IServiceCollection services) => + services.AddMvc(config => + { + var policy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + config.Filters.Add(new AuthorizeFilter(policy)); + }) + .Services; } } diff --git a/test/Identity.FunctionalTests/LoginTests.cs b/test/Identity.FunctionalTests/LoginTests.cs index 6e1994e0c8..17cf9495d0 100644 --- a/test/Identity.FunctionalTests/LoginTests.cs +++ b/test/Identity.FunctionalTests/LoginTests.cs @@ -58,6 +58,28 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests await UserStories.LoginExistingUser2FaAsync(newClient, userName, password, twoFactorKey); } + [Fact] + public async Task CanLogInWithTwoFactorAuthentication_WithGlobalAuthorizeFilter() + { + // Arrange + var server = ServerFactory.CreateServer(builder => + builder.ConfigureServices(services => services.SetupGlobalAuthorizeFilter())); + var client = ServerFactory.CreateDefaultClient(server); + var newClient = ServerFactory.CreateDefaultClient(server); + + var userName = $"{Guid.NewGuid()}@example.com"; + var password = $"!Test.Password1$"; + + var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password); + var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn); + + var twoFactorKey = showRecoveryCodes.Context.AuthenticatorKey; + + // Act & Assert + // Use a new client to simulate a new browser session. + await UserStories.LoginExistingUser2FaAsync(newClient, userName, password, twoFactorKey); + } + [Fact] public async Task CanLogInWithRecoveryCode() { @@ -79,6 +101,28 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests await UserStories.LoginExistingUserRecoveryCodeAsync(newClient, userName, password, recoveryCode); } + [Fact] + public async Task CanLogInWithRecoveryCode_WithGlobalAuthorizeFilter() + { + // Arrange + var server = ServerFactory.CreateServer(builder => + builder.ConfigureServices(services => services.SetupGlobalAuthorizeFilter())); + var client = ServerFactory.CreateDefaultClient(server); + var newClient = ServerFactory.CreateDefaultClient(server); + + var userName = $"{Guid.NewGuid()}@example.com"; + var password = $"!Test.Password1$"; + + var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password); + var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn); + + var recoveryCode = showRecoveryCodes.Context.RecoveryCodes.First(); + + // Act & Assert + // Use a new client to simulate a new browser session. + await UserStories.LoginExistingUserRecoveryCodeAsync(newClient, userName, password, recoveryCode); + } + [Fact] public async Task CannotLogInWithoutRequiredEmailConfirmation() { diff --git a/test/WebSites/Identity.DefaultUI.WebSite/Pages/Index.cshtml.cs b/test/WebSites/Identity.DefaultUI.WebSite/Pages/Index.cshtml.cs index 1e78f1257f..a799fceec8 100644 --- a/test/WebSites/Identity.DefaultUI.WebSite/Pages/Index.cshtml.cs +++ b/test/WebSites/Identity.DefaultUI.WebSite/Pages/Index.cshtml.cs @@ -1,10 +1,12 @@ // 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 Microsoft.AspNetCore.Mvc.RazorPages; namespace Identity.DefaultUI.WebSite.Pages { + [AllowAnonymous] public class IndexModel : PageModel { public void OnGet()