在 ASP.NET Core Identity(独立)中,您如何执行 2FA?

In ASP.NET Core Identity (standalone), how do you enforce 2FA?

我在 Razor Pages 项目中使用 ASP.NET Core Identity。 如果经过身份验证的用户不满足特定策略(例如未启用 2FA),您如何重定向到特定页面(例如启用 2FA 页面)?

我想避免在每个 OnGet 中检查声明,例如:

    public IActionResult OnGet()
    {
        var claimTwoFactorEnabled = User.Claims.FirstOrDefault(t => t.Type == "TwoFactorEnabled");

        if (claimTwoFactorEnabled != null && "true".Equals(claimTwoFactorEnabled.Value))
        {
            // You logged in with MFA, do the admin stuff
        }
        else
        {
            return Redirect("/Identity/Account/Manage/TwoFactorAuthentication");
        }

        return Page();
    }

(如https://damienbod.com/2020/01/03/requiring-mfa-for-admin-pages-in-an-asp-net-core-identity-application/所述)

我确实找到了这个 但它似乎需要 OpenIdConnect。我正在使用独立身份。

我从 https://damienbod.com/2020/01/03/requiring-mfa-for-admin-pages-in-an-asp-net-core-identity-application/ 开始 AdditionalUserClaimsPrincipalFactory:

using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
 
namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }
 
        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;
 
            var claims = new List<Claim>();
 
            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("TwoFactorEnabled", "true"));
            }
            else
            {
                claims.Add(new Claim("TwoFactorEnabled", "false")); ;
            }
 
            identity.AddClaims(claims);
            return principal;
        }
    }
}

此外,在启动的 ConfigureServices 中,添加:

   services.AddAuthorization(options =>
            {
                options.AddPolicy("TwoFactorEnabled",
                    x => x.RequireClaim("TwoFactorEnabled", "true")
                );
                // you can also combine with a role based policy
                options.AddPolicy("RequireAdminRole",
                    policy => policy.RequireRole("Admin", "SuperAdmin").RequireClaim("TwoFactorEnabled", "true"));

            });

然后我没有向每个 OnGet 方法添加 if 逻辑,而是添加 [Authorize(Policy = "TwoFactorEnabled")] 在代码隐藏文件的顶部,例如:

    [Authorize(Policy = "TwoFactorEnabled")]
    public class DetailModel : PageModel
    {