Есть ли способ заставить корректно работать SendTwoFactorCode сразу после вызова PasswordSignIn?
Краткий ответ: Нет
Предлагаемая альтернатива:
Поток, обычно рекомендуемый в документации:
- Аутентификация пользователя через имя пользователя и пароль.
- Если действительный пользователь и пароль, и требуется дополнительная проверка, так как 2FA включен, топеренаправить на страницу для отправки кода.
- Если пользователь инициирует отправку кода, код отправляется, а затем пользователь перенаправляется на страницу подтверждения.
ВторойШаг обычно заключается в подтверждении поставщика, если есть несколько (например, SMS, электронная почта, и т. д.), чтобы использовать для проверки 2-го фактора.
Например, следующее выполняет перенаправление на RequiresVerification
результат
Аккаунт / Логин
//...
var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
Но так как вы уже определили, чтоВы собираетесь отправить код по электронной почте, затем вы можете пропустить второй шаг и перенаправить непосредственно на проверочный код, который может быть там, где код отправляется и проверяется.
Account / VerifyCode
[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}
var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};
return View(model);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}
var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Это должно позволить включить TwoFactorCookie
в следующий запрос, чтобы GetVerifiedUserIdAsync
вел себя как ожидалось.
/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}
Источник
Так же, как вы указали
Если я сделаю второй запрос, вызов SendTwoFactorCode будет работать так, как я ожидаю.
Этот второй запрос важен, так как онбудет включать файл cookie, установленный в предыдущем запросе.
Ссылка Как SignInManager проверяет требование 2FA