Exercise - Configure multifactor authentication
In the previous unit, you learned how ASP.NET Core Identity implements time-based one-time password (TOTP) for multifactor authentication (MFA). In this unit, you customize the existing Configure authenticator app form to provide a QR code that contains the registration key.
Generating QR codes
Multiple strategies exist for generating the QR code. An example in the documentation uses a client-side JavaScript library. In this unit, however, a third-party NuGet package is used to generate the QR code with C# on the server. The resulting QR code image is injected into an HTML placeholder element as a base-64 encoded string.
Add a QR code service
Let's build everything you need to generate QR codes on the Configure authenticator app form.
In the terminal pane, install the
QRCoder
NuGet package:dotnet add package QRCoder --version 1.6.0
In the Explorer pane, right-click on the Services folder and add a new file named QRCodeService.cs. Add the following code:
using QRCoder; namespace RazorPagesPizza.Services; public class QRCodeService { private readonly QRCodeGenerator _generator; public QRCodeService(QRCodeGenerator generator) { _generator = generator; } public string GetQRCodeAsBase64(string textToEncode) { QRCodeData qrCodeData = _generator.CreateQrCode(textToEncode, QRCodeGenerator.ECCLevel.Q); var qrCode = new PngByteQRCode(qrCodeData); return Convert.ToBase64String(qrCode.GetGraphic(4)); } }
The preceding code:
- Uses constructor injection to gain access to an instance of the library's
QRCodeGenerator
class. - Exposes the
GetQRCodeAsBase64
method to return the base-64 encoded string. The integer value passed toGetGraphic
determines the QR code dimensions. In this case, the generated QR code is composed of blocks sized four pixels squared.
- Uses constructor injection to gain access to an instance of the library's
In Program.cs, add the highlighted lines:
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using RazorPagesPizza.Areas.Identity.Data; using Microsoft.AspNetCore.Identity.UI.Services; using RazorPagesPizza.Services; using QRCoder; var builder = WebApplication.CreateBuilder(args); var connectionString = builder.Configuration.GetConnectionString("RazorPagesPizzaAuthConnection"); builder.Services.AddDbContext<RazorPagesPizzaAuth>(options => options.UseSqlServer(connectionString)); builder.Services.AddDefaultIdentity<RazorPagesPizzaUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<RazorPagesPizzaAuth>(); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddTransient<IEmailSender, EmailSender>(); builder.Services.AddSingleton(new QRCodeService(new QRCodeGenerator())); var app = builder.Build();
QRCodeService
is registered as a singleton service in the IoC container within Program.cs.
Customize multifactor authentication
Now that you can generate QR codes, you can embed a QR code into the Configure authenticator app form.
Open Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs and make the following changes:
To store the QR code's base-64 string representation, add the following property to the
EnableAuthenticatorModel
class :public class EnableAuthenticatorModel : PageModel { private readonly UserManager<RazorPagesPizzaUser> _userManager; private readonly ILogger<EnableAuthenticatorModel> _logger; private readonly UrlEncoder _urlEncoder; public string QrCodeAsBase64 { get; set; }
Incorporate the highlighted changes in the
OnGetAsync
page handler:public async Task<IActionResult> OnGetAsync([FromServices] QRCodeService qrCodeService) { var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } await LoadSharedKeyAndQrCodeUriAsync(user); QrCodeAsBase64 = qrCodeService.GetQRCodeAsBase64(AuthenticatorUri); return Page(); }
In the preceding page handler, parameter injection provides a reference to the
QRCodeService
singleton service.To resolve the reference to
QRCodeService
, add the followingusing
statement to the top of the file. Save your changes.using RazorPagesPizza.Services;
Incorporate the highlighted change to the
GenerateQrCodeUri
method.private string GenerateQrCodeUri(string email, string unformattedKey) { return string.Format( CultureInfo.InvariantCulture, AuthenticatorUriFormat, _urlEncoder.Encode("RazorPagesPizza"), _urlEncoder.Encode(email), unformattedKey); }
This sets the display name for the key in your TOTP app.
In Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml, make the following highlighted changes and save:
<li> <p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p> <div class="alert alert-info">Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable QR code generation</a>.</div> <div id="qrCode"> <img alt="embedded QR code" src="data:image/png;base64,@Model.QrCodeAsBase64" /> </div> <div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div> </li>
The preceding markup embeds the base-64 encoded image in the page.
Test multifactor authentication
You made all the changes needed for a QR code on the Configure authenticator app form. Now you can easily test the MFA functionality.
Ensure you saved all your changes.
Build and run the app with
dotnet run
.Navigate to the site and sign in with either registered user, if you aren't already signed in. Select Hello, [First name] [Last name]! link to navigate to the profile management page, and then select Two-factor authentication.
Select the Add authenticator app button.
Follow the on-screen instructions to register and verify your authenticator app for this user.
For example, using Microsoft Authenticator on Android, follow these steps to add the account to the app:
- Open the Microsoft Authenticator app.
- Select the kebab menu (vertical ellipsis) in the upper right.
- Select Add account.
- Select Other account (Google, Facebook, etc.).
- Scan the QR code as indicated.
Enter the verification code provided by your TOTP app in the Verification Code text box.
Select Verify.
Upon successful verification, the page displays a Your authenticator app has been verified banner and some recovery codes.
In the SQL Server tab in VS Code, right-click the RazorPagesPizza database and select New query. Enter the following query and press Ctrl+Shift+E to run it.
SELECT FirstName, LastName, Email, TwoFactorEnabled FROM dbo.AspNetUsers
For the signed in user, the output shows that the
TwoFactorEnabled
column is equal to1
. Because multifactor authentication isn't enabled for the other registered user, the record's column value is0
.In the web app, select Logout, and then sign in again with the same user.
Enter the verification code from the TOTP authenticator app in the Authenticator code text box. Select the Log in button.
Select Hello, [First name] [Last name]!. Then, select the Two-factor authentication tab.
Because Microsoft Authenticator is set up, the following buttons appear:
- Disable 2FA
- Reset recovery codes
- Set up authenticator app
- Reset authenticator app
In the terminal pane in VS Code, press Ctrl+C to stop the app.
Summary
In this unit, you added the ability to generate a QR code to the Configure authenticator app form. In the next unit, you can learn about using Identity to store claims and apply authorization policies.