11

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)[] {
        ("[email protected]", "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);
}
10
  • You did not say what are your actually wanting? You just presented some code and then said nothing! Commented Jan 14, 2019 at 13:35
  • @TanvirArjel Can you point to missing information after the title and code? I expect that (await _userManager.FindByEmailAsync(email)) == new Data.ApplicationDbContext().Users.First(x => x.NormalizedEmail == email); Commented Jan 14, 2019 at 13:47
  • var email = info.Principal.FindFirstValue(ClaimTypes.Email); is returning the email that is being used for external login. So have you created an user with that email please? Commented Jan 14, 2019 at 13:51
  • Can you add your ApplicationDbContext() code please? Commented Jan 14, 2019 at 13:56
  • 2
    I checked the UserManager's FindByEmailAsync which calls FirstOrDefaultAsync on the Users set just like you do. The only difference between your query and what the UserStore does would in line 1414 but this can be ignored since FindByEmail returns null for you. Commented Jan 14, 2019 at 21:58

3 Answers 3

11

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();
Sign up to request clarification or add additional context in comments.

Comments

2

I solved this problem just by clearing the users that were registered and I registered again, soon after FindByEmailAsync() started to find the Users, I'm not sure why this happens but I'll leave my report here because it might help someone. good luck!

1 Comment

Interesting, I had the same problem. Not sure why it then starts to work with the exact same email.
0

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();

1 Comment

I would not use this in production as it deviates from the way Identity normalizes values. Simply using ToUpper is problematic as it depends on the current culture which may not contain uppercase variants; Also a call to normalize is missing. So FindByEmailAsync still may not necessarily work with international domains.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.