Events
Power BI DataViz World Championships
Feb 14, 4 PM - Mar 31, 4 PM
With 4 chances to enter, you could win a conference package and make it to the LIVE Grand Finale in Las Vegas
Learn moreThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
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 ).
Start by installing and running Visual Studio Express 2013 for Web or higher.
Note
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.
This tutorial provides instructions for using either Twilio or ASPSMS but you can use any other SMS provider.
Creating a User Account with an SMS provider
Installing additional packages or adding service references
Twilio:
In the Package Manager Console, enter the following command:
Install-Package Twilio
ASPSMS:
The following service reference needs to be added:
Address:
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Namespace:
ASPSMSX2
Figuring out SMS Provider User credentials
We recommend using the most secure secure authentication option. For .NET apps deployed to Azure, see:
Azure Key Vault and .NET Aspire provide the most secure way to store and retrieve secrets. Azure Key Vault is a cloud service that safeguards encryption keys and secrets like certificates, connection strings, and passwords. For .NET Aspire, see Secure communication between hosting and client integrations.
Avoid Resource Owner Password Credentials Grant because it:
When the app is deployed to a test server, an environment variable can be used to set the connection string to a test database server. Environment variables are generally stored in plain, unencrypted text. If the machine or process is compromised, environment variables can be accessed by untrusted parties. We recommend against using environment variables to store a production connection string as it's not the most secure approach.
Configuration data guidelines:
Twilio:
From the Dashboard tab of your Twilio account, copy the Account SID and Auth token.
ASPSMS:
From your account settings, navigate to Userkey and copy it together with your self-defined Password.
We will later store these values in the web.config file within the keys "SMSAccountIdentification"
and "SMSAccountPassword"
.
4. Specifying SenderID / Originator
Twilio:
From the Numbers tab, copy your Twilio phone number.
ASPSMS:
Within the Unlock Originators Menu, unlock one or more Originators or choose an alphanumeric Originator (Not supported by all networks).
We will later store this value in the web.config file within the key "SMSAccountFrom"
.
5. Transferring SMS provider credentials into app
Make the credentials and sender 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.
[!code-xml[Main](aspnet-mvc-5-app-with-sms-and-email-two-factor-authentication/samples/sample1.xml?highlight=8-10)]
> [!WARNING]
> Security - Never store sensitive data in your source code. The account and credentials are added to the code above to keep the sample simple. See [Best practices for deploying passwords and other sensitive data to ASP.NET and Azure](../../../identity/overview/features-api/best-practices-for-deploying-passwords-and-other-sensitive-data-to-aspnet-and-azure.md).
Implementation of data transfer to SMS provider
Configure the SmsService
class in the App_Start\IdentityConfig.cs file.
Depending on the used SMS provider activate either the Twilio or the ASPSMS section:
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Twilio Begin
//var accountSid = ConfigurationManager.AppSettings["SMSAccountIdentification"];
//var authToken = ConfigurationManager.AppSettings["SMSAccountPassword"];
//var fromNumber = ConfigurationManager.AppSettings["SMSAccountFrom"];
//TwilioClient.Init(accountSid, authToken);
//MessageResource result = MessageResource.Create(
//new PhoneNumber(message.Destination),
//from: new PhoneNumber(fromNumber),
//body: message.Body
//);
////Status is one of Queued, Sending, Sent, Failed or null if the number is not valid
//Trace.TraceInformation(result.Status.ToString());
////Twilio doesn't currently have an async API, so return success.
//return Task.FromResult(0);
// Twilio End
// ASPSMS Begin
// var soapSms = new MvcPWx.ASPSMSX2.ASPSMSX2SoapClient("ASPSMSX2Soap");
// soapSms.SendSimpleTextSMS(
// System.Configuration.ConfigurationManager.AppSettings["SMSAccountIdentification"],
// System.Configuration.ConfigurationManager.AppSettings["SMSAccountPassword"],
// message.Destination,
// System.Configuration.ConfigurationManager.AppSettings["SMSAccountFrom"],
// message.Body);
// soapSms.Close();
// return Task.FromResult(0);
// ASPSMS End
}
}
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")
@: |
@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>
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");
}
Run the app and log in with the account you previously registered.
Click on your User ID, which activates the Index
action method in Manage
controller.
Click Add.
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();
}
In a few seconds you will get a text message with the verification code. Enter it and press Submit.
The Manage view shows your phone number was added.
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.
Logout, 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.
Events
Power BI DataViz World Championships
Feb 14, 4 PM - Mar 31, 4 PM
With 4 chances to enter, you could win a conference package and make it to the LIVE Grand Finale in Las Vegas
Learn moreTraining
Module
Secure a .NET web app with the ASP.NET Core Identity framework - Training
Learn how to add authentication and authorization to a .NET web app using the ASP.NET Core Identity framework.
Certification
Microsoft Certified: Identity and Access Administrator Associate - Certifications
Demonstrate the features of Microsoft Entra ID to modernize identity solutions, implement hybrid solutions, and implement identity governance.