Create a secure ASP.NET Web Forms app with user registration, email confirmation and password reset (C#)

by Erik Reitan

This tutorial shows you how to build an ASP.NET Web Forms app with user registration, e-mail confirmation and password reset using the ASP.NET Identity membership system. This tutorial was based on Rick Anderson's MVC tutorial.

Introduction

This tutorial guides you through the steps required to create an ASP.NET Spider web Forms awarding using Visual Studio and ASP.Cyberspace iv.5 to create a secure Web Forms app with user registration, email confirmation and password reset.

Tutorial Tasks and Information:

  • Create an ASP.NET Web Forms app
  • Hook Upward SendGrid
  • Crave E-mail Confirmation Earlier Log In
  • Password Recovery and Reset
  • Resend Electronic mail Confirmation Link
  • Troubleshooting the App
  • Additional Resources

Create an ASP.Cyberspace Web Forms App

Commencement past installing and running Visual Studio Limited 2013 for Web or Visual Studio 2013. Install Visual Studio 2013 Update 3 or higher every bit well.

  1. Create a new project (File -> New Project) and select the ASP.Net Web Application template and the latest .NET Framework version from the New Projection dialog box.

  2. From the New ASP.Cyberspace Project dialog box, select the Spider web Forms template. Leave the default hallmark equally Individual User Accounts. If you'd like to host the app in Azure, leave the Host in the cloud check box checked.
    So, click OK to create the new project.
    New ASP.NET Project dialog box

  3. Enable Secure Sockets Layer (SSL) for the project. Follow the steps bachelor in the Enable SSL for the Project department of the Getting Started with Web Forms tutorial series.

  4. Run the app, click the Annals link and register a new user. At this point, the only validation on the email is based on the [EmailAddress] attribute to ensure the email address is well-formed. You volition modify the code to add together email confirmation. Close the browser window.

  5. In Server Explorer of Visual Studio (View -> Server Explorer), navigate to Data Connections\DefaultConnection\Tables\AspNetUsers, right click and select Open table definition.

    The following image shows the AspNetUsers table schema:

    AspNetUsers table schema

  6. In Server Explorer, right-click on the AspNetUsers table and select Show Table Data.

    AspNetUsers table data
    At this indicate the email for the registered user has not been confirmed.

  7. Click on the row and select delete to delete the user. You'll add this electronic mail once more in the next step and transport a confirmation message to the e-mail address.

E-mail Confirmation

It's a best practice to confirm the e-mail during the registration of a new user to verify they are not impersonating someone else (that is, they haven't registered with someone else's e-mail). Suppose you lot had a give-and-take forum, you lot would want to foreclose "bob@cpandl.com" from registering as "joe@contoso.com". Without email confirmation, "joe@contoso.com" could get unwanted email from your app. Suppose Bob accidentally registered as "bib@cpandl.com" and hadn't noticed information technology, he wouldn't be able to use password recovery because the app doesn't have his right e-mail. Electronic mail confirmation provides but limited protection from bots and doesn't provide protection from determined spammers.

Yous generally desire to prevent new users from posting whatsoever data to your website before they have been confirmed by either email, an SMS text bulletin or another mechanism. In the sections below, we volition enable email confirmation and modify the code to prevent newly registered users from logging in until their e-mail has been confirmed. You lot'll use the e-mail service SendGrid in this tutorial.

Hook up SendGrid

SendGrid has inverse it'south API since this tutorial was written. For current SendGrid instructions, see SendGrid or Enable business relationship confirmation and password recovery.

Although this tutorial only shows how to add e-mail notification through SendGrid, you tin send e-mail using SMTP and other mechanisms (meet additional resources).

  1. In Visual Studio, open up the Package Manager Panel (Tools -> NuGet Package Manger -> Package Managing director Console), and enter the post-obit command:
    Install-Package SendGrid

  2. Become to the Azure SendGrid sign-upwardly folio and register for gratuitous SendGrid account. Y'all tin also sign-up for a free SendGrid business relationship directly on SendGrid's site.

  3. From Solution Explorer open the IdentityConfig.cs file in the App_Start folder and add the post-obit code highlighted in yellowish to the EmailService form to configure SendGrid:

                      public class EmailService : IIdentityMessageService {    public async Task SendAsync(IdentityMessage message)    {      expect configSendGridasync(message);    }     // Use NuGet to install SendGrid (Bones C# customer lib)     private async Task configSendGridasync(IdentityMessage message)    {       var myMessage = new SendGridMessage();       myMessage.AddTo(bulletin.Destination);       myMessage.From = new Organisation.Cyberspace.Mail.MailAddress(                           "Royce@contoso.com", "Royce Sellars (Contoso Admin)");       myMessage.Subject = message.Subject;       myMessage.Text = message.Body;       myMessage.Html = message.Trunk;        var credentials = new NetworkCredential(                  ConfigurationManager.AppSettings["emailServiceUserName"],                  ConfigurationManager.AppSettings["emailServicePassword"]                  );        // Create a Spider web transport for sending email.       var transportWeb = new Spider web(credentials);        // Transport the email.       if (transportWeb != nothing)       {          await transportWeb.DeliverAsync(myMessage);       }       else       {          Trace.TraceError("Failed to create Web transport.");          expect Task.FromResult(0);       }    } }                                  
  4. Too, add the following using statements to the beginning of the IdentityConfig.cs file:

                      using SendGrid; using System.Cyberspace; using System.Configuration; using System.Diagnostics;                                  
  5. To continue this sample simple, you'll store the email service business relationship values in the appSettings department of the web.config file. Add the following XML highlighted in yellow to the spider web.config file at the root of your projection:

                      </connectionStrings>    <appSettings>       <add key="emailServiceUserName" value="[EmailServiceAccountUserName]" />       <add key="emailServicePassword" value="[EmailServiceAccountPassword]" />    </appSettings>   <arrangement.web>                                  
  6. Add the electronic mail service values to reflect your SendGrid authentication values (User Name and Password) so that you can successful send email from your app. Exist sure to use your SendGrid account name rather than the e-mail address you provided SendGrid.

Enable Email Confirmation

To enable email confirmation, you'll modify the registration code using the post-obit steps.

  1. In the Account folder, open up the Register.aspx.cs code-behind and update the CreateUser_Click method to enable the following highlighted changes:

                      protected void CreateUser_Click(object sender, EventArgs e) {     var managing director = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();     var user = new ApplicationUser() { UserName = Email.Text, Email = Electronic mail.Text };     IdentityResult result = manager.Create(user, Password.Text);     if (result.Succeeded)     {         // For more than information on how to enable business relationship confirmation and password reset delight visit https://get.microsoft.com/fwlink/?LinkID=320771         string code = manager.GenerateEmailConfirmationToken(user.Id);         string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);         manager.SendEmail(user.Id, "Confirm your account", "Please ostend your account past clicking <a href=\"" + callbackUrl + "\">here</a>.");          IdentityHelper.SignIn(managing director, user, isPersistent: simulated);         IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);     }     else      {         ErrorMessage.Text = result.Errors.FirstOrDefault();     } }                                  
  2. In Solution Explorer, correct-click Default.aspx and select Gear up Every bit Start Page.

  3. Run the app by pressing F5. Later the page is displayed, click the Annals link to brandish the Register folio.

  4. Enter your email and password, so click the Register push to send an email message via SendGrid.
    The electric current state of your project and lawmaking will let the user to log in once they complete the registration form, even though they oasis't confirmed their account.

  5. Check your email account and click on the link to ostend your email.
    Once you submit the registration form, y'all will be logged in.
    Sample Website - Signed In

Require Email Confirmation Before Log In

Although you accept confirmed the email account, at this indicate you would not need to click on the link independent in the verification electronic mail to be fully signed-in. In the following section, you volition change the code requiring new users to have a confirmed email earlier they are logged in (authenticated).

  1. In Solution Explorer of Visual Studio, update the CreateUser_Click upshot in the Register.aspx.cs code-behind independent in the Accounts folder with the post-obit highlighted changes:

                      protected void CreateUser_Click(object sender, EventArgs e) {     var managing director = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();     var user = new ApplicationUser() { UserName = Email.Text, Email = E-mail.Text };     IdentityResult result = manager.Create(user, Password.Text);     if (result.Succeeded)     {         // For more information on how to enable account confirmation and password reset please visit https://get.microsoft.com/fwlink/?LinkID=320771         string code = director.GenerateEmailConfirmationToken(user.Id);         cord callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);         manager.SendEmail(user.Id, "Confirm your business relationship", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>.");          if (user.EmailConfirmed)         {           IdentityHelper.SignIn(manager, user, isPersistent: false);           IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);         }          else         {           ErrorMessage.Text = "An email has been sent to your account. Delight view the email and confirm your account to consummate the registration process.";         }     }     else      {         ErrorMessage.Text = upshot.Errors.FirstOrDefault();     } }                                  
  2. Update the LogIn method in the Login.aspx.cs code-behind with the following highlighted changes:

                      protected void LogIn(object sender, EventArgs eastward) {     if (IsValid)     {         // Validate the user password         var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();         var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();          // Crave the user to have a confirmed electronic mail before they can log on.         var user = manager.FindByName(Email.Text);         if (user != null)         {             if (!user.EmailConfirmed)             {                 FailureText.Text = "Invalid login endeavour. You must accept a confirmed email business relationship.";                 ErrorMessage.Visible = true;             }             else             {                 // This doen't count login failures towards account lockout                 // To enable password failures to trigger lockout, change to shouldLockout: true                 var consequence = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: simulated);                  switch (consequence)                 {                     example SignInStatus.Success:                         IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);                         break;                     case SignInStatus.LockedOut:                         Response.Redirect("/Account/Lockout");                         break;                     example SignInStatus.RequiresVerification:                         Response.Redirect(String.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={1}",                                                     Request.QueryString["ReturnUrl"],                                                     RememberMe.Checked),                                       truthful);                         suspension;                     case SignInStatus.Failure:                     default:                         FailureText.Text = "Invalid login effort";                         ErrorMessage.Visible = true;                         break;                 }             }         }     }           }                                  

Run the Application

At present that you accept implemented the code to bank check whether a user's email address has been confirmed, you tin check the functionality on both the Register and Login pages.

  1. Delete any accounts in the AspNetUsers table that contain the email allonym you wish to test.
  2. Run the app (F5) and verify y'all cannot register every bit a user until yous take confirmed your electronic mail accost.
  3. Earlier confirming your new account via the email that was just sent, attempt to log in with the new account.
    Y'all'll run into that you are unable to log in and that you must have a confirmed email account.
  4. In one case you confirm your email address, log in to the app.

Password Recovery and Reset

  1. In Visual Studio, remove the annotate characters from the Forgot method in the Forgot.aspx.cs code-behind contained in the Business relationship binder, and so that the method appears as follows:

                      protected void Forgot(object sender, EventArgs e) {     if (IsValid)     {         // Validate the user's email address         var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();         ApplicationUser user = manager.FindByName(Email.Text);         if (user == aught || !manager.IsEmailConfirmed(user.Id))         {             FailureText.Text = "The user either does not exist or is not confirmed.";             ErrorMessage.Visible = truthful;             return;         }         // For more information on how to enable account confirmation and countersign reset please visit https://go.microsoft.com/fwlink/?LinkID=320771         // Send email with the code and the redirect to reset password folio         string code = managing director.GeneratePasswordResetToken(user.Id);         string callbackUrl = IdentityHelper.GetResetPasswordRedirectUrl(code, Asking);         manager.SendEmail(user.Id, "Reset Password", "Please reset your countersign past clicking <a href=\"" + callbackUrl + "\">hither</a>.");         loginForm.Visible = false;         DisplayEmail.Visible = truthful;     } }                                  
  2. Open the Login.aspx page. Supersede the markup nearly the finish of the loginForm section as highlighted below:

                      <%@ Page Title="Log in" Language="C#" MasterPageFile="~/Site.Principal" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="WebForms002.Account.Login" Async="true" %>  <%@ Register Src="~/Account/OpenAuthProviders.ascx" TagPrefix="uc" TagName="OpenAuthProviders" %>  <asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">     <h2><%: Championship %>.</h2>      <div form="row">         <div class="col-md-8">             <section id="loginForm">                 <div grade="grade-horizontal">                     <h4>Use a local account to log in.</h4>                     <hr />                     <asp:PlaceHolder runat="server" ID="ErrorMessage" Visible="fake">                         <p class="text-danger">                             <asp:Literal runat="server" ID="FailureText" />                         </p>                     </asp:PlaceHolder>                     <div form="course-grouping">                         <asp:Label runat="server" AssociatedControlID="Electronic mail" CssClass="col-md-2 control-characterization">Email</asp:Label>                         <div class="col-doc-10">                             <asp:TextBox runat="server" ID="E-mail" CssClass="form-control" TextMode="Email" />                             <asp:RequiredFieldValidator runat="server" ControlToValidate="Email"                                 CssClass="text-danger" ErrorMessage="The email field is required." />                         </div>                     </div>                     <div class="form-group">                         <asp:Label runat="server" AssociatedControlID="Password" CssClass="col-dr.-2 command-label">Password</asp:Characterization>                         <div form="col-md-ten">                             <asp:TextBox runat="server" ID="Countersign" TextMode="Password" CssClass="form-command" />                             <asp:RequiredFieldValidator runat="server" ControlToValidate="Password" CssClass="text-danger" ErrorMessage="The password field is required." />                         </div>                     </div>                     <div class="class-group">                         <div class="col-md-offset-two col-md-x">                             <div class="checkbox">                                 <asp:CheckBox runat="server" ID="RememberMe" />                                 <asp:Label runat="server" AssociatedControlID="RememberMe">Recollect me?</asp:Label>                             </div>                         </div>                     </div>                     <div class="form-grouping">                         <div class="col-md-offset-2 col-md-10">                             <asp:Button runat="server" OnClick="LogIn" Text="Log in" CssClass="btn btn-default" />                         </div>                     </div>                 </div>                 <p>                     <asp:HyperLink runat="server" ID="RegisterHyperLink" ViewStateMode="Disabled">Register as a new user</asp:HyperLink>                 </p>                 <p>                     <%-- Enable this once you have account confirmation enabled for password reset functionality --%>                     <asp:HyperLink runat="server" ID="ForgotPasswordHyperLink" ViewStateMode="Disabled">Forgot your password?</asp:HyperLink>                 </p>             </department>         </div>          <div grade="col-md-4">             <section id="socialLoginForm">                 <uc:OpenAuthProviders runat="server" ID="OpenAuthLogin" />             </section>         </div>     </div> </asp:Content>                                  
  3. Open the Login.aspx.cs code-behind and uncomment the following line of code highlighted in yellow from the Page_Load event handler:

                      protected void Page_Load(object sender, EventArgs e) {     RegisterHyperLink.NavigateUrl = "Register";     // Enable this one time you have account confirmation enabled for password reset functionality     ForgotPasswordHyperLink.NavigateUrl = "Forgot";     OpenAuthLogin.ReturnUrl = Asking.QueryString["ReturnUrl"];     var returnUrl = HttpUtility.UrlEncode(Asking.QueryString["ReturnUrl"]);     if (!String.IsNullOrEmpty(returnUrl))     {         RegisterHyperLink.NavigateUrl += "?ReturnUrl=" + returnUrl;     } }                                  
  4. Run the app past pressing F5. After the folio is displayed, click the Log in link.

  5. Click the Forgot your password? link to display the Forgot Password page.

  6. Enter your email address and click the Submit button to ship an email to your address which will allow you lot to reset your password.
    Cheque your e-mail account and click on the link to display the Reset Password page.

  7. On the Reset Password folio, enter your e-mail, countersign, and confirmed countersign. So, press the Reset button.
    When you successfully reset your password, the Countersign Changed page will be displayed. Now y'all tin log in with your new password.

Once a user creates a new local account, they are emailed a confirmation link they are required to use before they tin can log on. If the user accidentally deletes the confirmation email, or the email never arrives, they volition need the confirmation link sent over again. The post-obit code changes show how to enable this.

  1. In Visual Studio, open up the Login.aspx.cs code-behind and add the following event handler after the LogIn upshot handler:

                      protected void SendEmailConfirmationToken(object sender, EventArgs e) {     var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();     var user = manager.FindByName(Electronic mail.Text);     if (user != nix)     {         if (!user.EmailConfirmed)         {             string code = managing director.GenerateEmailConfirmationToken(user.Id);             string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);             director.SendEmail(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">hither</a>.");              FailureText.Text = "Confirmation email sent. Please view the e-mail and confirm your account.";             ErrorMessage.Visible = truthful;             ResendConfirm.Visible = false;         }     } }                                  
  2. Change the LogIn upshot handler in the Login.aspx.cs code-backside by changing the code highlighted in yellow as follows:

                      protected void LogIn(object sender, EventArgs e) {     if (IsValid)     {         // Validate the user countersign         var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();         var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();          // Require the user to have a confirmed electronic mail before they can log on.         var user = manager.FindByName(Email.Text);         if (user != null)         {             if (!user.EmailConfirmed)             {                 FailureText.Text = "Invalid login attempt. Y'all must accept a confirmed email accost. Enter your electronic mail and countersign, then press 'Resend Confirmation'.";                 ErrorMessage.Visible = true;                 ResendConfirm.Visible = true;             }             else             {                 // This doen't count login failures towards account lockout                 // To enable password failures to trigger lockout, change to shouldLockout: true                 var result = signinManager.PasswordSignIn(Electronic mail.Text, Password.Text, RememberMe.Checked, shouldLockout: faux);                  switch (result)                 {                     case SignInStatus.Success:                         IdentityHelper.RedirectToReturnUrl(Asking.QueryString["ReturnUrl"], Response);                         break;                     case SignInStatus.LockedOut:                         Response.Redirect("/Account/Lockout");                         break;                     case SignInStatus.RequiresVerification:                         Response.Redirect(Cord.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={one}",                                                     Request.QueryString["ReturnUrl"],                                                     RememberMe.Checked),                                       true);                         pause;                     instance SignInStatus.Failure:                     default:                         FailureText.Text = "Invalid login try";                         ErrorMessage.Visible = truthful;                         pause;                 }             }         }     } }                                  
  3. Update the Login.aspx page by adding the code highlighted in yellow as follows:

                      <%@ Page Title="Log in" Language="C#" MasterPageFile="~/Site.Main" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="WebForms002.Account.Login" Async="true" %>  <%@ Register Src="~/Account/OpenAuthProviders.ascx" TagPrefix="uc" TagName="OpenAuthProviders" %>  <asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">     <h2><%: Title %>.</h2>      <div class="row">         <div class="col-doctor-8">             <department id="loginForm">                 <div class="form-horizontal">                     <h4>Use a local account to log in.</h4>                     <hr />                     <asp:PlaceHolder runat="server" ID="ErrorMessage" Visible="false">                         <p class="text-danger">                             <asp:Literal runat="server" ID="FailureText" />                         </p>                     </asp:PlaceHolder>                     <div form="course-group">                         <asp:Characterization runat="server" AssociatedControlID="E-mail" CssClass="col-doc-2 command-characterization">Electronic mail</asp:Label>                         <div class="col-md-ten">                             <asp:TextBox runat="server" ID="Electronic mail" CssClass="form-control" TextMode="Email" />                             <asp:RequiredFieldValidator runat="server" ControlToValidate="Email"                                 CssClass="text-danger" ErrorMessage="The email field is required." />                         </div>                     </div>                     <div course="form-group">                         <asp:Label runat="server" AssociatedControlID="Password" CssClass="col-md-ii command-characterization">Password</asp:Characterization>                         <div class="col-doctor-x">                             <asp:TextBox runat="server" ID="Password" TextMode="Password" CssClass="form-control" />                             <asp:RequiredFieldValidator runat="server" ControlToValidate="Password" CssClass="text-danger" ErrorMessage="The countersign field is required." />                         </div>                     </div>                     <div course="form-group">                         <div class="col-physician-showtime-2 col-md-x">                             <div class="checkbox">                                 <asp:CheckBox runat="server" ID="RememberMe" />                                 <asp:Label runat="server" AssociatedControlID="RememberMe">Remember me?</asp:Label>                             </div>                         </div>                     </div>                     <div class="form-grouping">                         <div course="col-md-first-2 col-md-10">                             <asp:Push button runat="server" OnClick="LogIn" Text="Log in" CssClass="btn btn-default" />                             &nbsp;&nbsp;                             <asp:Push runat="server" ID="ResendConfirm"  OnClick="SendEmailConfirmationToken" Text="Resend confirmation" Visible="false" CssClass="btn btn-default" />                         </div>                     </div>                 </div>                 <p>                     <asp:HyperLink runat="server" ID="RegisterHyperLink" ViewStateMode="Disabled">Register as a new user</asp:HyperLink>                 </p>                 <p>                     <%-- Enable this once you accept account confirmation enabled for countersign reset functionality --%>                     <asp:HyperLink runat="server" ID="ForgotPasswordHyperLink" ViewStateMode="Disabled">Forgot your password?</asp:HyperLink>                 </p>             </section>         </div>          <div course="col-md-4">             <section id="socialLoginForm">                 <uc:OpenAuthProviders runat="server" ID="OpenAuthLogin" />             </section>         </div>     </div> </asp:Content>                                  
  4. Delete any accounts in the AspNetUsers table that incorporate the electronic mail alias yous wish to test.

  5. Run the app (F5) and register your email address.

  6. Before confirming your new business relationship via the email that was only sent, endeavor to log in with the new account.
    You'll encounter that y'all are unable to log in and that you lot must have a confirmed email account. In addition, you can at present resend a confirmation message to your email account.

  7. Enter your e-mail address and password, then press the Resend confirmation button.

  8. Once y'all confirm your electronic mail address based on the newly sent email message, log in to the app.

Troubleshooting the App

If you don't receive an e-mail containing the link to verify your credentials:

  • Check your junk or spam folder.
  • Log into your SendGrid account and click on the Email Action link.
  • Be certain you used your SendGrid user account proper name as a Web.config value rather than your SendGrid account email address.

Additional Resources

  • Links to ASP.NET Identity Recommended Resources
  • Account Confirmation and Password Recovery with ASP.NET Identity
  • ASP.Internet Web Forms tutorial serial - Add an OAuth 2.0 Provider
  • Deploy a Secure ASP.NET Web Forms App with Membership, OAuth, and SQL Database to Azure App Service
  • ASP.Net Web Forms tutorial series - Enable SSL for the Projection