Search code examples
c#asp.net-core.net-coreasp.net-core-identity

ASP CORE Identity browser login fail but via UserManager.CheckPasswordAsync with literal string work


I'm use mvc core 3.1 with Identity 3.1.2, with default built-in UI:

services.AddIdentity<IdentityUser, IdentityRole>(opt =>
{
    opt.User.RequireUniqueEmail = true;
    opt.SignIn.RequireConfirmedEmail = false;
    opt.SignIn.RequireConfirmedAccount = false;
}).AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddDefaultUI();

I wrote hard code that creates initial administrator user:

var userAdminExists = await _userManager.FindByNameAsync("Admin");

if (userAdminExists == null)
{
    var user = new IdentityUser
    {
        Id = Guid.NewGuid().ToString(),
        UserName = "Admin",
        Email = "xxxxxx@gmail.com",
        EmailConfirmed = true
    };


    var x = await _userManager.CreateAsync(user, _IConfiguration.GetValue<string>("defaultAdminPassword"));

    await _userManager.AddToRoleAsync(user, "AdminRole");
}

(Of course I also tried to write the literal password inside the code, for those wondering about the IConfiguration, and I also watched the entries at break-points and everything worked as planned).

the user is created, but when i login with this email address and password in the browser i get "Invalid login attempt." (in normal response, status 200). I wrote test code:

var pass = _configuration.GetValue<string>("defaultAdminPassword");
var res = await _userManager.CheckPasswordAsync(await _userManager.FindByEmailAsync("xxxx@gmail.com"), pass);

And of course it return true...

furthermore, also if i reset password via email reset link ("Forgot your password?") and i set new password, is not work...

but new users created through the "Register" UI interface, work great.

What am I missing?


Solution

  • When you use the Identity UI to sign in, you end up calling SignInManager.PasswordSignInAsync:

    public virtual Task<SignInResult> PasswordSignInAsync(
        string userName, string password, bool isPersistent, bool lockoutOnFailure);
    

    As you can see from this signature, the first parameter is userName. When you use the UI as you've described, you provide the email address where the userName is expected. However, when you call FindByEmailAsync and pass the result into CheckPasswordAsync, you find the user based on Email and not UserName, which works.

    When you register a new user using the Identity UI, you end up running this code:

    await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
    await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
    

    As you can see, this sets both the UserName and the Email to Input.Email. This is why you are able to sign in with these accounts.