Retrieving Confirmation Token in SimpleMembership

A reader commented on my blog "Adding Email Confirmation to SimpleMembership" that they would like the ability to resend the email confirmation to users that did not receive it for whatever reason.  This seemed like a reasonable request and it is actually asked a lot on various forums.  The problem is that WebMatrix.WebData.WebSecurity does not provide a method to retrieve the confirmation token so that you can resend the email.  The token is only provided when you call CreateUserAndAccount and set requireConfirmationToken to true.  It turns out the only way to get the confirmation token in SimpleMembership is to directly query the webpages_Membership table.  I have encapsulated this functionality in the open source project called SimpleSecurity.

SimpleSecurity encapsulates WebSecurity and adds missing features like getting the confirmation token. It also decouples the security model from the ASP.NET MVC framework.  Now you can just call SimpleSecurity.WebSecurity.GetConfirmationToken with the user name as a parameter and it will return the confirmation token.  I have added an example of how to resend the registration email in the reference application in this project which is called SeedSimple.

In the example I use the login page to allow the user to resend the email.  When the user logs in, if they fail authentication then I check if the user is in the system and if they have not completed the confirmation process. If they are not confirmed I provide them with a message informing them to look for the email to complete the registration process or to resend the email.  Here is what that page looks like.


Here are the modifications to the Login action to make this work.

       public ActionResult Login(LoginModel model, string returnUrl)
        {
            string errorMsg = "The user name or password provided is incorrect.";

            if (model.IsConfirmed)
            {
                if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
                {
                    return RedirectToLocal(returnUrl);
                }
                else if (WebSecurity.FoundUser(model.UserName) && !WebSecurity.IsConfirmed(model.UserName))
                {
                    model.IsConfirmed = false;
                    errorMsg = "You have not completed the registration process. To complete this process look for the email that provides instructions or press the button to resend the email.";
                }

            }
            else //Need to resend confirmation email
            {
                ResendConfirmationEmail(model.UserName);
                errorMsg = "The registration email has been resent. Find the email and follow the instructions to complete the registration process.";
                model.IsConfirmed = true;
            }

            // If we got this far, something failed, redisplay form
            ModelState.AddModelError("", errorMsg );
            return View(model);
        }

I modified the LoginModel to include a flag named IsConfirmed which lets me manage the state of the view that is displayed and whether I should try to authenticate the user or resend the email confirmation. The method to resend the email confirmation look like this.

        private void ResendConfirmationEmail(string username)
        {
            string email = WebSecurity.GetEmail(username);
            string token = WebSecurity.GetConfirmationToken(username);
            SendEmailConfirmation(email, username, token);
        }

There were also some changes to the Login view to make this work.  You can get the source code for this project here.  And if you have any suggestion on how to improve SimpleSecurity you can post them here or just comment on this blog.




Comments

Popular posts from this blog

Using Claims in ASP.NET Identity

Seeding & Customizing ASP.NET MVC SimpleMembership

Customizing Claims for Authorization in ASP.NET Core 2.0