ASP.NET Core 3.1 - 2FA QR Code Generator
This article will demonstrate the implementation of qrcode.js to display the 2FA authenticator key in a QR Code formatted image. 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
 
The ASP.NET Core Identity UI displays the authenticator key as text. I implemented qrcode.js complete with CDN link and integrity check to display the key in a QR Code formatted image. Mobile authenticator apps can use the image and the phone's camera to input the key.
The EnableAuthenticator page from the scaffolded Identity UI uses a method, LoadSharedKeyAndQrCodeUriAsync to format the key and the QR Code Uri. Notice the page property, AuthenticatorUri is set with the QR Code Uri.
See EnableAuthenticator.cshtml.cs:
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
private async Task LoadSharedKeyAndQrCodeUriAsync(AppUser user)
{
    // Load the authenticator key & QR code URI to display on the form
    var unformattedKey = await _userService.GetAuthenticatorKeyAsync(user.Id);
    if (string.IsNullOrEmpty(unformattedKey))
    {
        await _userService.ResetAuthenticatorKeyAsync(user.Id);
        unformattedKey = await _userService.GetAuthenticatorKeyAsync(user.Id);
    }
    SharedKey = FormatKey(unformattedKey);
    var name = User.FindFirst(ClaimTypes.Name).Value;
    AuthenticatorUri = GenerateQrCodeUri(name, unformattedKey);
}
private string FormatKey(string unformattedKey)
{
    var result = new StringBuilder();
    int currentPosition = 0;
    while (currentPosition + 4 < unformattedKey.Length)
    {
        result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
        currentPosition += 4;
    }
    if (currentPosition < unformattedKey.Length)
        result.Append(unformattedKey.Substring(currentPosition));
    return result.ToString().ToLowerInvariant();
}
private string GenerateQrCodeUri(string name, string unformattedKey)
{
    return string.Format(AuthenticatorUriFormat, _urlEncoder.Encode("Users Without Identity"),
        _urlEncoder.Encode(name), unformattedKey);
}
    Add qrcode.js to libman.json. See ASP.NET Core 2.2 - Manage Client-Side Libraries
Edit libman.json:
{
  "destination": "wwwroot/lib/qrcode/",
  "files": [
    "qrcode.js",
    "qrcode.min.js"
  ],
  "library": "qrcodejs@1.0.0"
}
    Add the CDN link for qrcode.js to the EnableAuthenticator page.
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"
    asp-fallback-src="~/lib/qrcode/qrcode.min.js"
    asp-fallback-test="window.QRCode"
    integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA=="
    crossorigin="anonymous">
</script>
    The EnableAuthenticator page uses a div to display the image.
<div id="qrCode"></div>
Use JavaScript on the page to configure the size and load the image.
<script>
    new QRCode(document.getElementById("qrCode"),
        {
            text: "@@Html.Raw(Model.AuthenticatorUri)",
            width: 200,
            height: 200
        });
</script>
    Update 02/23/2021
I added the Enhanced User Series' article links.
Comments(0)