UserManager.FindByEmailAsync returns null, but the user exists in the database

Viewed 4690

UserManager.FindByEmailAsync returns null, but the user exists in the database.

The code below explain the strange problem:

var email = info.Principal.FindFirstValue(ClaimTypes.Email);
var test = new Data.ApplicationDbContext().Users.First(x => x.NormalizedEmail == email);
var usermail = await _userManager.FindByEmailAsync(email);

Console.WriteLine(test == null);      //false
Console.WriteLine(usermail == null);  //true

EDIT

Also via _userManager itself, the desired user is obtained:

var test = _userManager.Users.FirstOrDefault(x => x.NormalizedEmail == email);
var usermail = await _userManager.FindByEmailAsync(email);

Console.WriteLine(test == null);      //false
Console.WriteLine(usermail == null);  //true

It should be noted that the user was not created in a "conventional" manner, but by Data-Seed (in OnModelCreating):

protected override void OnModelCreating(ModelBuilder builder)
{
    var users = new (string email, string name)[] {
        ("xyz@gmail.com", "admin")
    };

    var appUsers = users.Select(x => new ApplicationUser
    {
        Email = x.email,
        NormalizedEmail = x.email,
        NormalizedUserName = x.email,
        UserName = x.email,
        EmailConfirmed = true,
        Id = Guid.NewGuid().ToString(),
        SecurityStamp = Guid.NewGuid().ToString()
    }).ToArray();

    var role = new IdentityRole("Admins") { Id = Guid.NewGuid().ToString() };
    var roleToUser = appUsers.Select(x => new IdentityUserRole<string> { RoleId = role.Id, UserId = x.Id });

    builder.Entity<ApplicationUser>().HasData(appUsers);
    builder.Entity<IdentityRole>().HasData(role);
    builder.Entity<IdentityUserRole<string>>().HasData(roleToUser);
        
    base.OnModelCreating(builder);
}
2 Answers

As you can see in the source-code links in the comments I made to your OP FindByEmailAsync performs a NormalizeKey before it actually starts searching for the user.

email = NormalizeKey(email);

This NormalizeKey(email) is done by the UpperInvariantLookupNormalizer that will do the following string operation on your email

return key.Normalize().ToUpperInvariant();

Now the part of your code that is causing the "strange" behaviour is the missing call to normalize in your code when creating the user:

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = x.email,
    NormalizedUserName = x.email,
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();

Not normalizing the email will still make it discoverable via the users table as this simply compares the NormalizedEmail (which you did not normalize when you created the user) with the not-normalized email you pass as argument:

_userManager.Users.FirstOrDefault(x => x.NormalizedEmail == email);

...however, userManager.FindByEmailAsync will normalize it first and afterwards do the search...

_userManager.FindByEmailAsync(email);

...and therefore not find the user.

Change your code to the following:

// inject via using Microsoft.AspNetCore.Identity
protected ILookupNormalizer normalizer;

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = normalizer.Normalize(x.email),
    NormalizedUserName = normalizer.Normalize(x.email),
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();

For NormalizedEmail and NormalizedUserName, it should be uppercase letter.

Try

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = x.email.ToUpper(),
    NormalizedUserName = x.email.ToUpper(),
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();
Related