I'm working towards moving an application from Web Forms to MVC and opted to go with MVC 6 using ASP.NET Core.
In my current application I have a custom password hasher used with Identity. The implementation is very simple in my custom UserManager class:
public ApplicationUserManager()
: base(new UserStore<IdentityUser>(new AuthContext()))
{
this.PasswordHasher = new SqlPasswordHasher();
}
I'm trying to do the same with .NET Core but the PasswordHasher property doesn't exist in UserManager. I see that the constructor will take an IPasswordHasher parameter so I tried this:
public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, new SqlPasswordHasher(), userValidators, passwordValidators, keyNormalizer, errors,
serviceProvider, logger)
{
}
In SqlPasswordHasher I'm simply overriding the VerifyHashedPassword method which looks like this:
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
// My custom logic is here
...
}
However, the above doesn't work. I have a breakpoint set in the VerifyHashedPassword method of SqlPasswordHasher and it doesn't get triggered.
I thought I was going about this the wrong way and I should be utilizing DI to accomplish this. I updated the constructor of my user manager so that it doesn't instantiate a new SqlPasswordHasher, but uses the default interface parameter instead:
public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
serviceProvider, logger)
{
}
Then in Startup.cs I added a scoped service:
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
But again, this doesn't work and the breakpoint in SqlPasswordHasher is never triggered.
I have a similar line for my custom Sign In Manager:
services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
That works great. The ApplicationSignInManager takes a UserManager parameter and I can see that the UserManager takes an IPasswordHasher parameter.
I'm assuming SignInManager uses the UserManager which uses the PasswordHasher. So my question is, how do I get the UserManager to use my custom Password Hasher? Or if thats not the case, how do I get the SignInManager to use my Password hasher?
EDIT: I've been able to confirm that when my ApplicationUserManager is instantiated, my SqlPasswordHasher is being used in the constructor so the DI is working properly. I just can't figure out why my override of VerifyHashedPassword is not being triggered.
It turns out the problem was not related to code at all. Adding my SqlPasswordHasher to the services via
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
worked perfectly.
The problem was with how I migrated the data. Since I was using an existing database that was being used with an older version of Identity, I had to add the following fields to my existing AspNetUsers table:
NormalizedUserName
ConcurrencyStamp
LockoutEnd
NormalizedEmail
However I didn't populate the NormalizedUserName or NormalizedEmail fields. So that's why it was never triggering my override of VerifyHashedPassword; because it never found my user since it was looking up based on NormalizedUserName.
Once I populated those fields it started triggering my VerifyHashedPassword method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With