Search code examples
c#asp.netasp.net-corehttp-post

How to change user's Email in ASP.NET core only after he confirmed his email


I am making a panel where the website admins can access users' account data and change stuff. For example, name, email, username, etc... I want when the admin changes the email on his computer it will not change the email in the database unless the user confirms it in his new email. I have the registration functions and I kind of used them but with them, I can't change the Email After it clicked, only before. Here's the code, hope you can help me find a way to do this.

    [HttpPost]
    public async Task<IActionResult> EditUser(EditUserModel model, string returnUrl = null)
    {
        var user = await userManager.FindByIdAsync(model.Id);

        var _user = user;

        _user.UserName = model.UserName;
        _user.EmailConfirmed = false;
        _user.FirstName = model.FirstName;
        _user.LastName = model.LastName;

        if (user == null)
        {
            ViewBag.ErrorMessage = $"User with Id: {model.Id} cannot be found";
            return View("NotFound");
        }
        else
        {
            if (user.Email != model.Email)
            {

                returnUrl ??= Url.Content("~/");

                var result = await userManager.UpdateAsync(_user);

                if (result.Succeeded)
                {
                    var code = await userManager.GenerateEmailConfirmationTokenAsync(_user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(model.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    /*
                    if (NewEmailConfirmed) --> the if statement that supposes to check if the user confirmed his new email
                    {
                        _user.Email = model.Email;
                        await userManager.UpdateAsync(_user);
                    }
                    */
                }

              
            }
            else //If the admin updated anything but the Email.
            {
                user.UserName = model.UserName;
                user.FirstName = model.FirstName;
                user.LastName = model.LastName;

                var result = await userManager.UpdateAsync(user);

                if (result.Succeeded)
                    return RedirectToAction("ListUsers");

                foreach (var error in result.Errors)
                    ModelState.AddModelError("", error.Description);

                return View(model);
            }

        }
        return RedirectToAction("ListUsers");
    }

Solution

  • So, After some research, I found that in the "Add new scaffold item" you can add the checkbox called "Account\Manage\Email". After I added it, when I clicked on my username it opened some user settings and I could change My email. So I took the code from there and after some debugging I found the code below to work 100%.

    Controller

    private UserManager<ApplicationUser> userManager;
    private readonly IEmailSender _emailSender;
    private readonly ILogger logger;
    
    public AdministrationController(UserManager<ApplicationUser> userManager,
                                        IEmailSender emailSender,
                                        ILogger<AdministrationController> logger)
        {
            this.userManager = userManager;
            _emailSender = emailSender;
            this.logger = logger;
        }
    
        public string Username { get; set; }
    
        public string Email { get; set; }
    
        public bool IsEmailConfirmed { get; set; }
    
        [TempData]
        public string StatusMessage { get; set; }
    
        [BindProperty]
        public InputModel Input { get; set; }
    
        public class InputModel
        {
            [EmailAddress]
            [Display(Name = "New email")]
            public string NewEmail { get; set; }
        }
    
        private async Task LoadAsync(ApplicationUser user)
        {
            var email = await userManager.GetEmailAsync(user);
            Email = email;
    
            Input = new InputModel
            {
                NewEmail = email,
            };
    
            IsEmailConfirmed = await userManager.IsEmailConfirmedAsync(user);
        }
    
        public async Task<IActionResult> OnGetAsync()
        {
            var user = await userManager.GetUserAsync(User);
            if (user == null)
            {
                return NotFound($"Unable to load user with ID '{userManager.GetUserId(User)}'.");
            }
    
            await LoadAsync(user);
            return RedirectToAction("ListUsers", "Administration");
        }
    
        [HttpPost]
        public async Task<IActionResult> ChangeEmail(EditUserModel model)
        {
            var user = await userManager.FindByIdAsync(model.Id);
            if (user == null)
            {
                return NotFound($"Unable to load user with ID '{userManager.GetUserId(User)}'.");
            }
    
            //if (!ModelState.IsValid)
            //{
            //    await LoadAsync(user);
            //    return RedirectToAction("ListUsers", "Administration");
            //}
    
            var email = await userManager.GetEmailAsync(user);
            if (Input.NewEmail != email && Input.NewEmail != null)
            {
                var userId = await userManager.GetUserIdAsync(user);
                var code = await userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmailChange",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, email = Input.NewEmail, code = code },
                    protocol: Request.Scheme);
                await _emailSender.SendEmailAsync(
                    Input.NewEmail,
                    "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
    
                StatusMessage = "Confirmation link to change email sent. Please check your email.";
                return RedirectToAction("ListUsers", "Administration");
            }
    
            StatusMessage = "Your email is unchanged.";
            return View(model);
        }
    
        [HttpPost]
        public async Task<IActionResult> EditUser(EditUserModel model) 
        {
            var user = await userManager.FindByIdAsync(model.Id);
    
            if (user == null)
                return NotFound($"User with Id: {model.Id} cannot be found.");
    
            user.UserName = model.UserName;
            user.FirstName = model.FirstName;
            user.LastName = model.LastName;
    
            var result = await userManager.UpdateAsync(user);
    
            if (result.Succeeded)
            {
                if (user.Email != Input.NewEmail)
                    await ChangeEmail(model);
                return RedirectToAction("ListUsers");
            }
    
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError("", error.Description);
            }
    
            return View(model);
        }
    

    Model

    public class EditUserModel
    {
        [BindProperty]
        public InputModel Input { get; set; }
    
        public class InputModel
        {
            [EmailAddress]
            [Display(Name = "New email")]
            public string NewEmail { get; set; }
        }
    }
    

    View

    @model SvivaTeamVersion3.Models.EditUserModel
    <form method="post">
        <div class="form-group row"> @*Display the current Email*@
            <label asp-for="Email" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Email" disabled class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
        </div>
    
        <div class="form-group row"> @*Change to a new Email*@
            <label asp-for="Input.NewEmail" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Input.NewEmail" id="newMail" class="form-control" />
            </div>
        </div>
    
     <div asp-validation-summary="All" class="text-danger"></div>
    
    </form>
    
    @section Scripts {
        <partial name="_ValidationScriptsPartial" />
    }
    

    After all

    If you have done everything correctly you should have an input box to change the Email. When you change your Email a new Email will be sent to you in your new mail. (only if you have done Email confirmation) If you would click on it the Email will change.