< Summary

Information
Class: OzonOrdersWeb.Areas.Identity.Pages.Account.Manage.EnableAuthenticatorModel
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 65
Coverable lines: 65
Total lines: 188
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
get_SharedKey()100%210%
get_AuthenticatorUri()100%210%
get_RecoveryCodes()100%210%
get_StatusMessage()100%210%
get_Input()100%210%
get_Code()100%210%
OnGetAsync()0%620%
OnPostAsync()0%7280%
LoadSharedKeyAndQrCodeUriAsync()0%620%
FormatKey(...)0%2040%
GenerateQrCodeUri(...)100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3#nullable disable
 4
 5using System;
 6using System.ComponentModel.DataAnnotations;
 7using System.Globalization;
 8using System.Linq;
 9using System.Text;
 10using System.Text.Encodings.Web;
 11using System.Threading.Tasks;
 12using Microsoft.AspNetCore.Identity;
 13using Microsoft.AspNetCore.Mvc;
 14using Microsoft.AspNetCore.Mvc.RazorPages;
 15using Microsoft.Extensions.Logging;
 16
 17namespace OzonOrdersWeb.Areas.Identity.Pages.Account.Manage
 18{
 19    public class EnableAuthenticatorModel : PageModel
 20    {
 21        private readonly UserManager<IdentityUser> _userManager;
 22        private readonly ILogger<EnableAuthenticatorModel> _logger;
 23        private readonly UrlEncoder _urlEncoder;
 24
 25        private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
 26
 027        public EnableAuthenticatorModel(
 028            UserManager<IdentityUser> userManager,
 029            ILogger<EnableAuthenticatorModel> logger,
 030            UrlEncoder urlEncoder)
 31        {
 032            _userManager = userManager;
 033            _logger = logger;
 034            _urlEncoder = urlEncoder;
 035        }
 36
 37        /// <summary>
 38        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 39        ///     directly from your code. This API may change or be removed in future releases.
 40        /// </summary>
 041        public string SharedKey { get; set; }
 42
 43        /// <summary>
 44        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 45        ///     directly from your code. This API may change or be removed in future releases.
 46        /// </summary>
 047        public string AuthenticatorUri { get; set; }
 48
 49        /// <summary>
 50        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 51        ///     directly from your code. This API may change or be removed in future releases.
 52        /// </summary>
 53        [TempData]
 054        public string[] RecoveryCodes { get; set; }
 55
 56        /// <summary>
 57        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 58        ///     directly from your code. This API may change or be removed in future releases.
 59        /// </summary>
 60        [TempData]
 061        public string StatusMessage { get; set; }
 62
 63        /// <summary>
 64        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 65        ///     directly from your code. This API may change or be removed in future releases.
 66        /// </summary>
 67        [BindProperty]
 068        public InputModel Input { get; set; }
 69
 70        /// <summary>
 71        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 72        ///     directly from your code. This API may change or be removed in future releases.
 73        /// </summary>
 74        public class InputModel
 75        {
 76            /// <summary>
 77            ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
 78            ///     directly from your code. This API may change or be removed in future releases.
 79            /// </summary>
 80            [Required]
 81            [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLengt
 82            [DataType(DataType.Text)]
 83            [Display(Name = "Verification Code")]
 084            public string Code { get; set; }
 85        }
 86
 87        public async Task<IActionResult> OnGetAsync()
 88        {
 089            var user = await _userManager.GetUserAsync(User);
 090            if (user == null)
 91            {
 092                return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
 93            }
 94
 095            await LoadSharedKeyAndQrCodeUriAsync(user);
 96
 097            return Page();
 098        }
 99
 100        public async Task<IActionResult> OnPostAsync()
 101        {
 0102            var user = await _userManager.GetUserAsync(User);
 0103            if (user == null)
 104            {
 0105                return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
 106            }
 107
 0108            if (!ModelState.IsValid)
 109            {
 0110                await LoadSharedKeyAndQrCodeUriAsync(user);
 0111                return Page();
 112            }
 113
 114            // Strip spaces and hyphens
 0115            var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
 116
 0117            var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
 0118                user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
 119
 0120            if (!is2faTokenValid)
 121            {
 0122                ModelState.AddModelError("Input.Code", "Verification code is invalid.");
 0123                await LoadSharedKeyAndQrCodeUriAsync(user);
 0124                return Page();
 125            }
 126
 0127            await _userManager.SetTwoFactorEnabledAsync(user, true);
 0128            var userId = await _userManager.GetUserIdAsync(user);
 0129            _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId);
 130
 0131            StatusMessage = "Your authenticator app has been verified.";
 132
 0133            if (await _userManager.CountRecoveryCodesAsync(user) == 0)
 134            {
 0135                var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
 0136                RecoveryCodes = recoveryCodes.ToArray();
 0137                return RedirectToPage("./ShowRecoveryCodes");
 138            }
 139            else
 140            {
 0141                return RedirectToPage("./TwoFactorAuthentication");
 142            }
 0143        }
 144
 145        private async Task LoadSharedKeyAndQrCodeUriAsync(IdentityUser user)
 146        {
 147            // Load the authenticator key & QR code URI to display on the form
 0148            var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
 0149            if (string.IsNullOrEmpty(unformattedKey))
 150            {
 0151                await _userManager.ResetAuthenticatorKeyAsync(user);
 0152                unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
 153            }
 154
 0155            SharedKey = FormatKey(unformattedKey);
 156
 0157            var email = await _userManager.GetEmailAsync(user);
 0158            AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey);
 0159        }
 160
 161        private string FormatKey(string unformattedKey)
 162        {
 0163            var result = new StringBuilder();
 0164            int currentPosition = 0;
 0165            while (currentPosition + 4 < unformattedKey.Length)
 166            {
 0167                result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' ');
 0168                currentPosition += 4;
 169            }
 0170            if (currentPosition < unformattedKey.Length)
 171            {
 0172                result.Append(unformattedKey.AsSpan(currentPosition));
 173            }
 174
 0175            return result.ToString().ToLowerInvariant();
 176        }
 177
 178        private string GenerateQrCodeUri(string email, string unformattedKey)
 179        {
 0180            return string.Format(
 0181                CultureInfo.InvariantCulture,
 0182                AuthenticatorUriFormat,
 0183                _urlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"),
 0184                _urlEncoder.Encode(email),
 0185                unformattedKey);
 186        }
 187    }
 188}