Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that configuration specifies the same validationKey and validation algorithm.

Resolving view state message authentication code (MAC) errors

What is view state?

View state is information that is round-tripped between WebForms (.aspx) pages in an ASP.NET application. The HTML markup for the __VIEWSTATE field resembles the following:

<input type=”hidden” name=”__VIEWSTATE” id=”__VIEWSTATE” value=”…” />

One example of an item that might be stored in the __VIEWSTATE field is the text of a Button control. If a user clicks the button, the Button_Click event handler will be able to extract the Button’s text from the view state field. See the ASP.NET View State Overview topic on the Microsoft Developer Network (MSDN) website for a much more detailed overview of the ASP.NET view state.

Because the __VIEWSTATE field contains important information that is used to reconstruct the page on postback, make sure that an attacker cannot to tamper with this field. If an attacker submitted a malicious __VIEWSTATE payload, the attacker could potentially trick the application into performing an action that it otherwise would not have performed.

To prevent this kind of tampering attack, the __VIEWSTATE field is protected by a message authentication code (MAC). ASP.NET validates the MAC that is submitted together with the __VIEWSTATE payload when a postback occurs. The key that is used to calculate the MAC is specified in the application’s  element in the Web.config file. Because the attacker cannot guess the contents of the <machineKey> element, the attacker cannot provide a valid MAC if the attacker tries to tamper with the __VIEWSTATE payload. ASP.NET will detect that a valid MAC hasn’t been provided, and ASP.NET will reject the malicious request.

What causes MAC validation errors?

A MAC validation error will resemble the following example:

Server Error in ‘/’ Application.

Validation of viewstate MAC failed. If this application is hosted by a web farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.HttpException: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

Source Error: [No relevant source lines]

Source File: … Line: 0

Stack Trace:

[ViewStateException: Invalid viewstate.
Client IP: ::1
Port: 40653
Referer: http://localhost:40643/MyPage.aspx
Path: /MyPage.aspx
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
ViewState: …]

[HttpException (0x80004005): Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

See for more information.]
System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError) +190
System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState) +46
System.Web.UI.ObjectStateFormatter.Deserialize(String inputString, Purpose purpose) +861
System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter2.Deserialize(String serializedState, Purpose purpose) +51
System.Web.UI.Util.DeserializeWithAssert(IStateFormatter2 formatter, String serializedState, Purpose purpose) +67
System.Web.UI.HiddenFieldPageStatePersister.Load() +444
System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +368
System.Web.UI.Page.LoadAllState() +109
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +7959
System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +429
System.Web.UI.Page.ProcessRequest() +125
System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +48
System.Web.UI.Page.ProcessRequest(HttpContext context) +234
ASP.mypage_aspx.ProcessRequest(HttpContext context) in …:0
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +1300
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +140

Cause 1: The web application is running in a farm (multi-server environment)

ASP.NET automatically generates a cryptographic key for each application and stores the key in the HKCU registry hive. This auto-generated key is used if there is no explicit <machineKey> element in the application’s configuration. However, because this auto-generated key is local to the computer that created the key, this scenario causes a problem for applications that run in a farm. Each server in the farm will generate its own local key, and none of the servers in the farm will agree on which key to use. The result is that, if one server generates a __VIEWSTATE payload that a different server consumes, the consumer will experience a MAC validation failure.

  • Resolution 1a: Create an explicit <machineKey> element

    By adding an explicit <machineKey> element to the application’s Web.config file, the developer tells ASP.NET not to use the auto-generated cryptographic key. See Appendix A for instructions on how to generate a <machineKey> element. After this element is added to the Web.config file, redeploy the application to each server in the farm.

    Note Some web hosting services, such as Microsoft Azure websites, take steps to synchronize each application’s auto-generated key across their back-end servers. This lets applications that have not specified an explicit <machineKey> element to continue working in these environments, even if the application is running in a farm. If your application is running on a third-party hosting service, please contact your hosting provider to determine whether this situation applies to you.

  • Resolution 1b: Enable affinity in the load balancer

    If your sites are operating behind a load balancer, you can enable server affinity to temporarily work around the issue. This helps ensure that any given client only interacts with one physical server behind the load balancer so that all cryptographic payloads will be both generated by and consumed by the same server.

    This should not be considered a long-term solution to the problem. Even when server affinity is enabled, most load balancers will redirect the client to a different physical server if the original server to which the load balancers were affinitized goes offline. This causes the new server to reject cryptographic payloads (such as __VIEWSTATE, forms authentication tickets, MVCs anti-forgery tokens, and other services) that the client currently has.

    Using an explicit <machineKey> element and redeploying the application should be preferred over enabling server affinity.

