web-dev-qa-db-fra.com

Changement d'adresse email/nom d'utilisateur .NET

Est-ce que quelqu'un sait comment permettre à un utilisateur de changer de nom d'utilisateur/email avec identité ASP.NET avec confirmation par email? Il y a beaucoup d'exemples sur la façon de changer le mot de passe mais je ne trouve rien à ce sujet.

40
devlock

Mise à jour déc 2017 Quelques points positifs ont été soulevés dans les commentaires:

  • Il vaut mieux avoir un champ séparé pour les nouveaux courriels pendant la confirmation - dans les cas où l'utilisateur a saisi un courriel incorrect. Attendez que le nouvel email soit confirmé, puis faites-en l'email principal. Voir la réponse très détaillée de Chris_ ci-dessous.
  • Il pourrait également y avoir un cas où le compte avec cet email existe déjà - assurez-vous de vérifier cela aussi, sinon il pourrait y avoir un problème.

C'est une solution très basique qui ne couvre pas toutes les combinaisons possibles, alors utilisez votre jugement et assurez-vous de lire les commentaires - de très bons points ont été soulevés.

// get user object from the storage
var user = await userManager.FindByIdAsync(userId);

// change username and email
user.Username = "NewUsername";
user.Email = "[email protected]";

// Persiste the changes
await userManager.UpdateAsync(user);

// generage email confirmation code
var emailConfirmationCode = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);

// generate url for page where you can confirm the email
var callbackurl= "http://example.com/ConfirmEmail";

// append userId and confirmation code as parameters to the url
callbackurl += String.Format("?userId={0}&code={1}", user.Id, HttpUtility.UrlEncode(emailConfirmationCode));

var htmlContent = String.Format(
        @"Thank you for updating your email. Please confirm the email by clicking this link: 
        <br><a href='{0}'>Confirm new email</a>",
        callbackurl);

// send email to the user with the confirmation link
await userManager.SendEmailAsync(user.Id, subject: "Email confirmation", body: htmlContent);



// then this is the action to confirm the email on the user
// link in the email should be pointing here
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    var confirmResult = await userManager.ConfirmEmailAsync(userId, code);

    return RedirectToAction("Index");
}
42
trailmax

Trailmax a eu raison en grande partie, mais comme le soulignaient les commentaires, l’utilisateur serait essentiellement bloqué s’il décidait de gâcher sa nouvelle adresse e-mail lors de la mise à jour.

Pour résoudre ce problème, il est nécessaire d’ajouter des propriétés supplémentaires à votre classe d’utilisateurs et de modifier le login. (Remarque: cette réponse sera adressée via un projet MVC 5)

Voici où je l'ai pris:

1. Modifiez votre objet Utilisateur Commençons par mettre à jour l'utilisateur de l'application pour qu'il ajoute le champ supplémentaire dont nous avons besoin. Vous l'ajouterez dans le fichier IdentiyModel.cs de votre dossier Modèles:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    [MaxLength(256)]
    public string UnConfirmedEmail { get; set; }//this is what we add

}

Si vous voulez voir un exemple plus détaillé de cela, consultez ceci http://blog.falafel.com/customize-mvc-5-application-users-using-asp-net-identity-2 -0/ (c'est l'exemple que j'ai utilisé)

De plus, cela ne le mentionne pas dans l'article lié, mais vous voudrez également mettre à jour votre table AspNetUsers: 

ALTER TABLE dbo.AspNetUsers
ADD [UnConfirmedEmail] NVARCHAR(256) NULL;

2. Mettez à jour votre login

Maintenant, nous devons nous assurer que notre identifiant vérifie l'ancien e-mail de confirmation afin que tout soit en suspens pendant que nous attendons que l'utilisateur confirme ce nouvel e-mail:

   //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var allowPassOnEmailVerfication = false;
        var user = await UserManager.FindByEmailAsync(model.Email);
        if (user != null)
        {
            if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
            {
                allowPassOnEmailVerfication = true;
            }
        }


        // This now counts login failures towards account lockout
        // To enable password failures to trigger account lockout, I changed to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return allowPassOnEmailVerfication ? RedirectToLocal(returnUrl) : RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

C'est ça ... vous êtes essentiellement fait! Cependant, je suis toujours ennuyé par des demi-réponses qui ne vous font pas passer outre les pièges potentiels que vous rencontrerez plus tard, alors continuons notre aventure, d'accord?

