Многофакторная проверка подлинности в ASP.NET Core

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 8 этой статьи.

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется злоумышленником.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Подключение
  • Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

MFA, 2FA

Многофакторная проверка подлинности требует по крайней мере двух или более типов подтверждения для удостоверения, такого как то, что вы знаете, то, что у вас есть, или биография метрики для проверки подлинности пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения удостоверения.

2FA поддерживается по умолчанию при использовании ASP.NET Core Identity. Чтобы включить или отключить 2FA для определенного пользователя, задайте IdentityUser<TKey>.TwoFactorEnabled свойство. Пользовательский интерфейс по умолчанию ASP.NET Core Identity включает страницы для настройки 2FA.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP поддерживается по умолчанию при использовании ASP.NET Core Identity. Этот подход можно использовать вместе с любым приложением для проверки подлинности, в том числе:

  • Microsoft Authenticator
  • Google Authenticator

Сведения о реализации см. в разделе "Включение создания QR-кода" для приложений проверки подлинности TOTP в ASP.NET Core.

Чтобы отключить поддержку MFA TOTP, настройте проверку подлинности, используя AddIdentity вместо AddDefaultIdentityнее. AddDefaultIdentity вызывает внутренние вызовы AddDefaultTokenProviders , которые регистрируют несколько поставщиков маркеров, включая один для MFA TOTP. Чтобы зарегистрировать только конкретных поставщиков маркеров, вызовите AddTokenProvider для каждого обязательного поставщика. Дополнительные сведения о доступных поставщиках маркеров см. в источнике AddDefaultTokenProviders на GitHub.

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и IdentityRazor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в удостоверение после успешного входа.

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(
        Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
		options.SignIn.RequireConfirmedAccount = false)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
    AdditionalUserClaimsPrincipalFactory>();

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));

builder.Services.AddRazorPages();

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если удостоверение вошедло в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации MFA.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet удостоверение используется для доступа к утверждениям пользователя. Утверждение amr проверка для значенияmfa. Если удостоверение отсутствует это утверждение или находится false, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена при запуске. Для политики требуется amr утверждение со значением mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню Администратор с предупреждением:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Если удостоверение вошедло в систему с помощью MFA, Администратор меню отображается без предупреждения подсказки. При входе пользователя без MFA отображается меню Администратор (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Если пользователь входит в систему без MFA, отображается предупреждение:

проверка подлинности MFA Администратор istrator

Пользователь перенаправляется в режим включения MFA при нажатии ссылки Администратор:

Администратор istrator активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Подключение

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Подключение для работы.

Клиент OpenID Подключение ASP.NET Core

Клиентское приложение ASP.NET Core Razor Pages OpenID Подключение использует AddOpenIdConnect метод для входа на сервер OpenID Подключение. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

build.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "<OpenID Connect server URL>";
	options.RequireHttpsMetadata = true;
	options.ClientId = "<OpenID Connect client ID>";
	options.ClientSecret = "<>";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
	options.AdditionalAuthorizationParameters.Add("acr_values", "mfa");
});

Пример сервера Подключение Duende IdentityServer с ASP.NET CoreIdentity

На сервере OpenID Подключение, который реализуется с помощью ASP.NET Core Identity с Razor Pages, создается новая страница с именемErrorEnable2FA.cshtml. Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>

В методе LoginIIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Подключение. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. По мере отправки этого клиента с mfa набором это может быть проверка.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

Fido2Store используется для проверка, если пользователь активировал MFA с помощью пользовательского поставщика токенов FIDO2.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее Подключение OpenID для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

РеализуетсяAuthorizationHandler, который будет использовать amr утверждение и проверка для значенияmfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа проверки подлинности удостоверения и реализации сервера OpenID Подключение.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Подключение можно реализовать с помощью сервера Duende Identity Server с ASP.NET CoreIdentity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Подключение или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

В файле AddOpenIdConnect программы метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, который используется для проверка amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Подключение также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Дополнительные ресурсы

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется злоумышленником.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Подключение
  • Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

MFA, 2FA

Многофакторная проверка подлинности требует по крайней мере двух или более типов подтверждения для удостоверения, такого как то, что вы знаете, то, что у вас есть, или биография метрики для проверки подлинности пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения удостоверения.

2FA поддерживается по умолчанию при использовании ASP.NET Core Identity. Чтобы включить или отключить 2FA для определенного пользователя, задайте IdentityUser<TKey>.TwoFactorEnabled свойство. Пользовательский интерфейс по умолчанию ASP.NET Core Identity включает страницы для настройки 2FA.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP поддерживается по умолчанию при использовании ASP.NET Core Identity. Этот подход можно использовать вместе с любым приложением для проверки подлинности, в том числе:

  • Microsoft Authenticator
  • Google Authenticator

