Using Google Authenticator with ASP.NET Identity

In my previous 2 blog posts I showed you first how to use the 2 Factor Authentication in ASP.NET Identity 2.0 with email and I then followed it up showing you how easy it is to use it with SMS using the Twilio API.  Well, the next logical step is obviously to show you how to use 2 Factor Authentication in ASP.NET Identity 2.o with Google Authenticator.  This blog post is going to show you exactly how to do it.

While I am showing you how to use Google Authenticator in this blog post, it will work with any application which can issue TOTP tokens.  First thing to do before we start off is to install one of the following applications on your mobile phone:

I also strongly suggest your read this Beginner’s Guide to TOTP to understand how 2 factor authentication using TOTP works.

Create the basic project

For this application we will use a normal ASP.NET MVC Application with the ASP.NET Identity 2.o Samples package installed.  So first off, create a new ASP.NET MVC Application.

Next, make sure that you update all the Nuget packages for the solution to ensure that you have ASP.NET Identity 2.0 installed.

Install the ASP.NET Identity 2.0 Samples package by searching NuGet for the package named “Microsoft.AspNet.Identity.Samples”. At the time of writing this blog post the packages is still in beta, so make sure you select the Include Prerelease option.

Finally, we are going to need at open source package called OTP Sharp for validating the TOTP code, so go ahead and install that as well.

Implementing the Google Authenticator token provider

Our standard application at this point has 2 token providers registered, namely the PhoneNumberTokenProvider and EmailTokenProvider.  These are used to send 2 factor authentication tokens to a user’s cellphone number and email address respectively.  The samples package automatically created the code which enable these 2 providers and it can be found in the App_Start\IdentityConfig.cs class:

manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
    MessageFormat = "Your security code is: {0}"
});

manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
{
    Subject = "SecurityCode",
    BodyFormat = "Your security code is {0}"
});

Both of these providers ultimately implement the IUserTokenProvider class which is defined as follows:

public interface IUserTokenProvider<TUser, TKey> where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
/// <summary>
/// Generate a token for a user with a specific purpose
/// 
/// </summary>
/// <param name="purpose"/><param name="manager"/><param name="user"/>
/// <returns/>
Task<string> GenerateAsync(string purpose, UserManager<TUser, TKey> manager, TUser user);

/// <summary>
/// Validate a token for a user with a specific purpose
/// 
/// </summary>
/// <param name="purpose"/><param name="token"/><param name="manager"/><param name="user"/>
/// <returns/>
Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser, TKey> manager, TUser user);

/// <summary>
/// Notifies the user that a token has been generated, for example an email or sms could be sent, or
///                 this can be a no-op
/// 
/// </summary>
/// <param name="token"/><param name="manager"/><param name="user"/>
/// <returns/>
Task NotifyAsync(string token, UserManager<TUser, TKey> manager, TUser user);

/// <summary>
/// Returns true if provider can be used for this user, i.e. could require a user to have an email
/// 
/// </summary>
/// <param name="manager"/><param name="user"/>
/// <returns/>
Task<bool> IsValidProviderForUserAsync(UserManager<TUser, TKey> manager, TUser user);
}

All that we need to do therefore is to create a class which implements this interface and validates a token generated by the Google Authenticator application.  First off however we need to extend our ApplicationUser class to add 2 extra properties (and therefore 2 extra columns to the underlying database table).  The first will be a flag which indicates whether a user has enabled 2 factor authentication via Google Authenticator.  The second will be used to store the secret key which is associated with each user and used to validate to TOTP code for that specific user.

public class ApplicationUser : IdentityUser 
{
    public bool IsGoogleAuthenticatorEnabled { get; set; }

