Search code examples
c#asp.net-coreasp.net-core-identityasp.net-core-2.2

Simple IUserStore, IUserPasswordStore implementation gives Invalid login attempt


I am experimenting with Aspnet Core 2.2 and have replaced the standard IdentityUser with my MyIdentityUser. When I try to log on I get "Invalid login attempt."

What is it I have missed to implement to allow me to log in?


The ConfigureServices method sets up Identity with my MyIdentiyUser and MyIUserStore like so:

services.AddDefaultIdentity<MyIdentityUser>()
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddUserStore<MyUserStore>();

By implementing all IUserStore and IUserPasswordStore methods as sparingly as possbile (I started with every method throwing NotImplementedException and then implementing the ones that were called) I got to this:

public class MyUserStore : IUserStore<MyIdentityUser>, IUserPasswordStore<MyIdentityUser>
{
    public void Dispose() { }

    public Task<MyIdentityUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
    {
        var ret = new MyIdentityUser
        {
            Id = "1",
            UserName = normalizedUserName
        };
        return Task.FromResult(ret);
    }

    public Task<string> GetUserIdAsync(MyIdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult(user.UserName);
    }

    public Task<string> GetPasswordHashAsync(MyIdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult("asdf");
    }
    ... the rest of the methods throws exception.
}

For the sake of being complete here is my MyIdentityUser

public class MyIdentityUser
{
    public string Id {get;set;}
    public string UserName {get;set}
}

and start of _LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@inject SignInManager<MyIdentityUser> SignInManager
@inject UserManager<MyIdentityUser> UserManager

Solution

  • You were returning an empty string as hash

    public Task<string> GetPasswordHashAsync(MyIdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult(string.Empty);
    }
    

    But even an empty password, do not results in an empty hash string. On top of that, there are different algorithms used (which can be configured in Startup.cs, when adding Identity).

    You will have to return a real, calculated hash from a specific password if you want it succeed, otherwise it will recognized the entered password returns a different hash then the "stored" one and will cause a failure in login.

    Just because you are faking/implementing the user store do not mean the hash validations won't be performed.

    public Task<string> GetPasswordHashAsync(MyIdentityUser user, CancellationToken cancellationToken)
    {
        var hash = new PasswordHasher<MyIdentityUser>().HashPassword(user, "asdf");
        return Task.FromResult(hash)
    }
    

    Will do, though beware that if you change the password hash algorithm it may fail, since you are newing it, which means it uses the default options for password hashing.