ASP.NET Identity change password

112,393

Solution 1

ApplicationUserManager is the class generated by the ASP.NET Template.

Which means, you can edit it and add any functionality it doesn't have yet. The UserManager class has a protected property named Store which stores a reference to the UserStore class (or any subclass of it, depending on how you configured your ASP.NET Identity or if you use custom user store implementations, i.e. if you use different database engine like MySQL).

public class AplicationUserManager : UserManager<....> 
{
    public async Task<IdentityResult> ChangePasswordAsync(TKey userId, string newPassword) 
    {
        var store = this.Store as IUserPasswordStore;
        if(store==null) 
        {
            var errors = new string[] 
            { 
                "Current UserStore doesn't implement IUserPasswordStore"
            };

            return Task.FromResult<IdentityResult>(new IdentityResult(errors) { Succeeded = false });
        }

        if(PasswordValidator != null)
        {
            var passwordResult = await PasswordValidator.ValidateAsync(password);
            if(!password.Result.Success)
                return passwordResult;
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);

        await store.SetPasswordHashAsync(userId, newPasswordHash);
        return Task.FromResult<IdentityResult>(IdentityResult.Success);
    }
}

The UserManager is nothing else than a wrapper to the underlying UserStore. Check out IUserPasswordStore interface documentation at MSDN on available Methods.

Edit: The PasswordHasher is also a public property of the UserManager class, see interface definition here.

Edit 2: Since some people naively believe, you can't do password validation this way, I've updated it. The PasswordValidator property is also a property of UserManager and its as simple as adding 2 lines of code to add password validation too (which wasn't an requirement of the original question though).

Solution 2

EDIT: I know the OP requested an answer which performs the task in one transaction but I think the code is useful to people.

All the answers use the PasswordHasher directly which isn't a good idea as you will lose some baked in functionality (validation etc).

An alternative (and I would assume the recommended approach) is to create a password reset token and then use that to change the password. Example:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");

Solution 3

This method worked for me:

public async Task<IHttpActionResult> changePassword(UsercredentialsModel usermodel)
{
  ApplicationUser user = await AppUserManager.FindByIdAsync(usermodel.Id);
  if (user == null)
  {
    return NotFound();
  }
  user.PasswordHash = AppUserManager.PasswordHasher.HashPassword(usermodel.Password);
  var result = await AppUserManager.UpdateAsync(user);
  if (!result.Succeeded)
  {
    //throw exception......
  }
  return Ok();
}

Solution 4

In .net core 3.0

var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);

Solution 5

This is just a refinement on the answer provided by @Tseng. (I had to tweak it to get it to work).

public class AppUserManager : UserManager<AppUser, int>
{
    .
    // standard methods...
    .

    public async Task<IdentityResult> ChangePasswordAsync(AppUser user, string newPassword)
    {
        if (user == null)
            throw new ArgumentNullException(nameof(user));

        var store = this.Store as IUserPasswordStore<AppUser, int>;
        if (store == null)
        {
            var errors = new string[] { "Current UserStore doesn't implement IUserPasswordStore" };
            return IdentityResult.Failed(errors);
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
        await store.SetPasswordHashAsync(user, newPasswordHash);
        await store.UpdateAsync(user);
        return IdentityResult.Success;
    }
}

Note: this applies specifically to a modified setup that uses int as the primary keys for users and roles. I believe it would simply be a matter of removing the <AppUser, int> type args to get it to work with the default ASP.NET Identity setup.

Share:
112,393

Related videos on Youtube

Oleg Sh
Author by

Oleg Sh

Updated on October 14, 2021

Comments

  • Oleg Sh
    Oleg Sh over 2 years

    I need ability to change password for user by admin. So, admin should not enter a current password of user, he should have ability to set a new password. I look at ChangePasswordAsync method, but this method requires to enter old password. So, this method is not appropriate for this task. Therefore I have made it by the following way:

