Microsoft is coming up with a new Membership system called ASP.NET Identity (also the default in ASP.NET MVC 5). I found the sample project, but this is not implemented a password reset.
On password reset topic just found this Article: Implementing User Confirmation and Password Reset with One ASP.NET Identity – Pain or Pleasure, not help for me, because do not use the built-in password recovery.
As I was looking at the options, as I think we need to generate a reset token, which I will send to the user. The user can set then the new password using the token, overwriting the old one.
I found the IdentityManager.Passwords.GenerateResetPasswordToken
/ IdentityManager.Passwords.GenerateResetPasswordTokenAsync(string tokenId, string userName, validUntilUtc)
, but I could not figure out what it might mean the tokenId
parameter.
How do I implement the Password Reset in ASP.NET with MVC 5.0?
I get it: The tokenid is a freely chosen identity, which identifies a password option. For example,
1. looks like the password recovery process, step 1 (it is based on: https://stackoverflow.com/a/698879/208922)
[HttpPost] [ValidateAntiForgeryToken] [AllowAnonymous] //[RecaptchaControlMvc.CaptchaValidator] public virtual async Task<ActionResult> ResetPassword( ResetPasswordViewModel rpvm) { string message = null; //the token is valid for one day var until = DateTime.Now.AddDays(1); //We find the user, as the token can not generate the e-mail address, //but the name should be. var db = new Context(); var user = db.Users.SingleOrDefault(x=>x.Email == rpvm.Email); var token = new StringBuilder(); //Prepare a 10-character random text using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider()) { var data = new byte[4]; for (int i = 0; i < 10; i++) { //filled with an array of random numbers rngCsp.GetBytes(data); //this is converted into a character from A to Z var randomchar = Convert.ToChar( //produce a random number //between 0 and 25 BitConverter.ToUInt32(data, 0) % 26 //Convert.ToInt32('A')==65 + 65 ); token.Append(randomchar); } } //This will be the password change identifier //that the user will be sent out var tokenid = token.ToString(); if (null!=user) { //Generating a token var result = await IdentityManager .Passwords .GenerateResetPasswordTokenAsync( tokenid, user.UserName, until ); if (result.Success) { //send the email ... } } message = "We have sent a password reset request if the email is verified."; return RedirectToAction( MVC.Account.ResetPasswordWithToken( token: string.Empty, message: message ) ); }
2 And then when the user enters the token and the new password:
[HttpPost] [ValidateAntiForgeryToken] [AllowAnonymous] //[RecaptchaControlMvc.CaptchaValidator] public virtual async Task<ActionResult> ResetPasswordWithToken( ResetPasswordWithTokenViewModel rpwtvm ) { if (ModelState.IsValid) { string message = null; //reset the password var result = await IdentityManager.Passwords.ResetPasswordAsync( rpwtvm.Token, rpwtvm.Password ); if (result.Success) { message = "the password has been reset."; return RedirectToAction( MVC.Account.ResetPasswordCompleted(message: message) ); } else { AddErrors(result); } } return View(MVC.Account.ResetPasswordWithToken(rpwtvm)); }
Skeleton proposal to sample project on github, if anyone needs it may be tested.The E-mail sending not yet written, possibly with the addition soon.
Seems like a lot of trouble... What advantage does the above give over:
With this flow, at no time do you EVER send a password out of your domain.
Please, anyone, prove to me how this is any less secure.
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