Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement password reset with ASP.NET Identity for ASP.NET MVC 5.0?

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?

like image 488
Gábor Plesz Avatar asked Sep 25 '13 10:09

Gábor Plesz


2 Answers

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.

like image 99
Gábor Plesz Avatar answered Sep 25 '22 13:09

Gábor Plesz


Seems like a lot of trouble... What advantage does the above give over:

  1. the user clicking a 'Recover Account' link
  2. this sends an 64 byte encoded string of a datetime ticks value (call it psuedo-hash) in an email
  3. click the link back in the email to a controller/action route that
  4. matches email and it's source server to psuedo-hash, decrypts the psuedo-hash, validates the time since sent and
  5. offers a View for the user to set a new password
  6. with a valid password, the code removes the old user password and assigns the new.
  7. Once complete, successful or not, delete the psuedo-hash.

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.

like image 45
Robert Achmann Avatar answered Sep 26 '22 13:09

Robert Achmann