        [HttpPost]
        public async Task<ActionResult> ChangePassword(ViewModels.Admin.ChangePasswordViewModel model)
        {
            var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            var result = await userManager.RemovePasswordAsync(model.UserId);
            if (result.Succeeded)
            {
                result = await userManager.AddPasswordAsync(model.UserId, model.Password);
                if (result.Succeeded)
                {
                    return RedirectToAction("UserList");
                }
                else
                {
                    ModelState.AddModelError("", result.Errors.FirstOrDefault());
                }
            }
            else
            {
                ModelState.AddModelError("", result.Errors.FirstOrDefault());
            }
            return View(model);
        }
    

    it works, but theoretically we can receive error on AddPasswordAsync method. So, old password will be removed but new is not set. It's not good. Any way to do it in "one transaction"? PS. I seen ResetPasswordAsync method with reset token, seems, it's more safe (because can't be unstable situation with user) but in any case, it does by 2 actions.

    • Shaun Luttin
      Shaun Luttin about 9 years
      The crux of this question is doing it in one transaction. Would you be satisfied to do it in two transactions and to keep trying until the second one succeeds? If not, you might have to write your own implementation of changing a password.
  • Shaun Luttin
    Shaun Luttin about 9 years
    This is an interesting approach. I'm wondering what we could implement, so that the password is changed in one transaction, even though the admin doesn't know the original password.
  • Tseng
    Tseng about 9 years
    This approach is one transaction. You don't need an old password for it, as the newly implemented doesn't use any function of the old one and directly calls SetPasswordHashAsync from UserStore's implementation, if there is any. All you need to know is the userId and newPassword (which you can randomly generate)
  • Blackwood
    Blackwood over 8 years
    While this code may well work, a good answer should also explain how it works and why it is a good solution.
  • bryan c
    bryan c over 8 years
    default change password methods in UserManager class first try validating password and do security checks but i updated password value directly like an ordinary field that has no special constraint on it.
  • Dov Miller
    Dov Miller over 7 years
    What is UsercredentialsModel?
  • bryan c
    bryan c over 7 years
    UsercredentialsModel is a simple custom class as user model . in my case its something like this ' public class UsercredentialsModel { public string Id { get; set; } public string PhoneNumber { get; set; } public string VerificationCode { get; set; } public string Password { get; set; } }' @Dov Miller
  • shortstuffsushi
    shortstuffsushi almost 7 years
    Prefer answers with content in them rather than external links. If those links were ever to die for any reason, this answer would be entirely useless. In addition, you're answering a question that is over two years old with an accepted answer two years later without providing any additional value.
  • peter_the_oak
    peter_the_oak over 6 years
    This seems to be the proper way and opens a lot of new possibilities. Thank you!
  • Joe Lu
    Joe Lu almost 6 years
    I like this answer better
  • Tseng
    Tseng almost 6 years
    You can of course also use the PasswordValidator property which is defined in UserManager to validate it before setting it. But that wasn't a requirement of the original question
  • Zac Faragher
    Zac Faragher almost 5 years
    the problem with this is that if the new password fails validation, you've already removed the existing password. you could try and validate the password before removing the old password, but there's still a risk that adding the password fails, leaving a user with no password and possibly no way to login
  • Endri
    Endri almost 5 years
    I used this answer but password didn't update to database table unless I add await store.UpdateAsync(user, cancellation); after execution of SetPasswordHashAsync method. P.S. I have tweaked a little bit this and user is the object of class ApplicationUser: IdentityUser. Why does this happen?
  • Sabir Hossain
    Sabir Hossain almost 3 years
    though I feel like it's not the correct way to change password, but it helped me in different scenario where generating ResetToken and Validating the token wasn't possible.
  • gen
    gen over 2 years
    This seems to be the better way, thanks - wanted to still use the built in security checks and IdentityResult. seems iffy to update the value directly
  • BCA
    BCA over 2 years
    Excellent answer. One thing to note: it will throw an exception if you don't have a token provider registered. I had to add .AddDefaultTokenProviders() to the IdentityBuilder fluent chain in Program.cs (e.g. chained after builder.Services.AddIdentityCore)
  • Literate Corvette
    Literate Corvette about 2 years
    I believe this be user.Id as the parameter for GeneratePasswordResetTokenAsync()