web-dev-qa-db-fra.com

Comment implémenter la réinitialisation du mot de passe avec ASP.NET Identity pour ASP.NET MVC 5.0?

Microsoft propose un nouveau système d'adhésion appelé ASP.NET Identity (également la valeur par défaut dans ASP.NET MVC 5). J'ai trouvé le exemple de projet , mais ce n'est pas implémenté une réinitialisation de mot de passe.

Le sujet de la réinitialisation du mot de passe vient de trouver cet article: Implémentation de la confirmation de l'utilisateur et de la réinitialisation du mot de passe avec une identité ASP.NET - Douleur ou plaisir , ne m'aide pas, car n'utilisez pas la récupération de mot de passe intégrée.

Comme je regardais les options, je pense que nous devons générer un jeton de réinitialisation, que j'enverrai à l'utilisateur. L'utilisateur peut ensuite définir le nouveau mot de passe à l'aide du jeton, en remplaçant l'ancien.

J'ai trouvé la fonction IdentityManager.Passwords.GenerateResetPasswordToken/IdentityManager.Passwords.GenerateResetPasswordTokenAsync(string tokenId, string userName, validUntilUtc), mais je n'ai pas pu comprendre ce que cela pouvait signifier le paramètre tokenId.

Comment implémenter la réinitialisation de mot de passe dans ASP.NET avec MVC 5.0?

28
Gábor Plesz

Je comprends: le tokenid est une identité librement choisie, qui identifie une option de mot de passe. Par exemple,

1. ressemble au processus de récupération de mot de passe, étape 1 (il est basé sur: 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 Et puis lorsque l'utilisateur entre le token et le nouveau mot de passe:

[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));
}

Proposition de squelette à exemple de projet sur github, si quelqu'un en a besoin, il peut être testé. L'envoi d'e-mail n'est pas encore écrit, éventuellement avec l'ajout prochainement.

8
Gábor Plesz

On dirait beaucoup de problèmes ... Quel avantage donne ce qui précède:

  1. l'utilisateur cliquant sur un lien "Récupérer le compte"
  2. cela envoie une chaîne codée de 64 octets d'une valeur de ticks datetime (appelez-la psuedo-hash) dans un e-mail
  3. cliquez sur le lien dans l'e-mail vers une route de contrôleur/action qui
  4. associe le courrier électronique et son serveur source à psuedo-hash, déchiffre le psuedo-hash, valide le temps écoulé depuis l'envoi et
  5. offre une vue pour l'utilisateur pour définir un nouveau mot de passe
  6. avec un mot de passe valide, le code supprime l'ancien mot de passe utilisateur et attribue le nouveau.
  7. Une fois terminé, réussi ou non, supprimez le pseudo-hachage.

Avec ce flux, vous n'envoyez JAMAIS de mot de passe de votre domaine.

S'il vous plaît, n'importe qui, prouvez-moi comment cela est moins sûr.

5
Robert Achmann