ASP.NET OWIN 是否可以设置为以 random/occasional 间隔向用户询问 2FA 代码?
Can ASP.NET OWIN be set to challenge users for a 2FA code at random/occasional intervals?
我想将我的 MVC 应用程序配置为随机质询用户,对网站的 1 比 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
}
我想将我的 MVC 应用程序配置为随机质询用户,对网站的 1 比 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
}