Create an ASP.NET Web Forms app with SMS Two-Factor Authentication (C#)

by Erik Reitan

This tutorial shows you how to build an ASP.NET Web Forms app with Two-Factor Authentication. This tutorial was designed to complement the tutorial titled Create a secure ASP.NET Web Forms app with user registration, email confirmation and password reset. In addition, this tutorial was based on Rick Anderson's MVC tutorial.

Introduction

This tutorial guides you through the steps required to create an ASP.NET Web Forms application that supports Two-Factor Authentication using Visual Studio. Two-Factor Authentication is an extra user authentication step. This extra step generates a unique personal identification number (PIN) during sign-in. The PIN is commonly sent to the user as an email or SMS message. The user of your app then enters the PIN as an extra authentication measure when signing-in.

Tutorial Tasks and Information:

Create an ASP.NET Web Forms App

Start by installing and running Visual Studio Express 2013 for Web or Visual Studio 2013. Install Visual Studio 2013 Update 3 or higher as well. Also, you will need to create a Twilio account, as explained below.

Note

Important: You must install Visual Studio 2013 Update 3 or higher to complete this tutorial.

  1. Create a new project (File -> New Project) and select the ASP.NET Web Application template along with the .NET Framework version 4.5.2 from the New Project dialog box.
  2. From the New ASP.NET Project dialog box, select the Web Forms template. Leave the default authentication as Individual User Accounts. Then, click OK to create the new project.
    Screenshot of the New A S P dot Net Project dialog box showing the Web Forms icon highlighted in blue.
  3. Enable Secure Sockets Layer (SSL) for the project. Follow the steps available in the Enable SSL for the Project section of the Getting Started with Web Forms tutorial series.
  4. In Visual Studio, open the Package Manager Console (Tools -> NuGet Package Manger -> Package Manager Console), and enter the following command:
    Install-Package Twilio

Setup SMS and Two-Factor Authentication

This tutorial uses Twilio, but you can use any SMS provider.

  1. Create a Twilio account.

  2. From the Dashboard tab of your Twilio account, copy the Account SID and Auth Token. You will add them to your app later.

  3. From the Numbers tab, copy your Twilio phone number as well.

  4. Make the Twilio Account SID, Auth Token and phone number available to the app. To keep things simple you will store these values in the web.config file. When you deploy to Azure, you can store the values securely in the appSettings section on the web site configure tab. Also, when adding the phone number, only use numbers.
    Notice that you can also add SendGrid credentials. SendGrid is an email notification service. For details about how to enable SendGrid, see the 'Hook Up SendGrid' section of the tutorial titled Create a Secure ASP.NET Web Forms App with user registration, email confirmation and password reset.

    </connectionStrings>
      <appSettings>
        <!-- SendGrid Credentials-->    
        <add key="emailServiceUserName" value="[EmailServiceAccountUserName]" />
        <add key="emailServicePassword" value="[EmailServiceAccountPassword]" />
        <!-- Twilio Credentials-->
        <add key="SMSSID" value="[SMSServiceAccountSID]" />
        <add key="SMSAuthToken" value="[SMSServiceAuthorizationToken]" />
        <add key="SMSPhoneNumber" value="+[SMSPhoneNumber]" />    
      </appSettings>
      <system.web>
    

    Warning

    Security - Never store sensitive data in your source code. In this example, the account and credentials are stored in the appSettings section of the Web.config file. On Azure, you can securely store these values on the Configure tab in the Azure portal. For related information, see Rick Anderson's topic titled Best practices for deploying passwords and other sensitive data to ASP.NET and Azure.

  5. Configure the SmsService class in the App_Start\IdentityConfig.cs file by making the following changes highlighted in yellow:

    public class SmsService : IIdentityMessageService
    {
        public Task SendAsync(IdentityMessage message)
        {
            var Twilio = new TwilioRestClient(
               ConfigurationManager.AppSettings["SMSSID"],
               ConfigurationManager.AppSettings["SMSAuthToken"]
            );
            var result = Twilio.SendMessage(
                ConfigurationManager.AppSettings["SMSPhoneNumber"],
               message.Destination, message.Body);
    
            // Status is one of Queued, Sending, Sent, Failed or null if the number is not valid
            Trace.TraceInformation(result.Status);
    
            // Twilio doesn't currently have an async API, so return success.
            return Task.FromResult(0);
        }
    }
    
  6. Add the following using statements to the beginning of the IdentityConfig.cs file:

    using Twilio;
    using System.Net;
    using System.Configuration;
    using System.Diagnostics;
    
  7. Update the Account/Manage.aspx file by removing the lines highlighted in yellow:

    <%@ Page Title="Manage Account" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="WebFormsTwoFactor.Account.Manage" %>
    
    <%@ Register Src="~/Account/OpenAuthProviders.ascx" TagPrefix="uc" TagName="OpenAuthProviders" %>
    
    <asp:Content ContentPlaceHolderID="MainContent" runat="server">
        <h2><%: Title %>.</h2>
    
        <div>
            <asp:PlaceHolder runat="server" ID="successMessage" Visible="false" ViewStateMode="Disabled">
                <p class="text-success"><%: SuccessMessage %></p>
            </asp:PlaceHolder>
        </div>
    
        <div class="row">
            <div class="col-md-12">
                <div class="form-horizontal">
                    <h4>Change your account settings</h4>
                    <hr />
                    <dl class="dl-horizontal">
                        <dt>Password:</dt>
                        <dd>
                            <asp:HyperLink NavigateUrl="/Account/ManagePassword" Text="[Change]" Visible="false" ID="ChangePassword" runat="server" />
                            <asp:HyperLink NavigateUrl="/Account/ManagePassword" Text="[Create]" Visible="false" ID="CreatePassword" runat="server" />
                        </dd>
                        <dt>External Logins:</dt>
                        <dd><%: LoginsCount %>
                            <asp:HyperLink NavigateUrl="/Account/ManageLogins" Text="[Manage]" runat="server" />
    
                        </dd>
                        <%--
                            Phone Numbers can used as a second factor of verification in a two-factor authentication system.
                            See <a href="https://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
                            for details on setting up this ASP.NET application to support two-factor authentication using SMS.
                            Uncomment the following block after you have set up two-factor authentication
                        --%>
    
                        <dt>Phone Number:</dt>
                        <%--
                        <% if (HasPhoneNumber)
                           { %>
                        <dd>
                            <asp:HyperLink NavigateUrl="/Account/AddPhoneNumber" runat="server" Text="[Add]" />
                        </dd>
                        <% }
                           else
                           { %>
                        <dd>
                            <asp:Label Text="" ID="PhoneNumber" runat="server" />
                            <asp:HyperLink NavigateUrl="/Account/AddPhoneNumber" runat="server" Text="[Change]" /> &nbsp;|&nbsp;
                            <asp:LinkButton Text="[Remove]" OnClick="RemovePhone_Click" runat="server" />
                        </dd>
                        <% } %>
                        --%>
    
                        <dt>Two-Factor Authentication:</dt>
                        <dd>
                            <p>
                                There are no two-factor authentication providers configured. See <a href="https://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
                                for details on setting up this ASP.NET application to support two-factor authentication.
                            </p>
                            <% if (TwoFactorEnabled)
                              { %> 
                            <%--
                            Enabled
                            <asp:LinkButton Text="[Disable]" runat="server" CommandArgument="false" OnClick="TwoFactorDisable_Click" />
                            --%>
                            <% }
                              else
                              { %> 
                            <%--
                            Disabled
                            <asp:LinkButton Text="[Enable]" CommandArgument="true" OnClick="TwoFactorEnable_Click" runat="server" />
                            --%>
                            <% } %>
                        </dd>
                    </dl>
                </div>
            </div>
        </div>
    </asp:Content>
    
  8. In the Page_Load handler of the Manage.aspx.cs code-behind, uncomment the line of code highlighted in yellow so that it appears as follows:

    protected void Page_Load()
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
    
        HasPhoneNumber = String.IsNullOrEmpty(manager.GetPhoneNumber(User.Identity.GetUserId()));
    
        // Enable this after setting up two-factor authentientication
        PhoneNumber.Text = manager.GetPhoneNumber(User.Identity.GetUserId()) ?? String.Empty;
    
        TwoFactorEnabled = manager.GetTwoFactorEnabled(User.Identity.GetUserId());
    
        LoginsCount = manager.GetLogins(User.Identity.GetUserId()).Count;
    
        var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
    
        if (!IsPostBack)
        {
            // Determine the sections to render
            if (HasPassword(manager))
            {
                ChangePassword.Visible = true;
            }
            else
            {
                CreatePassword.Visible = true;
                ChangePassword.Visible = false;
            }
    
            // Render success message
            var message = Request.QueryString["m"];
            if (message != null)
            {
                // Strip the query string from action
                Form.Action = ResolveUrl("~/Account/Manage");
    
                SuccessMessage =
                    message == "ChangePwdSuccess" ? "Your password has been changed."
                    : message == "SetPwdSuccess" ? "Your password has been set."
                    : message == "RemoveLoginSuccess" ? "The account was removed."
                    : message == "AddPhoneNumberSuccess" ? "Phone number has been added"
                    : message == "RemovePhoneNumberSuccess" ? "Phone number was removed"
                    : String.Empty;
                successMessage.Visible = !String.IsNullOrEmpty(SuccessMessage);
            }
        }
    }
    
  9. In the codebehind of Account/TwoFactorAuthenticationSignIn.aspx.cs, update the Page_Load handler by adding the following code highlighted in yellow:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            var userId = signinManager.GetVerifiedUserId<ApplicationUser, string>();
            if (userId == null)
            {
                Response.Redirect("/Account/Error", true);
            }
            var userFactors = manager.GetValidTwoFactorProviders(userId);
            Providers.DataSource = userFactors.Select(x => x).ToList();
            Providers.DataBind();
        }
    }
    

    By making the above code change, the "Providers" DropDownList containing the authentication options will not be reset to the first value. This will allow the user to successfully select all options to use when authenticating, not just the first.

  10. In Solution Explorer, right-click Default.aspx and select Set As Start Page.

  11. By testing your app, first build the app (Ctrl+Shift+B) and then run the app (F5) and either select Register to create a new user account or select Log in if the user account has already been registered.

  12. Once you (as the user) have logged in, click on the User ID (email address) in the navigation bar to display the Manage Account page (Manage.aspx).
    Screenshot of the A S P dot Net response browser window showing the User I D highlighted with a red rectangle.

  13. Click Add next to Phone Number on the Manage Account page.
    Screenshot of the Manage Account browser window showing the list of account settings and option links to change them.

  14. Add the phone number where you (as the user) would like to receive SMS messages (text messages) and click the Submit button.
    Screenshot of the Phone Number browser window showing the Phone Number field with the entered phone number value and the Submit button.
    At this point, the app will use the credentials from the Web.config to contact Twilio. A SMS message (text message) will be sent to the phone associated with the user account. You can verify that the Twilio message was sent by viewing the Twilio dashboard.

  15. In a few seconds, the phone associated with the user account will get a text message containing the verification code. Enter the verification code and press Submit.
    Screenshot of the Verify Phone Number browser window showing the Code field with the entered verification code and the Submit button.

