在ASP.NET Core中启用并使用双因素认证

编程语言 2026-07-12

我想在一个ASP.NET Core项目中使用2FA。可以通过扫描QR码(图片“Activate 2FA”)在Google身份验证器中进行激活,然后我输入六位验证码就能工作。之后我从.NET Core应用程序中注销。

但当我尝试登录时,应用显示验证码不正确(图片“Invalid authenticator code”)。不过我确实从Google Authenticator应用中获取了验证码,我的手机和电脑的时间也自动设置为相同。

你有想法吗?谢谢

激活2FA

无效的身份验证代码

代码在 EnableAuthenticator.cshmtl.cs

using System;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using produit_chimiques.Areas.Identity.Data; // ← IMPORTANT : ton namespace ApplicationUser

namespace produit_chimiques.Areas.Identity.Pages.Account.Manage
{
    [Authorize]
    public class EnableAuthenticatorModel : PageModel
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly UrlEncoder _urlEncoder;

        private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";

        public EnableAuthenticatorModel(
            UserManager<ApplicationUser> userManager,
            UrlEncoder urlEncoder)
        {
            _userManager = userManager;
            _urlEncoder = urlEncoder;
        }

        [BindProperty]
        public InputModel Input { get; set; } = new InputModel();

        public string SharedKey { get; set; } = "";
        public string AuthenticatorUri { get; set; } = "";

        public class InputModel
        {
            public string Code { get; set; } = "";
        }

        public async Task<IActionResult> OnGetAsync()
        {
            var user = await _userManager.GetUserAsync(User);

            if (user == null)
            {
                return NotFound($"Impossible de charger l'utilisateur avec l'ID '{_userManager.GetUserId(User)}'.");
            }

            await LoadSharedKeyAndQrCodeUriAsync(user);
            return Page();
        }

        public async Task<IActionResult> OnPostAsync()
        {
            var user = await _userManager.GetUserAsync(User);

            if (user == null)
            {
                return NotFound($"Impossible de charger l'utilisateur avec l'ID '{_userManager.GetUserId(User)}'.");
            }

            if (!ModelState.IsValid)
            {
                await LoadSharedKeyAndQrCodeUriAsync(user);
                return Page();
            }

            // Nettoyer le code saisi
            var verificationCode = Input.Code.Replace(" ", string.Empty)
                                             .Replace("-", string.Empty);


        /////////////////////////////////////////////////////////////////////////////////////////
            // Juste AVANT VerifyTwoFactorTokenAsync, ajoute :
            var debugKey = await _userManager.GetAuthenticatorKeyAsync(user);
            Console.WriteLine($"DEBUG: Verification pour user {user.Id}, clé actuelle: {debugKey}");
            Console.WriteLine($"DEBUG: Code saisi: {verificationCode}");

            // Vérifier le code TOTP avec le provider Identity
            var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
                user,
                _userManager.Options.Tokens.AuthenticatorTokenProvider,
                verificationCode);

            if (!is2faTokenValid)
            {
                ModelState.AddModelError("Input.Code", "Code non valide. Vérifiez votre application d'authentification.");
                await LoadSharedKeyAndQrCodeUriAsync(user);
                return Page();
            }

            // Activer la 2FA pour cet utilisateur
            await _userManager.SetTwoFactorEnabledAsync(user, true);
            await _userManager.ResetAuthenticatorKeyAsync(user);

            return RedirectToPage("./TwoFactorAuthentication");
        }

        private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user)
        {
            // Générer/récupérer la clé TOTP
            var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);

            if (string.IsNullOrEmpty(unformattedKey))
            {
                await _userManager.ResetAuthenticatorKeyAsync(user);
                unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
            }

            SharedKey = FormatKey(unformattedKey);

            var email = await _userManager.GetEmailAsync(user) ?? 
                        await _userManager.GetUserNameAsync(user);

            AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey);
        }

        private string GenerateQrCodeUri(string email, string unformattedKey)
        {
            var issuer = "ProduitsChimiques";
            return string.Format(
                AuthenticatorUriFormat,
                _urlEncoder.Encode(issuer),
                _urlEncoder.Encode(email),
                unformattedKey);
        }

        private string FormatKey(string unformattedKey)
        {
            const int groupSize = 4;
            var result = new StringBuilder();
            var currentPosition = 0;

            while (currentPosition + groupSize < unformattedKey.Length)
            {
                result.Append(unformattedKey.AsSpan(currentPosition, groupSize));
                result.Append(' ');
                currentPosition += groupSize;
            }

            if (currentPosition < unformattedKey.Length)
            {
                result.Append(unformattedKey.AsSpan(currentPosition));
            }

            return result.ToString().ToLowerInvariant();
        }
    }
}

解决方案

在你的 OnPostAsync() 上,成功验证验证码后,你会调用 await _userManager.ResetAuthenticatorKeyAsync(user);,这会重置你用户的认证密钥,这意味着之前由你的用户扫描并验证过的QR码现在所使用的认证密钥与存储的不同。

因此,即使用户成功启用其2FA,你也改变了它的认证密钥,现在他的身份验证器代码就不对了。

总之,只需从你的 OnPostAsync() 中删除这一行,应该是这样的:

...
await _userManager.SetTwoFactorEnabledAsync(user, true);
//Removed await _userManager.ResetAuthenticatorKeyAsync(user);

return RedirectToPage("./TwoFactorAuthentication");
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章