This article will describe the implementation of functions to generate 2FA keys and verify the code
provided by the authenticator app. I will assume you have created a new ASP.NET Core 3.1 Razor
Pages project. See
Tutorial: Get started with Razor Pages in ASP.NET Core.
You should review the earlier articles of the Users Without Identity Project series.
Users Without Identity Project and Article Series
Access to the research project source code may be purchased on KenHaggerty.Com at
Manage > Assets.
A project which implements users without Identity has been published to
I enjoy writing these articles. It often enhances and clarifies my coding. The research
project is a result of a lot of refactoring and hopefully provides logical segues for the articles.
Thank you for supporting my efforts.
I developed a TwoFactorAuth class with functions to generate an
authenticator key and verify the code provided by the authenticator app. The authenticator key must use
base 32 characters. TwoFactorAuth.cs depends on the Identity Base32 class. See
GitHub - Identity/ src/ Core/ Base32.cs
Authenticator apps generate a time-based one-time password (TOTP). The TOTP is a 6 digit code
which is a hash of the key and the current time. To verify the TOTP you need to generate a code with the
same algorithm and compare.
Services > TwoFactorAuth.cs:
public static class TwoFactorAuth
public static string GetAuthenticatorKey()
// Generates a new base32 encoded 160-bit security secret (size of SHA1 hash).
byte bytes = new byte;
using (var rng = RandomNumberGenerator.Create())
public static int GetAuthenticatorCode(string key)
var unixTimestamp = (DateTime.UtcNow.Ticks - 621355968000000000L) / 10000000L;
var window = unixTimestamp / (long)30;
var keyBytes = Base32.FromBase32(key);
var counter = BitConverter.GetBytes(window);
var hmac = new HMACSHA1(keyBytes);
var hash = hmac.ComputeHash(counter);
var offset = hash[^1] & 0xf;
// Convert the 4 bytes into an integer, ignoring the sign.
var binary =
((hash[offset] & 0x7f) << 24)
| (hash[offset + 1] << 16)
| (hash[offset + 2] << 8)
| (hash[offset + 3]);
return binary % (int)Math.Pow(10, 6);
public static long GetCurrentCounter()
DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return 30 - (long)(DateTime.UtcNow - unixEpoch).TotalSeconds % 30;
I developed a demo which simulates an authenticator app. It allows you to scan an anonymous key into
your app and compare verification codes and time remaining. See:
Ken's Demo Site - Authenticator App
I updated the series article links.
I added the 2FA Admin Override article link.