Enable Two-Factor Authentication for a Registered User

At this point, you have enabled two-factor authentication for your app. For a user to use two-factor authentication, they can simply change their settings using the UI.

  1. As a user of your app you can enable two-factor authentication for your specific account by clicking on the user ID (email alias) in the navigation bar to display the Manage Account page.Then, click on the Enable link to enable two-factor authentication for the account.Screenshot of the Manage Account browser window showing the Enable link associated with Two-Factor Authentication highlighted in red.
  2. Log off, then log back in. If you've enabled email, you can select either SMS or email for two-factor authentication. If you haven't enabled email, see the tutorial titled Create a Secure ASP.NET Web Forms App with User Registration, Email Confirmation and Password Reset.Screenshot of the Two-Factor Authentication browser window showing the Select Two-Factor Authentication Provider dropdown list.
  3. The Two-Factor Authentication page is displayed where you can enter the code (from SMS or email).Screenshot of the Two-Factor Authentication browser window showing the Code field with the entered verification code and the Submit button.
    Clicking on the Remember this browser check box will exempt you from needing to use two-factor authentication to log in when using the browser and device where you checked the box. As long as malicious users can't gain access to your device, enabling two-factor authentication and clicking on the Remember this browser will provide you with convenient one step password access, while still retaining strong two-factor authentication protection for all access from non-trusted devices. You can do this on any private device you regularly use.

Additional Resources