3. Mettez à jour votre gestion/index

Dans notre index.cshtml, ajoutons une nouvelle section pour le courrier électronique. Avant d'y arriver, ajoutons le champ dont nous avons besoin dans ManageViewmodel.cs

public class IndexViewModel
{
    public bool HasPassword { get; set; }
    public IList<UserLoginInfo> Logins { get; set; }
    public string PhoneNumber { get; set; }
    public bool TwoFactor { get; set; }
    public bool BrowserRemembered { get; set; }

    public string ConfirmedEmail { get; set; } //add this
    public string UnConfirmedEmail { get; set; } //and this
}

Accédez à l'action d'index dans notre contrôleur de gestion pour l'ajouter à notre modèle de vue:

        var userId = User.Identity.GetUserId();
        var currentUser = await UserManager.FindByIdAsync(userId);

        var unConfirmedEmail = "";
        if (!String.IsNullOrWhiteSpace(currentUser.UnConfirmedEmail))
        {
            unConfirmedEmail = currentUser.UnConfirmedEmail;
        }
        var model = new IndexViewModel
        {
            HasPassword = HasPassword(),
            PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
            TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
            Logins = await UserManager.GetLoginsAsync(userId),
            BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId),
            ConfirmedEmail = currentUser.Email,
            UnConfirmedEmail = unConfirmedEmail
        };

Enfin, pour cette section, nous pouvons mettre à jour notre index pour nous permettre de gérer cette nouvelle option de courrier électronique:

<dt>Email:</dt>
    <dd>
        @Model.ConfirmedEmail
        @if (!String.IsNullOrWhiteSpace(Model.UnConfirmedEmail))
        {
            <em> - Unconfirmed: @Model.UnConfirmedEmail </em> @Html.ActionLink("Cancel", "CancelUnconfirmedEmail",new {email=Model.ConfirmedEmail})
        }
        else
        {
            @Html.ActionLink("Change Email", "ChangeEmail")
        }
    </dd>

4. Ajouter ces nouvelles modifications

Premièrement, ajoutons ChangeEmail:

Voir le modèle:

public class ChangeEmailViewModel
{
    public string ConfirmedEmail { get; set; } 
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
    public string UnConfirmedEmail { get; set; } 
}

Obtenez de l'action:

 public ActionResult ChangeEmail()
    {
        var user = UserManager.FindById(User.Identity.GetUserId());
        var model = new ChangeEmailViewModel()
        {
            ConfirmedEmail = user.Email
        };

        return View(model);
    }

Vue:

@model ProjectName.Models.ChangeEmailViewModel
@{
ViewBag.Title = "Change Email";
}

<h2>@ViewBag.Title.</h2>

