Microsoft Identity 2 SignInManager никогда не возвращает VerificationRequired

#c# #asp.net-mvc-5 #asp.net-identity #asp.net-identity-2 #two-factor-authentication

#c# #asp.net-mvc-5 #asp.net-identity #asp.net-identity-2 #двухфакторная аутентификация

Вопрос:

Я пытаюсь добавить 2-факторную аутентификацию, используя pin-коды, отправленные по электронной почте. Шаг 2FA требуется пользователям только через 30 дней после того, как они в последний раз выполнили шаг 2FA.

Технология проекта — это ASP.NET MVC 5 с использованием EntityFramework 6 с пакетом NuGet версии 2.2.2 от Microsoft.AspNet.Пакеты идентификации (.Core, .EntityFramework, .Owin).

Я следовал советам из этого руководства, но интегрировал его в уже существующее решение моей компании: https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity

Мои проблемы

SignInManager никогда не возвращает Requireesverification при использовании PasswordSignIn Я предположил, что я должен ожидать этот статус входа, когда флаг «TwoFactorEnabled» установлен в true в базе данных для каждого пользователя. Я не уверен, стоит ли мне использовать это, если я только хочу, чтобы пользователи проходили проверку 2FA каждые 2 недели, поскольку у меня сложилось впечатление, что это потребует от пользователей проходить это каждый раз. Либо это, либо я должен использовать временную метку, установленную для пользователя, чтобы определить, пришло ли время повторно включить флаг «TwoFactorEnabled»?

Я не могу получить идентификатор проверенного пользователя, при проверке кода я думал, что после выполнения обычного PasswordSignIn сервер сможет получить идентификатор пользователя, например, при проверке правильности введенного pin-кода:

 [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> VerifyCode(string pin)
    {
        string userId = await SignInManager.GetVerifiedUserIdAsync().WithCurrentCulture();

        if (userId == null || String.IsNullOrEmpty(pin))
        {
            return View("Error");
        }

        var user = await UserManager.FindByIdAsync(userId);

        if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, "EmailCode", pin))
        {
            await SignInManager.SignInAsync(user, false, false);
            return RedirectToAction("Index", "Dashboards");
        }
        else
        {
            ModelState.AddModelError("", "Invalid code");
        }

        return View();
    }
  

Это немного сбивает с толку, поскольку большинство руководств очень старые и используют AuthenticationManager, которого я никогда раньше не видел.

Как мне запретить пользователям просто переходить на другие страницы с экрана проверки, если они уже «успешно вошли в систему с именем пользователя и паролем», смогут ли они обойти атрибут авторизации на этом этапе?

Что уже работает Я могу заставить UserManager отправить электронное письмо с токеном (pin-кодом), и оно будет успешно проверено, если я введу правильный идентификатор пользователя

Что я сделал У меня установлена стандартная настройка идентификации по умолчанию, поэтому мы в значительной степени используем стандартную конфигурацию, которая поставляется с новыми проектами MVC 5.

Запуск.Аутентификация:

 // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(10));

        // Enables the application to remember the second login verification factor such as phone or email.
        // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
        // This is similar to the RememberMe option when you log in.
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
  

Настройка поставщика 2FA в ApplicationUserManager:

         manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
        {
            Subject = "Security Code",
            BodyFormat = "Your security code is {0}"
        });

        manager.EmailService = new EmailService();
        //manager.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }

        return manager;
  

Заранее большое вам спасибо за любую помощь.

Комментарии:

1. Просто для подтверждения: устанавливаются ли файлы cookie в вашем браузере после первого уровня аутентификации? если приложение запускается в режиме разработки с использованием Google Chrome, у вас возникнут проблемы с установкой файлов cookie для домена localhost. У вас должен быть домен, контролируемый реестром. Это известная проблема безопасности в Chrome. попробуйте запустить и в других браузерах. Ссылка на проблему: bugs.chromium.org/p/chromium/issues/detail?id=56211

2. С другой стороны, вы можете установить для SSL приложения значение true в свойствах проекта и скопировать ссылку https в свои веб-свойства, чтобы обойти проблему и иметь возможность работать в Chrome

3. @bhanu.cs Что я делаю прямо сейчас, так это следующее: 1. Пользователь отправляет имя пользователя и пароль в AJAX-запросе в метод JsonResult, называемый login. 2. В этом методе я выполняю вход с помощью SignInManager. Я передаю SignInStatus плюс ApplicationUser методу, который включает SignInStatus.

4. 3. Если SignInStatus выполнен успешно, я теперь просто проверяю значение утверждения для пользователя, которое содержит дату. Если дата старше 30 дней, я отправляю им pin-код и перенаправляю на страницу VerifyCode, где они должны ввести pin-код. Также, если это было так, я не устанавливаю никаких переменных сеанса для user, но я уже выполнил PasswordSignIn, что, вероятно, проблематично? Я устанавливаю только одну переменную сеанса, содержащую их идентификатор пользователя, который я извлекаю, когда они отправляют свой pin-код.

5. О, я вижу, это умный обходной путь, о котором вы подумали. Но на шаге 3, поскольку включен 2FA, SignInManager возвращает статус RequireVerification, и файл cookie аутентификации не будет сгенерирован до завершения 2FA (я не уверен в этом, хотя генерируются другие промежуточные файлы cookie), что приведет к тому, что вы не будете аутентифицированы. Вы рассматривали этот случай? Короче говоря, если 2FA включен до тех пор, пока не будет предоставлен телефонный код или код электронной почты, вы можете не пройти аутентификацию.

Ответ №1:

Я думаю, что, возможно, я понял, в чем проблема. UserManager вернул 0 для GetValidTwoFactorProvidersAsync. Похоже, что это происходит, когда электронное письмо не подтверждено для пользователя. Я попробовал настройки электронной почты как подтвержденные для пользователя, и теперь возвращается requireresverification.

Когда возвращается Requireresverification, кажется, что пользователь не полностью вошел в систему, но я все еще могу получить идентификатор пользователя в последующих вызовах проверки кода, используя метод GetVerifiedUserId от SignInManager.

Итак, теперь, когда пользователь входит в систему, перед выполнением входа по паролю я просто выполняю проверку, чтобы узнать, когда пользователь в последний раз выполнял аутентификацию 2FA. Если прошло более 30 дней, я повторно включаю флаг 2FA в базе данных. Затем, когда будет выполнен ввод пароля, я получу requireesverification, как и ожидалось, и я могу продолжить остальную часть потока, поскольку в противном случае потребовалась бы любая стандартная проверка 2FA.