    public string GoogleAuthenticatorSecretKey { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

The actual implementation of our GoogleAuthenticatorTokenProvider class is quite simple:

public class GoogleAuthenticatorTokenProvider : IUserTokenProvider<ApplicationUser, string>
{
    public Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser, string> manager, ApplicationUser user)
    {
        return Task.FromResult((string)null);
    }

    public Task<bool> ValidateAsync(string purpose, string token, UserManager<ApplicationUser, string> manager, ApplicationUser user)
    {
        long timeStepMatched = 0;

        var otp = new Totp(Base32Encoder.Decode(user.GoogleAuthenticatorSecretKey));
        bool valid = otp.VerifyTotp(token, out timeStepMatched, new VerificationWindow(2, 2));

        return Task.FromResult(valid);
    }

    public Task NotifyAsync(string token, UserManager<ApplicationUser, string> manager, ApplicationUser user)
    {
        return Task.FromResult(true);
    }

    public Task<bool> IsValidProviderForUserAsync(UserManager<ApplicationUser, string> manager, ApplicationUser user)
    {
        return Task.FromResult(user.IsGoogleAuthenticatorEnabled);
    }
}

Since our provider does not actually generate a code, we can leave the implementation for the GenerateAsync method to simply return a null value.  The same goes for the NotifyAsync method as we do not need to send a notification to the user of the code.  The IsValidProviderForUserAsync method indicates whether this user can use Google Authenticator as a 2 factor authentication mechanism during the login process.  We therefore check the IsGoogleAuthenticator flag on the ApplicationUser class to see whether this user has enabled Google Authenticator. This flag will be set when the user activates Google Authenticator for their account and is covered in the next section.

The final bit is to implement the ValidateAsync method which validates the code entered by the user.  We create a new instance of the Totp class and pass in the user’s secret key in the constructor.  This secret key is generated when the user activates Google Authenticator for their account (discussed in the next section below) and is stored in the database as a base32 encoded string so we need to decode it first.  Finally we call the VerifyTotp method to check whether the token entered by the user is valid.

We have completed the implementation of the GoogleAuthenticatorTokenProvider class and need to register it alongside the email and Sms providers in the ApplicationUserManager class which is found in App_Start\IdentityConfig.cs.

manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
    MessageFormat = "Your security code is: {0}"
}
);
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
{
    Subject = "SecurityCode",
    BodyFormat = "Your security code is {0}"
});

manager.RegisterTwoFactorProvider("GoogleAuthenticator", new GoogleAuthenticatorTokenProvider());

Allowing the user to activate Google Authenticator for their account

All of the above is pretty useless unless the user has some way in which they can activate Google Authenticator for their account.  We will need to extend to user management process to allow for a user to activate Google Authenticator for their account and register our application in Google Authenticator.  On the high level the process is as follows:

  1. User chooses to activate Google Authenticator
  2. A screen is displayed with a QR Code which the user scans with the Google Authenticator application.  This barcode contains the name of the application, the user’s username and most importantly the user’s secret key.  It is important that Google Authenticator and our application share the same secret key for the user otherwise the security codes will not be valid.
  3. After the user has scanned the QR code we prompt them to enter the code generated by Google Authenticator for the application
  4. If the code is valid we can be certain that the user has added our application correctly to Google Authenticator.  We set the IsGoogleAuthenticatorEnabled flag for the user and save the user’s secret key as a base32 encoded string in the database.

First up is to extend the IndexViewModel class to add a property to indicate whether the user has Google Authenticator enabled.  The IndexViewModel class is used in the user management view.

public class IndexViewModel 
{
    public bool HasPassword { get; set; }
    public IList<UserLoginInfo> Logins { get; set; }
    public string PhoneNumber { get; set; }
    public bool TwoFactor { get; set; }
    public bool BrowserRemembered { get; set; }
    public bool IsGoogleAuthenticatorEnabled { get; set; }
}

Edit the user management index view (located at Views\Manage\Index.cshtml) and add an area which displays whether the user has Google Authenticator enabled and allow them to enable it if it is not, and disabled it if it is.

<p>
    Google Authenticator Enabled: @(Model.IsGoogleAuthenticatorEnabled)
    @if (Model.IsGoogleAuthenticatorEnabled)
    {
        @Html.ActionLink("[Disable]", "DisableGoogleAuthenticator")
    }
    else
    {
        @Html.ActionLink("[Enable]", "EnableGoogleAuthenticator")
    }
</p>

Add the two actions specified above to the ManageController class.  First up the code which disables Google Authenticator:

public async Task<ActionResult> DisableGoogleAuthenticator()
{
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
    if (user != null)
    {
        user.IsGoogleAuthenticatorEnabled = false;
        user.GoogleAuthenticatorSecretKey = null;

        await UserManager.UpdateAsync(user);

        await SignInAsync(user, isPersistent: false);
    }
    return RedirectToAction("Index", "Manage");
}

All this method does is to find the user and clear the Google Authenticator flag and secret key.  The code to enable Google Authenticator is a bit more involved:

