Language

ASP.NET MVC 5 app with SMS and email Two-Factor Authentication

By Rick Anderson|

This tutorial shows you how to build an ASP.NET MVC 5 web app with Two-Factor Authentication. You should complete Create a secure ASP.NET MVC 5 web app with log in, email confirmation and password reset before proceeding.  You can download the completed application here.  The download contains debugging helpers that let you test email confirmation and SMS without setting up an email or SMS provider.

This tutorial was written by Rick Anderson  ( Twitter: @RickAndMSFT ).

Create an ASP.NET  MVC app

Start by installing and running Visual Studio Express 2013 for Web or Visual Studio 2013.  Install Visual Studio 2013 Update 3 or higher.

Warning: You should complete Create a secure ASP.NET MVC 5 web app with log in, email confirmation and password reset before proceeding. You must install Visual Studio 2013 Update 3 or higher to complete this tutorial.
  1. Create a new ASP.NET Web project and select the MVC template. Web Forms also supports ASP.NET Identity, so you could follow similar steps in a web forms app.

  2. Leave the default authentication as Individual User Accounts. If you'd like to host the app in Azure, leave the check box checked. Later in the tutorial we will deploy to Azure. You can open an Azure account for free.
  3. Set the project to use SSL.
  4. In the Package Manager Console,  enter the following the following command:

    Install-Package Twilio

Set up SMS for 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.
  3. From the Numbers tab, copy your Twilio phone number.
  4. Make the SID, account token and phone number available to the app. To keep things simple we will store these values in the web.config file. When we deploy to Azure, we can store the values securely in the app settings section on the web site configure tab.
    </connectionStrings>
       <appSettings>
          <add key="webpages:Version" value="3.0.0.0" />
          <!-- Markup removed for clarity. -->
          <!-- SendGrid-->
          <add key="mailAccount" value="account" />
          <add key="mailPassword" value="password" />
          <!-- Twilio-->
          <add key="TwilioSid" value="my sid" />
          <add key="TwilioToken" value="my token" />
          <add key="TwilioFromPhone" value="+12065551234" />
       </appSettings>
      <system.web>
    Security Note: Never store sensitive data in your source code. The account and credentials are added to the code above to keep the sample simple. See Jon Atten's ASP.NET MVC: Keep Private Settings Out of Source Control.
  5. Configure the SmsService class in the App_Start\IdentityConfig.cs file:
    public class SmsService : IIdentityMessageService
    {
       public Task SendAsync(IdentityMessage message)
       {
          var Twilio = new TwilioRestClient(
             ConfigurationManager.AppSettings["TwilioSid"],
             ConfigurationManager.AppSettings["TwilioToken"]
         );
          var result = Twilio.SendMessage(
              ConfigurationManager.AppSettings["TwilioFromPhone"],
             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. Update the Views\Manage\Index.cshtml Razor view: (note: don't just remove the comments in the exiting code, use the code below.)
    @model MvcPWy.Models.IndexViewModel
    @{
       ViewBag.Title = "Manage";
    }
    
    <h2>@ViewBag.Title.</h2>
    
    <p class="text-success">@ViewBag.StatusMessage</p>
    <div>
       <h4>Change your account settings</h4>
       <hr />
       <dl class="dl-horizontal">
          <dt>Password:</dt>
          <dd>
             [
             @if (Model.HasPassword)
             {
                @Html.ActionLink("Change your password", "ChangePassword")
             }
             else
             {
                @Html.ActionLink("Create", "SetPassword")
             }
             ]
          </dd>
          <dt>External Logins:</dt>
          <dd>
             @Model.Logins.Count [
             @Html.ActionLink("Manage", "ManageLogins") ]
          </dd>
          <dt>Phone Number:</dt>
          <dd>
             @(Model.PhoneNumber ?? "None") [
             @if (Model.PhoneNumber != null)
             {
                @Html.ActionLink("Change", "AddPhoneNumber")
                @: &nbsp;|&nbsp;
                @Html.ActionLink("Remove", "RemovePhoneNumber")
             }
             else
             {
                @Html.ActionLink("Add", "AddPhoneNumber")
             }
             ]
          </dd>
    
          <dt>Two-Factor Authentication:</dt> 
          <dd>
             @if (Model.TwoFactor)
             {
                using (Html.BeginForm("DisableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                   @Html.AntiForgeryToken()
                   <text>Enabled
                      <input type="submit" value="Disable" class="btn btn-link" />
                   </text>
                }
             }
             else
             {
                using (Html.BeginForm("EnableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                   @Html.AntiForgeryToken()
                   <text>Disabled
                      <input type="submit" value="Enable" class="btn btn-link" />
                   </text>
                }
             }
          </dd>
    
       </dl>
    </div>
  7. Verify the EnableTwoFactorAuthentication and DisableTwoFactorAuthentication action methods in the ManageController have the [ValidateAntiForgeryToken] attribute:
    //
    // POST: /Manage/EnableTwoFactorAuthentication
    [HttpPost,ValidateAntiForgeryToken]
    public async Task<ActionResult> EnableTwoFactorAuthentication()
    {
        await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", "Manage");
    }
    
    //
    // POST: /Manage/DisableTwoFactorAuthentication
    [HttpPost, ValidateAntiForgeryToken]
    public async Task<ActionResult> DisableTwoFactorAuthentication()
    {
        await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false);
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", "Manage");
    }
  8. Run the app and log in with the account you previously registered.
  9. Click on your User ID, which activates the Index action method in Manage controller.

  10. Click Add.

  11. The AddPhoneNumber action method displays a dialog box to enter a phone number that can receive SMS messages.

    // GET: /Account/AddPhoneNumber
    public ActionResult AddPhoneNumber()
    {
       return View();
    }

  12. In a few seconds you will get a text message with the verification code. Enter it and press Submit.

  13. The Manage view shows your phone number was added.

Enable two-factor authentication

In the template generated app, you need to use the UI to enable two-factor authentication (2FA). To enable 2FA, click on your user ID (email alias) in the navigation bar.


Click on enable 2FA.

Log out, then log back in. If you've enabled email (see my previous tutorial), you can select the SMS or email for 2FA.

The Verify Code page is displayed where you can enter the code (from SMS or email).

Clicking on the Remember this browser check box will exempt you from needing to use 2FA 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 2FA and clicking on the Remember this browser will provide you with convenient one step password access, while still retaining strong 2FA protection for all access from non-trusted devices. You can do this on any private device you regularly use.

This tutorial provides a quick introduction to enabling 2FA on a new ASP.NET MVC app. My tutorial Two-factor authentication using SMS and email with ASP.NET Identity  goes into detail on the code behind the sample.

Additional Resources

This article was originally created on August 19, 2014

Author Information

Rick Anderson

Rick Anderson – Rick Anderson works as a programmer writer for Microsoft, focusing on ASP.NET MVC, Windows Azure and Entity Framework. You can follow him on twitter via @RickAndMSFT.