Identityserver4.admin: how to RequireConfirmedEmail in skoruba?

Created on 28 Apr 2020  路  6Comments  路  Source: skoruba/IdentityServer4.Admin

I want force identity server check email confirmation before user login
services.AddIdentity(options => { options.User.RequireUniqueEmail = true; })
thanks skoruba

task

Most helpful comment

You can require the newly registered user to confirm email before he is able to log in. This can be achieved by modifying the Register POST method of the AccountController.cs in the STS.Identity project as follows:

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");

            ViewData["ReturnUrl"] = returnUrl;

            if (!ModelState.IsValid) return View(model);

            var user = new TUser
            {
                UserName = model.UserName,
                Email = model.Email,
            };

            RegisterWithoutUsernameViewModel model1 = new RegisterWithoutUsernameViewModel();
            var user1 = _userManager.FindByEmailAsync(model.Email);
            if (user1.Result != null)
            {
                ViewData["UserRegistered"] = "This email registered already.";
                return View(model1);
            }

            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                // add new by me on 03-11-2020:
                await _userManager.AddToRoleAsync(user, "CommunityRole");                
                await _userManager.AddClaimAsync(user, new Claim("num_ginckers", "10"));
                await _userManager.AddClaimAsync(user, new Claim("register_date", DateTime.Now.ToString("yyyy-MM-dd")));

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, HttpContext.Request.Scheme);

                await _emailSender.SendEmailAsync(model.Email, _localizer["ConfirmEmailTitle"], _localizer["ConfirmEmailBody", HtmlEncoder.Default.Encode(callbackUrl)]);
                //await _signInManager.SignInAsync(user, isPersistent: false);

                //return RedirectToLocal(returnUrl);
                return View("CheckEmail");
            }

            AddErrors(result);

            // If we got this far, something failed, redisplay form            
            if(_loginConfiguration.ResolutionPolicy == LoginResolutionPolicy.Email)
            {
                return View(model1);
            }
            return View(model);
        }

Here, the CheckEmail view can be very simple. Here is a sample CheckEmail.cshtml file:

@{
    ViewData["Title"] = "Check Email";
}

<div class="p-2">
    <h2>Thank You for Registration</h2><br />
    <p>
       A confirmation email is sent to you. Please check your email and click on the link in the email to complete your registration.
    </p>
</div>

Hope this helps.

Best, Jack

All 6 comments

You can require the newly registered user to confirm email before he is able to log in. This can be achieved by modifying the Register POST method of the AccountController.cs in the STS.Identity project as follows:

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");

            ViewData["ReturnUrl"] = returnUrl;

            if (!ModelState.IsValid) return View(model);

            var user = new TUser
            {
                UserName = model.UserName,
                Email = model.Email,
            };

            RegisterWithoutUsernameViewModel model1 = new RegisterWithoutUsernameViewModel();
            var user1 = _userManager.FindByEmailAsync(model.Email);
            if (user1.Result != null)
            {
                ViewData["UserRegistered"] = "This email registered already.";
                return View(model1);
            }

            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                // add new by me on 03-11-2020:
                await _userManager.AddToRoleAsync(user, "CommunityRole");                
                await _userManager.AddClaimAsync(user, new Claim("num_ginckers", "10"));
                await _userManager.AddClaimAsync(user, new Claim("register_date", DateTime.Now.ToString("yyyy-MM-dd")));

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, HttpContext.Request.Scheme);

                await _emailSender.SendEmailAsync(model.Email, _localizer["ConfirmEmailTitle"], _localizer["ConfirmEmailBody", HtmlEncoder.Default.Encode(callbackUrl)]);
                //await _signInManager.SignInAsync(user, isPersistent: false);

                //return RedirectToLocal(returnUrl);
                return View("CheckEmail");
            }

            AddErrors(result);

            // If we got this far, something failed, redisplay form            
            if(_loginConfiguration.ResolutionPolicy == LoginResolutionPolicy.Email)
            {
                return View(model1);
            }
            return View(model);
        }

Here, the CheckEmail view can be very simple. Here is a sample CheckEmail.cshtml file:

@{
    ViewData["Title"] = "Check Email";
}

<div class="p-2">
    <h2>Thank You for Registration</h2><br />
    <p>
       A confirmation email is sent to you. Please check your email and click on the link in the email to complete your registration.
    </p>
</div>

Hope this helps.

Best, Jack

Hi guys, thanks for your tips - I will check it and add it into solution.

@skoruba, I did a small change to @jack1232 solution above to allow for either require email confirmation or not, please check.

            await _emailSender.SendEmailAsync(model.Email, _localizer["ConfirmEmailTitle"], _localizer["ConfirmEmailBody", HtmlEncoder.Default.Encode(callbackUrl)]);

            if (_signInManager.Options.SignIn.RequireConfirmedEmail)
            {
                return View("CheckEmail");
            }

            await _signInManager.SignInAsync(user, isPersistent: false);
            return RedirectToLocal(returnUrl);

Regards, Martinus

I've created PR #627 incorporating some of @jack1232 code.

Merged in master, thanks for your help. 馃憤馃徏

Was this page helpful?
0 / 5 - 0 ratings