Cause 2: The worker process uses the IIS 7.0 application pool identity

Internet Information Services (IIS) 7.0 (Windows Vista, Windows Server 2008) introduced application pool identity, a new isolation mechanism that helps provide increased security for servers that run ASP.NET applications. However, sites that are running under the application pool identity do not have access to the HKCU registry. This is where the ASP.NET runtime stores its auto-generated <machineKey> keys. The result is that ASP.NET cannot persist the auto-generated key when the application pool is reset. Therefore, every time w3wp.exe is reset, a new temporary key is generated.

Note This is not an issue in IIS 7.5 (Windows 7, Windows Server 2008 R2) and later versions. On these versions of IIS, ASP.NET can persist its auto-generated keys in a different location that survives application pool resets.

  • Resolution 2a: Use the aspnet_regiis utility

    ASP.NET installations contain a utility, aspnet_regiis.exe. This utility lets ASP.NET interface with IIS to perform the configurations that are required to run a managed application. One of these configurations creates the necessary keys in the registry hive to enable persistence of auto-generated machine keys.

    First, you have to determine which application pool your site is using. This can be determined by using the inetmgr utility that is included with IIS. Select your site in the tree view at the left, right-click Manage Website, and then click Advanced Settings. The dialog box that appears will show the application pool name.

    Advanced Settings

    To scaffold the appropriate registry keys for an ASP.NET 4.0 application pool, follow these steps:

    1. Open an administrative command prompt.
    2. Locate the appropriate directory, depending on whether your application pool is 32-bit or 64-bit:
      • 32-bit application pool: cd /d %windir%\Microsoft.NET\Framework\v4.0.30319
      • 64-bit application pool: cd /d %windir%\Microsoft.NET\Framework64\v4.0.30319
    3. Move to the directory, type the following command, and then press Enter:
      aspnet_regiis -ga “IIS APPPOOL\app-pool-name”

    If the application pool is an ASP.NET 2.0 or 3.5 application pool, follow these steps:

    1. Open an administrative command prompt.
    2. Locate the appropriate directory, depending on whether your application pool is 32-bit or 64-bit:
      • 32-bit application pool: cd /d %windir%\Microsoft.NET\Framework\v2.0.50727
      • 64-bit application pool: cd /d %windir%\Microsoft.NET\Framework64\v2.0.50727
    3. Move to the directory, type the following command, and then press Enter:
      aspnet_regiis -ga “IIS APPPOOL\app-pool-name”

    For example, if your application pool is named My App Pool (as in the previous image), run the following command:

    aspnet_regiis -ga “IIS APPPOOL\My App Pool”

    Note The system services APPHOSTSVC and WAS may have to be running for the aspnet_regiis utility to resolve IIS APPPOOL\* names appropriately.

  • Resolution 2b: Create an explicit <machineKey> element

    By adding an explicit <machineKey> element to the application’s Web.config file, the developer tells ASP.NET not to use the auto-generated cryptographic key. See Appendix A for instructions on how to generate a <machineKey> element.

Cause 3: The application pool is configured by using LoadUserProfile=false

If the application pool is running with a custom identity, IIS may not have loaded the user profile for the identity. This has the side effect of making the HKCU registry unavailable for ASP.NET to persist the auto-generated <machineKey>. Therefore, a new auto-generated key will be created every time that the application restarts. See the User Profile section on the Microsoft website for more information.

  • Resolution 3a: Use the aspnet_regiis utility

    The instructions for this are the same as Resolution 2a. See that section for more information.

  • Resolution 3b: Use an explicit <machineKey>

    By adding an explicit <machineKey> element to the application’s Web.config file, the developer tells ASP.NET not to use the auto-generated cryptographic key. See Appendix A for instructions on how to generate a <machineKey> element.

  • Resolution 3c: Provision the required HKCU registry keys manually

    If you cannot run the aspnet_regiis utility, you can use a Windows PowerShell script to provision the appropriate registry keys in HKCU. See Appendix B for more information.

  • Resolution 3d: Set LoadUserProfile=true for this application pool

    You can also enable loading the user profile inside this application pool. This makes the HKCU registry hive, temporary folder, and other user-specific storage locations available to the application. However, this may cause increased disk or memory usage for the worker process. See the element for more information about how to enable this setting.

