Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

Implementing Two Factor Authentication in ASP.NET MVC with Google Authenticator

, 21 Aug 2013
How to add extra security to your MVC web application, using two factor authentication.
two-factor-noexe.zip
doc
src
packages
EntityFramework.5.0.0
Content
App.config.transform
Web.config.transform
EntityFramework.5.0.0.nupkg
EntityFramework.5.0.0.nuspec
lib
net40
net45
tools
EntityFramework.PS3.psd1
EntityFramework.psd1
EntityFramework.psm1
init.ps1
install.ps1
NUnit.2.6.1
lib
NUnit.2.6.1.nupkg
TwoFactor.sln.docstates.suo
TwoFactor.suo
TwoFactor.Tests
Properties
TwoFactorBruteForceAttempt
Properties
TwoFactorConsole
Properties
TwoFactorWeb
Content
themes
base
images
ui-bg_flat_0_aaaaaa_40x100.png
ui-bg_flat_75_ffffff_40x100.png
ui-bg_glass_55_fbf9ee_1x400.png
ui-bg_glass_65_ffffff_1x400.png
ui-bg_glass_75_dadada_1x400.png
ui-bg_glass_75_e6e6e6_1x400.png
ui-bg_glass_95_fef1ec_1x400.png
ui-bg_highlight-soft_75_cccccc_1x100.png
ui-icons_222222_256x240.png
ui-icons_2e83ff_256x240.png
ui-icons_454545_256x240.png
ui-icons_888888_256x240.png
ui-icons_cd0a0a_256x240.png
Controllers
Global.asax
Models
Properties
Scripts
Views
Account
Home
Shared
TwoFactor
Properties
two-factor.zip
lib
Gma.QrCodeNet.Encoding.dll
App.config.transform
Web.config.transform
EntityFramework.5.0.0.nupkg
EntityFramework.5.0.0.nuspec
EntityFramework.dll
EntityFramework.dll
EntityFramework.PowerShell.dll
EntityFramework.PowerShell.Utility.dll
EntityFramework.PS3.psd1
EntityFramework.psd1
EntityFramework.psm1
init.ps1
install.ps1
migrate.exe
nunit.framework.dll
NUnit.2.6.1.nupkg
TwoFactor.sln.docstates.suo
TwoFactor.suo
App_Data
ui-bg_flat_0_aaaaaa_40x100.png
ui-bg_flat_75_ffffff_40x100.png
ui-bg_glass_55_fbf9ee_1x400.png
ui-bg_glass_65_ffffff_1x400.png
ui-bg_glass_75_dadada_1x400.png
ui-bg_glass_75_e6e6e6_1x400.png
ui-bg_glass_95_fef1ec_1x400.png
ui-bg_highlight-soft_75_cccccc_1x100.png
ui-icons_222222_256x240.png
ui-icons_2e83ff_256x240.png
ui-icons_454545_256x240.png
ui-icons_888888_256x240.png
ui-icons_cd0a0a_256x240.png
Global.asax
using System;
using System.Runtime.Caching;

namespace TwoFactor
{
    public static class TimeBasedOneTimePassword
    {
        public static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        private static MemoryCache _cache;

        static TimeBasedOneTimePassword()
        {
            _cache = new MemoryCache("TimeBasedOneTimePassword");
        }

        public static string GetPassword(string secret)
        {
            return GetPassword(secret, GetCurrentCounter());
        }

        public static string GetPassword(string secret, DateTime epoch, int timeStep)
        {
            long counter = GetCurrentCounter(DateTime.UtcNow, epoch, timeStep);

            return GetPassword(secret, counter);
        }

        public static string GetPassword(string secret, DateTime now, DateTime epoch, int timeStep, int digits)
        {
            long counter = GetCurrentCounter(now, epoch, timeStep);

            return GetPassword(secret, counter, digits);
        }

        private static string GetPassword(string secret, long counter, int digits = 6)
        {
            return HashedOneTimePassword.GeneratePassword(secret, counter, digits);
        }

        private static long GetCurrentCounter()
        {
            return GetCurrentCounter(DateTime.UtcNow, UNIX_EPOCH, 30);
        }

        private static long GetCurrentCounter(DateTime now, DateTime epoch, int timeStep)
        {
            return (long)(now - epoch).TotalSeconds / timeStep;
        }

        public static bool IsValid(string secret, string password, int checkAdjacentIntervals = 1)
        {
            // Keeping a cache of the secret/password combinations that have been requested allows us to
            // make this a real one time use system. Once a secret/password combination has been tested,
            // it cannot be tested again until after it is no longer valid.
            // See http://tools.ietf.org/html/rfc6238#section-5.2 for more info.
            string cache_key = string.Format("{0}_{1}", secret, password);

            if (_cache.Contains(cache_key))
            {
                throw new OneTimePasswordException("You cannot use the same secret/iterationNumber combination more than once.");
            }

            _cache.Add(cache_key, cache_key, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(2) });

            if (password == GetPassword(secret))
                return true;

            for (int i = 1; i <= checkAdjacentIntervals; i++)
            {
                if (password == GetPassword(secret, GetCurrentCounter() + i))
                    return true;

                if (password == GetPassword(secret, GetCurrentCounter() - i))
                    return true;
            }

            return false;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Rick Bassham
Software Developer (Senior)
United States United States
I have been a software developer since 2005, focusing on .Net applications with MS SQL backends, and recently, C++ applications in Linux, Mac OS X, and Windows.
Follow on   Twitter   Google+

| Advertise | Privacy | Mobile
Web02 | 2.8.140922.1 | Last Updated 21 Aug 2013
Article Copyright 2012 by Rick Bassham
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid