ASP.NET Core 3.1 - 2FA Authenticating
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 downloaded the ASP.NET Core 3.1 - Users Without Identity Project or 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
Creation Series
- ASP.NET Core 3.1 - Users Without Identity
- ASP.NET Core 3.1 - User Entity
- ASP.NET Core 3.1 - Password Hasher
- ASP.NET Core 3.1 - User Management
- ASP.NET Core 3.1 - Admin Role
- ASP.NET Core 3.1 - Cookie Validator
- ASP.NET Core 3.1 - Concurrency Conflicts
- ASP.NET Core 3.1 - Must Change Password
- ASP.NET Core 3.1 - User Database Service
- ASP.NET Core 3.1 - Rename Related Entities
2FA Series
- ASP.NET Core 3.1 - 2FA Without Identity
- ASP.NET Core 3.1 - 2FA User Tokens
- ASP.NET Core 3.1 - 2FA Cookie Schemes
- ASP.NET Core 3.1 - 2FA Authenticating
- ASP.NET Core 3.1 - 2FA Sign In Service
- ASP.NET Core 3.1 - 2FA QR Code Generator
- ASP.NET Core 3.1 - Admin 2FA Requirement
- ASP.NET Core 3.1 - 2FA Admin Override
Enhanced User Series
- ASP.NET Core 3.1 - Enhanced User Without Identity
- ASP.NET Core 3.1 - 2FA Recovery Codes
- ASP.NET Core 3.1 - Login Lockout
- ASP.NET Core 3.1 - Created And Last Login Date
- ASP.NET Core 3.1 - Security Stamp
- ASP.NET Core 3.1 - Token Service
- ASP.NET Core 3.1 - Confirmed Email Address
- ASP.NET Core 3.1 - Password Recovery
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[20]; using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(bytes); return Base32.ToBase32(bytes); } 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); if (BitConverter.IsLittleEndian) Array.Reverse(counter); 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
Update 02/23/2021
I added the Enhanced User Series' article links.
Comments(0)