Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What causes SignInManager.SendTwoFactorCodeAsync to return false?

Documentation on the new Microsoft Identity Framework seems a bit sparse. I'm currently modifying the standard project template, and I came across some curious behavior when trying to sign in users with two factor authentication.

We're only allowing 2FA via SMS, so I bypassed the option for users to select how they want their code. Instead, when a user with 2FA and a verified phone number tries to log in, they trigger this code:

var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
    case SignInStatus.RequiresVerification:
        if (!await SignInManager.SendTwoFactorCodeAsync("Phone Code"))
        {
            TempData["ErrorMessage"] = "We could not verify your account. Please try again.";
            return RedirectToAction("Error", "Error");
        }
        return RedirectToAction("VerifyCode", new { Provider = "Phone Code", ReturnUrl = returnUrl, RememberMe = model.RememberMe });
    // other cases
}

Most of the time, this works fine. But after a user logs off, the first time they log back on again, SendTwoFactorCodeAsync returns false and they're directed to the error page.

What scenarios would cause SendTwoFactorCodeAsync to consistently fail the first time it's called for a logged out user?

like image 535
Bob Tway Avatar asked Jul 11 '16 16:07

Bob Tway


1 Answers

I forgot that the O in Owin stands for Open. The source code is available to answer this question:

public virtual async Task<bool> SendTwoFactorCodeAsync(string provider)
{
    var userId = await GetVerifiedUserIdAsync().WithCurrentCulture();
    if (userId == null)
    {
        return false;
    }

    var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider).WithCurrentCulture();
    // See IdentityConfig.cs to plug in Email/SMS services to actually send the code
    await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token).WithCurrentCulture();
    return true;
}

This is from the SignInManager, so that call to GetVerifiedUserIdAsync() is the same as calling SignInManager.GetVerifiedUserIdAsync() from your application.

like image 139
Bob Tway Avatar answered Oct 21 '22 03:10

Bob Tway