Сведения о реализации см. в разделе "Включение создания QR-кода" для приложений проверки подлинности TOTP в ASP.NET Core.

Чтобы отключить поддержку MFA TOTP, настройте проверку подлинности, используя AddIdentity вместо AddDefaultIdentityнее. AddDefaultIdentity вызывает внутренние вызовы AddDefaultTokenProviders , которые регистрируют несколько поставщиков маркеров, включая один для MFA TOTP. Чтобы зарегистрировать только конкретных поставщиков маркеров, вызовите AddTokenProvider для каждого обязательного поставщика. Дополнительные сведения о доступных поставщиках маркеров см. в источнике AddDefaultTokenProviders на GitHub.

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и IdentityRazor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в удостоверение после успешного входа.

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(
        Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
		options.SignIn.RequireConfirmedAccount = false)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
    AdditionalUserClaimsPrincipalFactory>();

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));

builder.Services.AddRazorPages();

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если удостоверение вошедло в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации MFA.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet удостоверение используется для доступа к утверждениям пользователя. Утверждение amr проверка для значенияmfa. Если удостоверение отсутствует это утверждение или находится false, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена при запуске. Для политики требуется amr утверждение со значением mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню Администратор с предупреждением:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Если удостоверение вошедло в систему с помощью MFA, Администратор меню отображается без предупреждения подсказки. При входе пользователя без MFA отображается меню Администратор (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Если пользователь входит в систему без MFA, отображается предупреждение:

проверка подлинности MFA Администратор istrator

Пользователь перенаправляется в режим включения MFA при нажатии ссылки Администратор:

Администратор istrator активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Подключение

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Подключение для работы.

Клиент OpenID Подключение ASP.NET Core

Клиентское приложение ASP.NET Core Razor Pages OpenID Подключение использует AddOpenIdConnect метод для входа на сервер OpenID Подключение. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

build.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "<OpenID Connect server URL>";
	options.RequireHttpsMetadata = true;
	options.ClientId = "<OpenID Connect client ID>";
	options.ClientSecret = "<>";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
	options.Events = new OpenIdConnectEvents
	{
		OnRedirectToIdentityProvider = context =>
		{
			context.ProtocolMessage.SetParameter("acr_values", "mfa");
			return Task.FromResult(0);
		}
	};
});

Пример сервера Подключение Duende IdentityServer с ASP.NET CoreIdentity

На сервере OpenID Подключение, который реализуется с помощью ASP.NET Core Identity с Razor Pages, создается новая страница с именемErrorEnable2FA.cshtml. Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>

В методе LoginIIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Подключение. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. По мере отправки этого клиента с mfa набором это может быть проверка.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

Fido2Store используется для проверка, если пользователь активировал MFA с помощью пользовательского поставщика токенов FIDO2.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее Подключение OpenID для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

РеализуетсяAuthorizationHandler, который будет использовать amr утверждение и проверка для значенияmfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа проверки подлинности удостоверения и реализации сервера OpenID Подключение.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Подключение можно реализовать с помощью сервера Duende Identity Server с ASP.NET CoreIdentity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Подключение или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

В файле AddOpenIdConnect программы метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, который используется для проверка amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Подключение также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Дополнительные ресурсы

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется злоумышленником.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Подключение
  • Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

MFA, 2FA

Многофакторная проверка подлинности требует по крайней мере двух или более типов подтверждения для удостоверения, такого как то, что вы знаете, то, что у вас есть, или биография метрики для проверки подлинности пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения удостоверения.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP — это поддерживаемая реализация с помощью ASP.NET Core Identity. Это можно использовать вместе с любым соответствующим приложением проверки подлинности, включая:

  • Приложение Microsoft Authenticator
  • Приложение Google Authenticator

Дополнительные сведения о реализации см. по следующей ссылке:

