Elsassmania
Elsassmania

Reputation: 175

Override the CheckPasswordAsync when user logs in with Identity 2.0

I implemented the authentication with ASP.NET Core Identity 2.0 in an existing App. As I use my own database schema and classes, I use my own User class and I had to create a custom UserStore. The problem is when I created the users, I hashed their password not with the same method which is use in Identity and the login failed when this function is called :

var result = await _signInManager.PasswordSignInAsync(model.Login, model.Password, model.RememberMe, lockoutOnFailure: false);

I have seen on different post that you need to override the CheckPasswordAsync() function to add your own way to check if the passwords are similar. However, I tried to implement it but it is no a success...

CustomUserManager:

public class CustomUserManager<TUser> : UserManager<TUser> where TUser : class
{
    public CustomUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators,
IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }

    public override Task<bool> CheckPasswordAsync(TUser user, string password)
    {
        return base.CheckPasswordAsync(user, password);
    }
}

AccountController constructor :

public AccountController(
        CustomUserManager<User> userManager,
        SignInManager<User> signInManager,
        ILogger<AccountController> logger)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
    }

I even tried to Add the scope in the services configuration:

services.AddIdentity<User, ProfileUser>().AddDefaultTokenProviders();
services.AddTransient<IUserStore<User>, UserIdentity>();
services.AddTransient<IRoleStore<ProfileUser>, ProfileIdentity>();
services.AddScoped< UserManager<User>, CustomUserManager>();

But it doesn't work. When I launch the App and try to access to the login page, I get an error:

InvalidOperationException: Unable to resolve service for type 'CustomUserManager`1[Maquetteur.Entities.User]' while attempting to activate 'Maquetteur.Web.AccountController'.

With the default UserManager I can access to the login Page but the login function fails. With the custom UserManager I cannot access to the login page.

Do you have an idea what I'm doing wrong ? Or maybe there is an other way to implement our own way to check if passwords are similar when the user logs in ?

Upvotes: 2

Views: 1855

Answers (1)

Kirk Larkin
Kirk Larkin

Reputation: 93063

You were heading in the right direction when you added this line:

services.AddScoped<UserManager<User>, CustomUserManager>();

This line essentially says: If I ask for a UserManager<User>, please give me a CustomUserManager instead. However, that's not exactly what you intended here. Instead, you wanted to say: If I ask for a CustomUserManager, please just give me a CustomUserManager.

It's actually quite easy to fix - You can just register it when you add the Identity services. e.g.:

services
    .AddIdentity<User, ProfileUser>()
    .AddUserManager<CustomUserManager<User>>()
    .AddDefaultTokenProviders();

Note: There are also additional extensions for registering your custom IUserStore and IRoleStore implementations.

Upvotes: 4

Related Questions