Cause 4: The Page.ViewStateUserKey property has an incorrect value

Software developers can decide to use the Page.ViewStateUserKey property to add cross-site request forgery protection to the __VIEWSTATE field. If you use the Page.ViewStateUserKey property, it is typically set to a value such as the current user’s username or the user’s session identifier. The project templates for WebForms applications in Microsoft Visual Studio 2012 and later versions contain samples that use this property. See the Page.ViewStateUserKey Property topic on the Microsoft Developer Network (MSDN) website for more information.

If the ViewStateUserKey property is specified, its value is burned into __VIEWSTATE at generation time. When the __VIEWSTATE field is consumed, the server checks the current Page’s ViewStateUserKey property and validates it against the value that was used to generate the __VIEWSTATE field. If the values do not match, the request is rejected as potentially malicious.

An example of a ViewStateUserKey-related failure would be a client who has two tabs open in the browser. The client is logged in as User A, and in the first tab, a Page is rendered with a __VIEWSTATE whose ViewStateUserKey property contains “User A.” In the second tab, the client logs out and then logs back in as User B. The client goes back to the first tab and submits the form. The ViewStateUserKey property might contain “User B” (because that is what the client’s authentication cookie says). However, the __VIEWSTATE field that the client submitted contains “User A.” This mismatch causes the failure.

  • Resolution 4a: Verify that ViewStateUserKey is set correctly

    If your application uses the ViewStateUserKey property, verify that the property’s value is the same both when view state is generated and when it is consumed. If you are using the current logged-in user’s username, make sure that the user is still logged in and that the user’s identity has not changed at the time of postback. If you are using the current user’s session identifier, make sure that the session hasn’t timed out.

    If you are running in a farm environment, make sure that the <machineKey> elements match. See Appendix A for instructions about how to generate these elements.

Appendix A: How to generate a <machineKey> element

Security warning

There are many web sites that will generate a <machineKey> element for you with the click of a button. Never use a <machineKey> element that you obtained from one of these sites. It is impossible to know whether these keys were created securely or if they are being recorded to a secret database. You should only ever use <machineKey> configuration elements that you created yourself.

To generate a <machineKey> element yourself, you can use the following Windows PowerShell script:

# Generates a <machineKey> element that can be copied + pasted into a Web.config file.
function Generate-MachineKey {
  param (
    [ValidateSet("AES", "DES", "3DES")]
    [string]$decryptionAlgorithm = 'AES',
    [ValidateSet("MD5", "SHA1", "HMACSHA256", "HMACSHA384", "HMACSHA512")]
    [string]$validationAlgorithm = 'HMACSHA256'
  process {
    function BinaryToHex {
        process {
            $builder = new-object System.Text.StringBuilder
            foreach ($b in $bytes) {
              $builder = $builder.AppendFormat([System.Globalization.CultureInfo]::InvariantCulture, "{0:X2}", $b)
    switch ($decryptionAlgorithm) {
      "AES" { $decryptionObject = new-object System.Security.Cryptography.AesCryptoServiceProvider }
      "DES" { $decryptionObject = new-object System.Security.Cryptography.DESCryptoServiceProvider }
      "3DES" { $decryptionObject = new-object System.Security.Cryptography.TripleDESCryptoServiceProvider }
    $decryptionKey = BinaryToHex($decryptionObject.Key)
    switch ($validationAlgorithm) {
      "MD5" { $validationObject = new-object System.Security.Cryptography.HMACMD5 }
      "SHA1" { $validationObject = new-object System.Security.Cryptography.HMACSHA1 }
      "HMACSHA256" { $validationObject = new-object System.Security.Cryptography.HMACSHA256 }
      "HMACSHA385" { $validationObject = new-object System.Security.Cryptography.HMACSHA384 }
      "HMACSHA512" { $validationObject = new-object System.Security.Cryptography.HMACSHA512 }
    $validationKey = BinaryToHex($validationObject.Key)
      "<machineKey decryption=`"{0}`" decryptionKey=`"{1}`" validation=`"{2}`" validationKey=`"{3}`" />",
      $decryptionAlgorithm.ToUpperInvariant(), $decryptionKey,
      $validationAlgorithm.ToUpperInvariant(), $validationKey)

For ASP.NET 4.0 applications, you can just call Generate-MachineKey without parameters to generate a <machineKey> element as follows:

PS> Generate-MachineKey
<machineKey decryption="AES" decryptionKey="..." validation="HMACSHA256" validationKey="..." />

ASP.NET 2.0 and 3.5 applications do not support HMACSHA256. Instead, you can specify SHA1 to generate a compatible <machineKey> element as follows:

PS> Generate-MachineKey -validation sha1
<machineKey decryption="AES" decryptionKey="..." validation="SHA1" validationKey="..." />

As soon as you have a <machineKey> element, you can put it in the Web.config file. The <machineKey> element is only valid in the Web.config file at the root of your application and is not valid at the subfolder level.

    <machineKey ... />

For a full list of supported algorithms, run help Generate-MachineKey from the Windows PowerShell prompt.

Appendix B: Provisioning the registry to persist auto-generated keys

By default, because ASP.NETs auto-generated keys are persisted in the HKCU registry, these keys may be lost if the user profile hasn’t been loaded into the IIS worker process and then the application pool recycles. This scenario could affect shared hosting providers that are running application pools as standard Windows user accounts.

To work around this situation, ASP.NET enables persisting the auto-generated keys in the HKLM registry instead of the HKCU registry. This is typically performed by using the aspnet_regiis utility (see instructions in the “Resolution 2a: Use the aspnet_regiis utility” section). However, for administrators who do not want to run this utility, the following Windows PowerShell script may be used instead:

# Provisions the HKLM registry so that the specified user account can persist auto-generated machine keys.
function Provision-AutoGenKeys {
  param (
    [ValidateSet("2.0", "4.0")]
    [Parameter(Mandatory = $True)]
    [string] $frameworkVersion,
    [ValidateSet("32", "64")]
    [Parameter(Mandatory = $True)]
    [string] $architecture,
    [Parameter(Mandatory = $True)]
    [string] $upn
  process {
    # We require administrative permissions to continue.
    if (-Not (new-object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Error "This cmdlet requires Administrator permissions."
    # Open HKLM with an appropriate view into the registry
    if ($architecture -eq "32") {
        $regView = [Microsoft.Win32.RegistryView]::Registry32;
    } else {
        $regView = [Microsoft.Win32.RegistryView]::Registry64;
    $baseRegKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView)
    # Open ASP.NET base key
    if ($frameworkVersion -eq "2.0") {
        $expandedVersion = "2.0.50727.0"
    } else {
        $expandedVersion = "4.0.30319.0"
    $aspNetBaseKey = $baseRegKey.OpenSubKey("SOFTWARE\Microsoft\ASP.NET\$expandedVersion", $True)
    # Create AutoGenKeys subkey if it doesn't already exist
    $autoGenBaseKey = $aspNetBaseKey.OpenSubKey("AutoGenKeys", $True)
    if ($autoGenBaseKey -eq $null) {
        $autoGenBaseKey = $aspNetBaseKey.CreateSubKey("AutoGenKeys")
    # Get the SID for the user in question, which will allow us to get his AutoGenKeys subkey
    $sid = (New-Object System.Security.Principal.WindowsIdentity($upn)).User.Value
    # SYSTEM, ADMINISTRATORS, and the target SID get full access
    $regSec = New-Object System.Security.AccessControl.RegistrySecurity
    $userAutoGenKey = $autoGenBaseKey.OpenSubKey($sid, $True)
    if ($userAutoGenKey -eq $null) {
        # Subkey didn't exist; create and ACL appropriately
        $userAutoGenKey = $autoGenBaseKey.CreateSubKey($sid, [Microsoft.Win32.RegistryKeyPermissionCheck]::Default, $regSec)
    } else {
        # Subkey existed; make sure ACLs are correct

The following example shows how to provision the appropriate HKLM registry entries for an application pool that runs as the user, (this is the UPN of the Windows user account). This application pool is a 32-bit application pool that is running the CLR v2.0 (ASP.NET 2.0 or 3.5).

PS> Provision-AutoGenKeys -FrameworkVersion 2.0 -Architecture 32 -UPN ""

If the application pool instead is a 64-bit application pool that is running CLR v4.0 (ASP.NET 4.0 or 4.5), the command is as follows:

PS> Provision-AutoGenKeys -FrameworkVersion 4.0 -Architecture 64 -UPN ""

Even though the auto-generated keys are stored in HKLM, the registry subkey that holds each user account’s secret cryptographic material is added to an access control list (ACL) so that the cryptographic material cannot be read by other user accounts.

Appendix C: Encrypting the <machineKey> element in configuration files

Server administrators may not want highly sensitive information such as the <machineKey> key material to bein plaintext form in configuration files. If this is the case, administrators may decide to take advantage of a .NET Framework feature known as “protected configuration.” This feature lets you encrypt certain sections of the .config files. If the contents of these configuration files are ever disclosed, these sections’ contents will still remain secret.

You can find a brief overview of protected configuration on the MSDN website. It also contains a tutorial about how to protect the <connectionStrings> and <machineKey> elements of the Web.config file.

How to use devexpress custom theme

To apply a custom theme assembly to your project, do the following.
1. Add a reference to the assembly.
2. Assign the assembly name to the customThemeAssemblies option in the DevExpress web.config section.
3. Set a theme for a website via the DevExpress web.config section.

Note that you can specify a theme of a website using our DevExpress ASP.NET Project Wizard. In this case, all points above will be done automatically.

Select specific data type columns in database

SELECT AS ‘schema’, AS TableName, AS column_name, c.column_id,
SCHEMA_NAME(t.schema_id) AS DatatypeSchema, AS Datatypename
,t.is_user_defined, t.is_assembly_type
,c.is_nullable, c.max_length, c.PRECISION,
FROM sys.columns AS c
INNER JOIN sys.types AS t ON c.user_type_id=t.user_type_id
INNER JOIN sys.tables ts ON ts.OBJECT_ID = c.OBJECT_ID
INNER JOIN sys.schemas s ON s.schema_id = ts.schema_id
–WHERE = ‘datetime’
ORDER BY,, c.column_id

How to start using Entity Framework 6 with MySQL databases.

To start working with VS 2013 and EF 6

1- Install the MySQL for Visual Studio 1.1.1 or later
2- Install the Connector/Net 6.8.1 or later product.
3- To work with Database first please do the following
a. Add the reference for the new assembly called MySql.Data.Entity.EF6 and copy it to the bin forlder of your application.
b. Add the provider to your app/web config file on the providers for Entity Framework section with the following line: c. Before you run the Wizard compile your application so the new changes are applied.
4- To work with Model First please do the following
a. Add the reference for the new assembly called MySql.Data.Entity.EF6 and copy it to the bin forlder of your application.
b. Add the ADO.Net Entity Model new or existing.
c. Select the T4 template corresponding to MySQL (SSDLToMySQL)
d. Right click on the model and then select Generate Script to Create Database. (A MySQL script should be generated for you to create your database).

MySQL – Entity Framework: The value for column ‘IsPrimaryKey’ in table ‘TableDetails’ is DBNull

  • Open Services (services.msc) and restart MySQL57 service.
  • Execute the following commands in MySQL
    use YourDatabaseName;
    set global optimizer_switch=’derived_merge=OFF’;
  • Close .edmx and open again
  • Update the .edmx.

Wamp Server fix for windows 10

Praveen Puglia Web Developer

Make WAMP work on Windows 10 Technical Preview

Edit For Windows 10 Clean Install

If you have done clean installation of Windows 10, you may not have the Word Wide Web Publishing Service. In that case, simple WAMP/XAMPP installation should work fine.

If it doesn’t, try installing Visual C++ Redistributable and then re-install WAMP/XAMPP.

Shortly after the Windows 10 Technical Preview was made available, I was excited to try it out. And so I did. Everything was pretty cool until I tried starting WAMP, just to find out that it never starts( stuck at the yellow state in the notification area ). Whenever a new version of Windows comes up there is something or the other wrong with WAMP.

Skype wasn’t the problem this time because I didn’t have it installed. I also removed the modern Skype that comes installed with the Technical Preview. Neither it was the IPV6 issue which stopped it from working on Windows 8.

As I figured out, the port 80 was being used by a native service named – World Wide Web Publishing Service. Stopping it and restarting WAMP did the trick. Here’s how to locate and stop the service.[screenshot below]

  • Go to Start, type in services.msc
  • Scroll down in the Services window to find the World Wide Web Publishing Service.
  • Right click on it and select Stop.

Now restart WAMP and you should be good to go.

UPDATE – In the newer versions of Windows 10, you may not find the World Wide Web Publishing Service. If WAMP isn’t working even then, try the next approach.

Wamp on Windows 10 - Services

If you don’t want to do it that way though, you can change the port WAMP listens to.  Here’s how to do that.

  • Click on the WAMP icon in system tray.
  • Select Apache > httpd.conf
  • Search for Listen 80.
  • Change 80 to any other port number e.g. – 9080.
  • Restart WAMP.

This will work. However, you will now be required to use that port number everywhere for access. E.g – http://localhost:9080/ instead of just http://localhost/

EDIT(17th March, 2015)  : As mentioned here, I got to know that World Wide Web Publishing Service is associated with IIS. Windows 10 comes pre-loaded with it. You either uninstall it or disable it while running Apache. For the latest build of Windows 10, the following from the mentioned link should solve the issue.

As of the current build (9926) of W10 Preview, the only thing you need to do to get WAMPServer 2.5 running is install the MSVC2012 VC11 C/C++ runtime library as this is not delivered as part of the W10 install.

Know a different workaround? Let me know in comments.

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



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.

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.
    New ASP.NET Project dialog box
  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]" />    
    Security Note: 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_StartIdentityConfig.cs file by making the following changes highlighted in yellow:
    public class SmsService : IIdentityMessageService
          public Task SendAsync(IdentityMessage message)
          {        var Twilio = new TwilioRestClient(
            var result = Twilio.SendMessage(
               message.Destination, message.Body);
            // Status is one of Queued, Sending, Sent, Failed or null if the number is not valid
            // 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>
            <asp:PlaceHolder runat="server" ID="successMessage" Visible="false" ViewStateMode="Disabled">
                <p class="text-success"><%: SuccessMessage %></p>
        <div class="row">
            <div class="col-md-12">
                <div class="form-horizontal">
                    <h4>Change your account settings</h4>
                    <hr />
                    <dl class="dl-horizontal">
                            <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" />
                        <dt>External Logins:</dt>
                        <dd><%: LoginsCount %>
                            <asp:HyperLink NavigateUrl="/Account/ManageLogins" Text="[Manage]" runat="server" />
                            Phone Numbers can used as a second factor of verification in a two-factor authentication system.
                            See <a href="">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)
                           { %>
                            <asp:HyperLink NavigateUrl="/Account/AddPhoneNumber" runat="server" Text="[Add]" />
                        <% }
                           { %>
                            <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" />
                        <% } %>
                        --%><dt>Two-Factor Authentication:</dt>
                        <dd>                        <p>
                                There are no two-factor authentication providers configured. See <a href="">this article</a>
                                for details on setting up this ASP.NET application to support two-factor authentication.
                            </p><% if (TwoFactorEnabled)
                              { %> 
                            <asp:LinkButton Text="[Disable]" runat="server" CommandArgument="false" OnClick="TwoFactorDisable_Click" />                        --%><% }
                              { %> 
                            <asp:LinkButton Text="[Enable]" CommandArgument="true" OnClick="TwoFactorEnable_Click" runat="server" />                        --%><% } %>
  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;
                        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).
  13. Click Add next to Phone Number on the Manage Account page.
  14. Add the phone number where you (as the user) would like to receive SMS messages (text messages) and click 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.