@using (Html.BeginForm("ChangeEmail", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>New Email Address:</h4>
    <hr />
    @Html.ValidationSummary("", new { @class = "text-danger" })
    @Html.HiddenFor(m=>m.ConfirmedEmail)
    <div class="form-group">
        @Html.LabelFor(m => m.UnConfirmedEmail, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.UnConfirmedEmail, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Email Link" />
        </div>
    </div>
}

Action HttpPost: 

    [HttpPost]
    public async Task<ActionResult> ChangeEmail(ChangeEmailViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return RedirectToAction("ChangeEmail", "Manage");
        }

        var user = await UserManager.FindByEmailAsync(model.ConfirmedEmail);
        var userId = user.Id;
        if (user != null)
        {
            //doing a quick swap so we can send the appropriate confirmation email
            user.UnConfirmedEmail = user.Email;
            user.Email = model.UnConfirmedEmail;
            user.EmailConfirmed = false;
            var result = await UserManager.UpdateAsync(user);

            if (result.Succeeded)
            {

                string callbackUrl =
                await SendEmailConfirmationTokenAsync(userId, "Confirm your new email");

                var tempUnconfirmed = user.Email;
                user.Email = user.UnConfirmedEmail;
                user.UnConfirmedEmail = tempUnconfirmed;
                result = await UserManager.UpdateAsync(user);

                callbackUrl = await SendEmailConfirmationWarningAsync(userId, "You email has been updated to: "+user.UnConfirmedEmail);


            }
        }
        return RedirectToAction("Index","Manage");
    }

Maintenant, ajoutez cet avertissement:

    private async Task<string> SendEmailConfirmationWarningAsync(string userID, string subject)
    {
        string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
        var callbackUrl = Url.Action("ConfirmEmail", "Account",
           new { userId = userID, code = code }, protocol: Request.Url.Scheme);
        await UserManager.SendEmailAsync(userID, subject,
           "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

        return callbackUrl;
    }

Et maintenant, enfin, nous pouvons ajouter l’annulation de la nouvelle adresse e-mail:

    public async Task<ActionResult> CancelUnconfirmedEmail(string emailOrUserId)
    {
        var user = await UserManager.FindByEmailAsync(emailOrUserId);
        if (user == null)
        {
            user = await UserManager.FindByIdAsync(emailOrUserId);
            if (user != null)
            {
                user.UnConfirmedEmail = "";
                user.EmailConfirmed = true;
                var result = await UserManager.UpdateAsync(user);
            }
        }
        else
        {
            user.UnConfirmedEmail = "";
            user.EmailConfirmed = true;
            var result = await UserManager.UpdateAsync(user);
        }
        return RedirectToAction("Index", "Manage");

    }

5. Mettre à jour ConfirmEmail (la toute dernière étape)

Après tout cela, nous pouvons maintenant confirmer le nouvel email, ce qui signifie que nous devrions supprimer l'ancien email en même temps.

 var result = UserManager.ConfirmEmail(userId, code);
 if (result.Succeeded)
 {

     var user = UserManager.FindById(userId);
     if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
     {
         user.Email = user.UnConfirmedEmail;
         user.UserName = user.UnConfirmedEmail;
         user.UnConfirmedEmail = "";

         UserManager.Update(user);
     }
 }
35
Chris_

Vous n'avez pas encore consulté ChangeEmailOnIdentity2.0ASPNET , mais ne pouvez-vous pas simplement profiter du fait que les valeurs UserName et Email correspondent généralement? Cela vous permet de changer la colonne Email sur demande et ensuite UserName lors de la confirmation. 

Ces deux contrôleurs semblent fonctionner pour moi:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ChangeUserName(LoginViewModel model)
    {
        IdentityResult result = new IdentityResult();
        try
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

                SignInStatus verify = await SignInManager.PasswordSignInAsync(user.UserName, model.Password, false, false);

                if (verify != SignInStatus.Success)
                {
                    ModelState.AddModelError("Password", "Incorrect password.");
                }
                else
                {
                    if (model.Email != user.Email)
                    {
                        user.Email = model.Email;
                        user.EmailConfirmed = false;

                        // Persist the changes
                        result = await UserManager.UpdateAsync(user);

                        if (result.Succeeded)
                        {
                            string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                            var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, protocol: Request.Url.Scheme);
                            await UserManager.SendEmailAsync(user.Id, "Confirm your updated email", "Please confirm your email address by clicking <a href=\"" + callbackUrl + "\">this</a>");

                            return RedirectToAction("Index", new { Message = ManageMessageId.ChangeUserNamePending });
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("Email", "Address specified matches current setting.");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            result.Errors.Append(ex.Message);
        }
        AddErrors(result);
        return View(model);
    }

    [AllowAnonymous]
    public async Task<ActionResult> ConfirmEmail(string userId, string code)
    {
        if (userId == null || code == null)
        {
            return View("Error");
        }
        var result = await UserManager.ConfirmEmailAsync(userId, code);

        if (result.Succeeded)
        {
            var user = await UserManager.FindByIdAsync(userId);
            if (user.Email != user.UserName)
            {
                // Set the message to the current values before changing
                String message = $"Your email user name has been changed from {user.UserName} to {user.Email} now.";

                user.UserName = user.Email;
                result = await UserManager.UpdateAsync(user);
                if (result.Succeeded)
                {
                    ViewBag.Message = message;

                    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                }
                else
                {
                    result.Errors.Append("Could not modify your user name.");
                    AddErrors(result);

                    return View("Error");
                }
            }
            return View("ConfirmEmail");
        }
        else
        {
            return View("Error");
        }
    }
1
Doug Dekker

Si quelqu'un cherche une solution avec Asp.Net Core: Ici, les choses sont beaucoup plus simples, voir l'article sur SO AspNet Core Générer et modifier l'adresse e-mail

0
axuno

J'ai suivi les étapes de Jonathan dans un tout nouveau projet ASP.NET pour tester les modifications et a fonctionné à merveille. Ceci est le lien vers le répertoire repository

0
Santiago Rebella