6

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.

3
  • which one did worked? on the same boat. apparently v2.0 its injected within usermanager it self. Been trying to use this within cusotm passwordvalidator without success. Commented Aug 15, 2017 at 0:44
  • @Jay the code I had worked. My issue was actually with the data. I haven't looked at this for 2.0 so I'm not sure if my code above would work there. Sorry. (Also, sorry I responded so late) Commented Aug 31, 2017 at 13:42
  • no worries. I have figured it out. in my case it was a silly mistake.Thanks for your message Commented Aug 31, 2017 at 23:53

1 Answer 1

19

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I just wasted hours on this. Thanks a million for posting your solution.
@ScottHulme no problem, I'm glad it could help.

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.