Contraseña de cambio de identidad de ASP.NET

Necesito la capacidad de cambiar la contraseña del usuario por administrador. Por lo tanto, el administrador no debe ingresar una contraseña actual del usuario, debe tener la capacidad de establecer una nueva contraseña. Miro el método ChangePasswordAsync, pero este método requiere ingresar la contraseña anterior. Entonces, este método no es apropiado para esta tarea. Por lo tanto, lo hice de la siguiente manera:

[HttpPost] public async Task ChangePassword(ViewModels.Admin.ChangePasswordViewModel model) { var userManager = HttpContext.GetOwinContext().GetUserManager(); 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); } 

funciona, pero teóricamente podemos recibir un error en el método AddPasswordAsync. Por lo tanto, se eliminará la contraseña anterior, pero no se establece nueva. No es bueno. ¿Alguna forma de hacerlo en “una transacción”? PD. He visto el método ResetPasswordAsync con token de reinicio, parece que es más seguro (porque no puede haber una situación inestable con el usuario) pero, en cualquier caso, lo hace por 2 acciones.

ApplicationUserManager es la clase generada por la plantilla ASP.NET.

Lo que significa que puede editarlo y agregar cualquier funcionalidad que aún no tenga. La clase UserManager tiene una propiedad protegida llamada Store que almacena una referencia a la clase UserStore (o cualquier subclase de ella, dependiendo de cómo haya configurado su identidad ASP.NET o si usa implementaciones personalizadas de store de usuario, es decir, si usa un motor de base de datos diferente como MySQL).

 public class AplicationUserManager : UserManager<....> { public async Task 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(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.Success); } } 

El UserManager no es más que un contenedor para la UserStore subyacente. Consulte la documentación de la interfaz IUserPasswordStore en MSDN en Métodos disponibles.

Editar: PasswordHasher también es una propiedad pública de la clase UserManager ; consulte la definición de la interfaz aquí .

Edición 2: Como algunas personas creen ingenuamente , no se puede validar la contraseña de esta manera, la he actualizado. La propiedad PasswordHasher también es propiedad de UserManager y es tan simple como agregar 2 líneas de código para agregar validación de contraseña también (lo cual no era un requisito de la pregunta original).

Este método funcionó para mi:

 public async Task 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(); } 

EDITAR: Sé que OP solicitó una respuesta que realiza la tarea en una transacción, pero creo que el código es útil para las personas.

Todas las respuestas usan PasswordHasher directamente, lo cual no es una buena idea, ya que perderá algunas funciones cocidas (validación, etc.).

Una alternativa (y asumiría el enfoque recomendado) es crear un token de restablecimiento de contraseña y luego usarlo para cambiar la contraseña. Ejemplo:

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

Esto es solo un refinamiento en la respuesta proporcionada por @Tseng. (Tuve que modificarlo para que funcione).

 public class AppUserManager : UserManager { . // standard methods... . public async Task ChangePasswordAsync(AppUser user, string newPassword) { if (user == null) throw new ArgumentNullException(nameof(user)); var store = this.Store as IUserPasswordStore; 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; } } 

Nota: esto se aplica específicamente a una configuración modificada que usa int como las claves principales para usuarios y roles. Creo que simplemente sería cuestión de eliminar los tipo para que funcionen con la configuración predeterminada de identidad de ASP.NET.

 public async Task ChangePassword(ChangePwdViewModel usermodel) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var user = await _userManager.FindByIdAsync(userId); var result = await _userManager.ChangePasswordAsync(user, usermodel.oldPassword, usermodel.newPassword); if (!result.Succeeded) { //throw exception...... } return Ok(); } public class ChangePwdViewModel { [DataType(DataType.Password), Required(ErrorMessage ="Old Password Required")] public string oldPassword { get; set; } [DataType(DataType.Password), Required(ErrorMessage ="New Password Required")] public string newPassword { get; set; } } 

Nota: aquí UserId estoy recuperando del usuario registrado actual.

 public async Task ChangePassword(ResetPasswordViewModel CP) { ApplicationDbContext context = new ApplicationDbContext(); UserStore store = new UserStore(context); UserManager UserManager = new UserManager(store); var user = await UserManager.FindAsync(User.Identity.Name, CP.CurrentPassword); if (!UserManager.CheckPassword(user, CP.CurrentPassword)) { ViewBag.notification = "Incorrect password."; return View("~/Views/User/settings.cshtml"); } else { if (CP.Password != CP.ConfirmPassword) { ViewBag.notification = "try again"; return View("~/Views/User/settings.cshtml"); } else { String hashedNewPassword = UserManager.PasswordHasher.HashPassword(CP.Password); await store.SetPasswordHashAsync(user, hashedNewPassword); await store.UpdateAsync(user); ViewBag.notification = "successful"; return View("~/Views/User/settings.cshtml"); } } } 
  public async Task> AsyncModifyPassword(LoginDTO entity) { List errors = new List(); ApplicationUser user = await _userManager.FindByEmailAsync(entity.Email); if (user == null) { errors.Add("User Not Found"); //todo, hablar sobre el tema de lanzar las excepciones return errors; } //user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, entity.Password); IdentityResult result = await _userManager.ChangePasswordAsync(user, entity.Password , entity.NewPassword); if (!result.Succeeded) errors = result.Errors.ToList().Select(error => error.Description).ToList(); return errors; } 

Sí, estás en lo correcto. ResetPassword a través de token es un enfoque preferido. Hace algún tiempo, creé un contenedor completo sobre .NET Identity y el código se puede encontrar aquí . Puede ser útil para ti. También puedes encontrar nuget aquí . También expliqué la biblioteca en un blog aquí . Este contenedor es fácilmente consumible como nuget y crea todas las configuraciones requeridas durante la instalación.