Включение создания QR-кодов для приложений проверки подлинности TOTP в ASP.NET Core

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и IdentityRazor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в удостоверение после успешного входа.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<IdentityUser, IdentityRole>(
            options => options.SignIn.RequireConfirmedAccount = false)
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddSingleton<IEmailSender, EmailSender>();
    services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
        AdditionalUserClaimsPrincipalFactory>();

    services.AddAuthorization(options =>
        options.AddPolicy("TwoFactorEnabled",
            x => x.RequireClaim("amr", "mfa")));

    services.AddRazorPages();
}

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если удостоверение вошедло в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации MFA.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet удостоверение используется для доступа к утверждениям пользователя. Утверждение amr проверка для значенияmfa. Если удостоверение отсутствует это утверждение или находится false, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена в файл программы. Для политики требуется amr утверждение со значением mfa.

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню Администратор с предупреждением:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Если удостоверение вошедло в систему с помощью MFA, Администратор меню отображается без предупреждения подсказки. При входе пользователя без MFA отображается меню Администратор (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Если пользователь входит в систему без MFA, отображается предупреждение:

проверка подлинности MFA Администратор istrator

Пользователь перенаправляется в режим включения MFA при нажатии ссылки Администратор:

Администратор istrator активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Подключение

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Подключение для работы.

Клиент OpenID Подключение ASP.NET Core

Клиентское приложение ASP.NET Core Razor Pages OpenID Подключение использует AddOpenIdConnect метод для входа на сервер OpenID Подключение. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme =
            OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.SignInScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = "<OpenID Connect server URL>";
        options.RequireHttpsMetadata = true;
        options.ClientId = "<OpenID Connect client ID>";
        options.ClientSecret = "<>";
        options.ResponseType = "code";
        options.UsePkce = true;	
        options.Scope.Add("profile");
        options.Scope.Add("offline_access");
        options.SaveTokens = true;
        options.Events = new OpenIdConnectEvents
        {
            OnRedirectToIdentityProvider = context =>
            {
                context.ProtocolMessage.SetParameter("acr_values", "mfa");
                return Task.FromResult(0);
            }
        };
    });

Пример сервера OpenID Подключение IdentityServer 4 с ASP.NET CoreIdentity

На сервере OpenID Подключение, который реализуется с помощью ASP.NET Core Identity с представлениями MVC, создается новое представлениеErrorEnable2FA.cshtml. Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a asp-controller="Manage" asp-action="TwoFactorAuthentication">Enable MFA</a>

В методе LoginIIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Подключение. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. По мере отправки этого клиента с mfa набором это может быть проверка.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
    var returnUrl = model.ReturnUrl;
    var context = 
        await _interaction.GetAuthorizationContextAsync(returnUrl);
    var requires2Fa = 
        context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

    var user = await _userManager.FindByNameAsync(model.Email);
    if (user != null && !user.TwoFactorEnabled && requires2Fa)
    {
        return RedirectToAction(nameof(ErrorEnable2FA));
    }

    // code omitted for brevity

Метод ExternalLoginCallback работает так же, как локальное Identity имя входа. Свойство AcrValues проверка для mfa значения. mfa Если значение присутствует, многофакторная проверка подлинности принудительно выполняется до завершения входа (например, перенаправление в ErrorEnable2FA представление).

//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(
    string returnUrl = null,
    string remoteError = null)
{
    var context =
        await _interaction.GetAuthorizationContextAsync(returnUrl);
    var requires2Fa =
        context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

    if (remoteError != null)
    {
        ModelState.AddModelError(
            string.Empty,
            _sharedLocalizer["EXTERNAL_PROVIDER_ERROR", 
            remoteError]);
        return View(nameof(Login));
    }
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        return RedirectToAction(nameof(Login));
    }

    var email = info.Principal.FindFirstValue(ClaimTypes.Email);

    if (!string.IsNullOrEmpty(email))
    {
        var user = await _userManager.FindByNameAsync(email);
        if (user != null && !user.TwoFactorEnabled && requires2Fa)
        {
            return RedirectToAction(nameof(ErrorEnable2FA));
        }
    }

    // Sign in the user with this external login provider if the user already has a login.
    var result = await _signInManager
        .ExternalLoginSignInAsync(
            info.LoginProvider, 
            info.ProviderKey, 
            isPersistent: 
            false);

    // code omitted for brevity

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное ASP.NET клиента OpenID OpenID Подключение, чтобы требовать многофакторную проверку подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее Подключение OpenID для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc
{
    public class RequireMfa : IAuthorizationRequirement{}
}

РеализуетсяAuthorizationHandler, который будет использовать amr утверждение и проверка для значенияmfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа проверки подлинности удостоверения и реализации сервера OpenID Подключение.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Подключение можно реализовать с помощью IdentityServer4 с ASP.NET CoreIdentity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Подключение или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

В методе Startup.ConfigureServicesAddOpenIdConnect метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, который используется для проверка amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

    services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme =
            OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.SignInScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = "https://localhost:44352";
        options.RequireHttpsMetadata = true;
        options.ClientId = "AspNetCoreRequireMfaOidc";
        options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
        options.ResponseType = "code";
        options.UsePkce = true;	
        options.Scope.Add("profile");
        options.Scope.Add("offline_access");
        options.SaveTokens = true;
    });

    services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
        {
            policyIsAdminRequirement.Requirements.Add(new RequireMfa());
        });
    });

    services.AddRazorPages();
}

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Подключение также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Дополнительные ресурсы