是否可以 ASP.NET OWIN 设置为以随机/偶尔的间隔向用户质询 2FA 代码



我想配置我的 MVC 应用程序,以便在 1 in N 访问网站时随机挑战用户进行 2 因素身份验证检查。

首先,我只是在登录过程中这样做:

int challengeFrequency = Convert.ToInt16(ConfigurationManager.AppSettings["ChallengeFrequency"]);
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
    case SignInStatus.Success:
        Random rnd = new Random();
        if(rnd.Next(challengeFrequency) == 1)
        {
            if (!await SignInManager.SendTwoFactorCodeAsync("Phone Code"))
            {
                return RedirectToAction("Error");
            }
            return RedirectToAction("VerifyCode", new { Provider = "Phone Code", ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        }
        return RedirectToLocal(returnUrl);
    // other cases
}

但事实证明,我误解了这个过程,如果用户拥有该站点的有效cookie,则不会调用PasswordSignInAsync。我计划更改这些设置,以便用户在任何情况下都必须每次登录,因此在 Startup.Auth 中添加了:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    SlidingExpiration = true,
    ExpireTimeSpan = TimeSpan.FromSeconds(5),
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromSeconds(0), 
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});

所以现在用户每次都受到挑战。但是,当然,如果没有cookie,我们永远不会得到SignInStatus.Success结果,并且用户总是受到密码和2FA代码的挑战。

如何中断该过程以确保用户始终必须使用密码登录,但只是偶尔被要求输入 2FA 代码?

我最终能够通过动态生成一个令牌并使用它来实现 TwoFactorSignIn 方法来实现这一点,如下所示:

int challengeFrequency = Convert.ToInt16(ConfigurationManager.AppSettings["ChallengeFrequency"]);
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
    case SignInStatus.Success:
        return RedirectToLocal(returnUrl);
    case SignInStatus.RequiresVerification:
        Random rnd = new Random();
        if (rnd.Next(1, challengeFrequency) == 1)
        {
            if (!await SignInManager.SendTwoFactorCodeAsync("Phone Code"))
            {
                return RedirectToAction("Login", "Account");
            }
            return RedirectToAction("VerifyCode", new { Provider = "Phone Code", ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        }
        else
        {
            var token = await UserManager.GenerateTwoFactorTokenAsync(user.Id, "Phone Code");
            await SignInManager.TwoFactorSignInAsync("Phone Code", token, false, false);
            return RedirectToLocal(returnUrl);
        }
    // other cases
}

最新更新