[HttpGet]
public async Task<ActionResult> EnableGoogleAuthenticator()
{
    byte[] secretKey = KeyGeneration.GenerateRandomKey(20);
    string userName = User.Identity.GetUserName();
    string barcodeUrl = KeyUrl.GetTotpUrl(secretKey, userName) + "&issuer=MySuperApplication";

    var model = new GoogleAuthenticatorViewModel
    {
        SecretKey = Base32Encoder.Encode(secretKey),
        BarcodeUrl = HttpUtility.UrlEncode(barcodeUrl)
    };

    return View(model); 
}

We generate a random secret key for the user using one of the Utility classes which was installed as part of the OTP Sharp library.  We also generate the URL for the TOTP QR Code using another of the utility classes of OTP Sharp.  This is then packaged into a view model and passed to the view:

<div class="row">
    <div class="col-md-8">
        <h3>1\. Add MySuperApplication to Google Authenticator</h3>
        <p>Open Google Authenticator and add MySuperApplication by scanning the QR Code to the right.</p>
        <h3>2\. Enter the 6 digit code that Google Authenticator generates</h3>
        <p>
            Verify that MySuperApplication is added correctly in Google Authenticator by entering the 6 digit code which
            Google Authenticator generates for MySuperApplication below, and then click Enable.
        </p>
        @using (Html.BeginForm("EnableGoogleAuthenticator", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
        {
            @Html.AntiForgeryToken()   
            @Html.ValidationSummary(false)
            @Html.HiddenFor(m => m.SecretKey)
            @Html.HiddenFor(m => m.BarcodeUrl)
            <div class="form-group">
                @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.TextBoxFor(m => m.Code, new { @class = "form-control" })
                </div>
            </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" class="btn btn-default" value="Enable" />
                </div>
            </div>
        }
    </div>
    <div class="col-md-4">
        <img src="http://qrcode.kaywa.com/img.php?s=4&d=@(Model.BarcodeUrl)"/>
    </div>
</div>

The view displays the QR code and allows the user to scan it using Google Authenticator.  The user must then enter the code generated by Google Authenticator so we can validate that the user has added the application correctly when they click the Enable button.  The validation and enablement code can also be found in the ManageController class:

[HttpPost]
public async Task<ActionResult> EnableGoogleAuthenticator(GoogleAuthenticatorViewModel model)
{
    if (ModelState.IsValid)
    {
        byte[] secretKey = Base32Encoder.Decode(model.SecretKey);

        long timeStepMatched = 0;
        var otp = new Totp(secretKey);
        if (otp.VerifyTotp(model.Code, out timeStepMatched, new VerificationWindow(2, 2)))
        {
            var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
            user.IsGoogleAuthenticatorEnabled = true;
            user.GoogleAuthenticatorSecretKey = model.SecretKey;
            await UserManager.UpdateAsync(user);

            return RedirectToAction("Index", "Manage");
        }
        else
            ModelState.AddModelError("Code", "The Code is not valid");
    }

    return View(model);
}

This code is once again fairly straight forward.  We validate that the code which the user entered and if it is correct we set the Google Authenticator flag and store the secret key for the user.  We then pass the user back to the user management screen.  If the code is invalid we display an error message to the user indicating that an incorrect code was entered.

Running the application

To test this out go through the process of registering a user and enabling 2 factor authentication for the user on the user management screen.  Next head on back to the user management screen and click the Enable link next the the Google Authenticator Enabled label:

You will be presented with a screen which displays the barcode which you can scan from you Google Authenticator application:

Open Google Authenticator on your mobile phone and click the edit icon:

Click on the Add button:

Select Scan barcode and scan the QR Code:

You will see MySuperApplication added to your list of applications in Google Authenticator.  Google Authenticator will display a new generated code for it:

Enter the code in your browser windows and click Enable:

Now log out of the application and log in again.  You will be prompted to select the authentication provider you want to use.  Select GoogleAuthenticator and click Submit:

Enter the authentication code supplied by Google Authenticator:

You will now be logged in to the application.

Conclusion

This blog post took you through the basic steps of creating a Google Authenticator provider to use with ASP.NET Identity 2.0.  If you want to use this in a production application there are a few issues your will need to address.  The user flow during the login process for example is based on the fact that it assumes a code is sent to the user.  The title of the one screen above is for example “Send Verification Code” - in the case of Google Authenticator that does not make sense.  I suggest you look at the 2 factor authentication workflows for applications such as Github or Google and design something similar.  The code provided by Microsoft in the ASP.NET Identity Samples package is really just for demonstration purposes and needs a bit of cleanup if you want to use it in production.

If you have any issues or questions comment below.

Source code can be found at https://github.com/beabigrockstar/ASPNETIdentity_GoogleAuthenticator/