Search code examples
c#asp.net-mvcasp.net-identity

Prevent login when EmailConfirmed is false


The newest ASP.NET identity bits (2.0 beta) include the foundation for confirming user email addresses. The NuGet package "Microsoft Asp.Net Identity Samples" contains a sample showing this flow. But in that sample, even when EmailConfirmed = false, there is no different behavior in the user experience.

How can I prevent users from being able to login when their email address is not yet confirmed? I understand that I can have the users log in regardless and then perform the check on the EmailConfirmed field, but it seems like it would be much more efficient if I could prevent the user from successfully logging in at all when EmailConfirmed == false


Solution

  • You need to add a few lines to the Login action (POST method) to verify that the user has confirmed their email. The method you want to check is UserManager.IsEmailConfirmed. Here is what your Login action will look like.

        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByNameAsync(model.Email);
                if (user == null)
                {
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
                }
                //Add this to check if the email was confirmed.
                if (!await UserManager.IsEmailConfirmedAsync(user.Id))
                {
                    ModelState.AddModelError("", "You need to confirm your email.");
                    return View(model);
                }
                if (await UserManager.IsLockedOutAsync(user.Id))
                {
                    return View("Lockout");
                }
                if (await UserManager.CheckPasswordAsync(user, model.Password))
                {
                    // Uncomment to enable lockout when password login fails
                    //await UserManager.ResetAccessFailedCountAsync(user.Id);
                    return await LoginCommon(user, model.RememberMe, returnUrl);
                }
                else
                {
                    // Uncomment to enable lockout when password login fails
                    //await UserManager.AccessFailedAsync(user.Id);
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
                }
            }
    
            // If we got this far, something failed, redisplay form
            return View(model);
        }
    

    In this example I just return the user to the login view and display an error message. You could return them to another view that provides more details on the next steps to confirm their email, or even give them the option to resend the email confirmation.