8

I'm a little flummoxed by trying to implement the IPasswordStore in the new asp.net mvc 5. I want to use my own ORM.

Take this familiar code snippet from the scaffolded 'AccountController' that runs when the 'register' screen is used in the example project.

public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser() { UserName = model.UserName };
            var result = await UserManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                await SignInAsync(user, isPersistent: false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                AddErrors(result);
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

the

  var result = await UserManager.CreateAysnc(user, model.Password)

line first calls the IPasswordStore function

  public Task SetPasswordHashAsync(TUser user, string passwordHash)

without having first called from IUserStore

   public Task CreateAsync(TUser user)

How do I set the password hash if the user isn't created in the db yet? Furthermore, we actually don't even know if we can create the proposed 'user' because we haven't checked to see if the username is taken yet using

   public Task<TUser> FindByNameAsync(string userNameIn)

which is called right afterwards.

Any ideas?

Isaac Bolinger
  • 7,328
  • 11
  • 52
  • 90

3 Answers3

2

You are right in noticing that you probably should not persist the password hash for the user before the user is created. You could save the password hash to a location that will be reverted if the user is not actually created anyway. The password should not be stored untill the user is created and stored, i.e. in CreateAsync(TUser user).

The IdentityUser in the EntityFramework implementation would be such a location, and it would enable you to store both user and password information in the CreateAsync method. I am not saying you should reference the Identity.EntityFramework assembly, just saying that a User object that has both User and PasswordHash info similar to the IdentityUser is one possible solution.

Olav Nybø
  • 11,454
  • 8
  • 42
  • 34
  • Just stored it in a temporary dictionary in my IUserStore implementation, if the user didn't already exist. Works alright. – Isaac Bolinger Nov 24 '13 at 06:35
  • 5
    Can someone explain why they defined the behavior like this? there must be a reason... it seems a bit weird otherwise. – Matt Howlett Apr 07 '15 at 12:51
  • 1
    @Matt Howlett: Agreed, would expect at the very least some documentation for the IUserPasswordStore.SetPasswordHashAsync() method. Big gap here. And why async if it's not persisted? – William T. Mallard Jul 10 '16 at 23:49
1

Always set the password hash property of your user object in the IUserPasswordStore.SetPasswordHashAsync method body as the IUser interface doesn't define the property so UserManager can't set it. If you do this and then only update the user database record if the user exists you can add the password hash when you finally create your user in the IUserStore.CreateAsync method.

This principal also works for the IUserSecurityStampStore.SetSecurityStampAsync method.

stevehipwell
  • 56,138
  • 6
  • 44
  • 61
0

Well, I was there, downloaded the source code for ASP.NET Identity from Git and found:

public virtual async Task<IdentityResult> CreateAsync(TUser user, string password)
        {
            ThrowIfDisposed();
            var passwordStore = GetPasswordStore();
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (password == null)
            {
                throw new ArgumentNullException("password");
            }
            var result = await UpdatePasswordHash(passwordStore, user, password);
            if (!result.Succeeded)
            {
                return result;
            }
            return await CreateAsync(user);
        }

If you provide your own then, at a minimum one should be as secure as the "out of the box" solution is. when I implemented mine I looked at each function and implemented my own with my own data layer and classes.

I ended up implementing quite some code that otherwize I would not have, in regards to the hashing of your users Passwords I user something similar to Zespri's answer. Call me paranoid, not like to use the default salt for my Passwords....

Community
  • 1
  • 1