You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SignInManager.TwoFactorSignInAsync始终返回false的原因排查求助

双因素认证TwoFactorSignInAsync始终返回false的修复方案

核心问题分析

你的代码存在几个关键逻辑错误,导致TwoFactorSignInAsync无法正常验证OTP:

  1. Login Action流程顺序颠倒:先执行PasswordSignInAsync再验证用户密码,导致双因素会话未正确初始化
  2. LoginwithOTP Action未使用双因素会话用户:直接通过用户名查找用户,而非从双因素认证会话中获取,OTP验证依赖会话上下文
  3. 代码逻辑顺序错误:多处存在先执行操作后做合法性检查的问题

具体修复步骤

1. 修复Login Action的双因素初始化流程

调整代码顺序,先验证用户身份,再启动双因素会话:

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginUserDTO credentials)
{
    var user = await userManager.FindByEmailAsync(credentials.Email);
    
    // 先检查用户是否存在且密码正确
    if (user == null || !await userManager.CheckPasswordAsync(user, credentials.Password))
    {
        return StatusCode(StatusCodes.Status401Unauthorized,
            new { Status = "Failed", Message = "Email or Password is incorrect" });
    }

    if (user.TwoFactorEnabled)
    {
        // 登出当前会话,启动双因素认证流程
        await signInManager.SignOutAsync();
        var signInResult = await signInManager.PasswordSignInAsync(user, credentials.Password, false, true);
        
        // 确认双因素流程已触发
        if (signInResult.RequiresTwoFactor)
        {
            var twoFactorToken = await userManager.GenerateTwoFactorTokenAsync(user, "Email");
            var message = new Message(new string[] { user.Email }, "OTP Confirmation", twoFactorToken);
            emailService.SendEmail(message);
            return StatusCode(StatusCodes.Status200OK, new Models.Response { Status = "Success", Message = $"We have sent an OTP to your Email {user.Email}" });
        }
        return StatusCode(StatusCodes.Status400BadRequest, new { Status = "Failed", Message = "Two-factor authentication setup error" });
    }

    // 双因素未启用时直接返回JWT
    var expDate = DateTime.Now.AddDays(2);
    var token = await GenerateJwtToken(user);
    return Ok(new TokenDTO
    {
        Token = token,
        Exp = expDate,
        Message = "User logged in"
    });
}

2. 修复LoginwithOTP Action的OTP验证逻辑

必须从双因素会话中获取用户,再执行OTP验证:

[HttpPost("Logintwo")]
public async Task<IActionResult> LoginwithOTP(LoginUser2FDTO loginUser2FDTO)
{
    // 从双因素认证会话中获取用户,这是OTP验证的前提
    var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
    
    if (user == null)
    {
        return Unauthorized(new { ErrorMessage = "No two-factor authentication session found. Please re-login." });
    }

    // 验证OTP
    var signInResult = await signInManager.TwoFactorSignInAsync("Email", loginUser2FDTO.Code, false, false);

    if (signInResult.Succeeded)
    {
        var expDate = DateTime.Now.AddDays(2);
        var token = await GenerateJwtToken(user);
        return Ok(new TokenDTO
        {
            Token = token,
            Exp = expDate,
            Message = "User logged in"
        });
    }
    else if (signInResult.IsLockedOut)
    {
        return StatusCode(StatusCodes.Status403Forbidden, new { Status = "Failed", Message = "Account locked due to failed attempts. Please try later." });
    }
    else
    {
        return StatusCode(StatusCodes.Status401Unauthorized, new { Status = "Failed", Message = "Invalid OTP code. Please try again." });
    }
}

3. 额外注意事项

  • 确保用户的TwoFactorEnabled字段已被正确设置为true(可在用户注册或设置页面开启)
  • 若需要调整OTP有效期,可在身份配置中添加token生命周期设置:
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    // 其他配置...
    options.Tokens.EmailConfirmationTokenProvider = "Email";
})
.AddEntityFrameworkStores<ITIStore>()
.AddTokenProvider<EmailTokenProvider<ApplicationUser>>("Email")
.AddTokenProviderOptions<EmailTokenProviderOptions>("Email", options =>
{
    options.TokenLifespan = TimeSpan.FromMinutes(15);
});

内容的提问来源于stack exchange,提问作者Ahmed Farag

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:52:47