Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.
Por Damien Bowden
Exibir ou baixar código de exemplo (repositório GitHub damienbod/AspNetCoreHybridFlowWithApi)
A autenticação multifator (MFA) é um processo no qual um usuário é solicitado durante um evento de entrada para formas adicionais de identificação. Esse prompt pode ser para inserir um código de um telemóvel, usar uma chave FIDO2 ou fornecer uma leitura de impressão digital. Quando você precisa de uma segunda forma de autenticação, a segurança é aprimorada. O fator adicional não é facilmente obtido ou duplicado por um ciberatacante.
Este artigo abrange as seguintes áreas:
- O que é MFA e quais fluxos de MFA são recomendados
- Configurar MFA para páginas de administração usando ASP.NET Core Identity
- Enviar requisito de login MFA para o servidor OpenID Connect
- Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
MFA (Autenticação Multifator), 2FA (Autenticação de Dois Fatores)
A MFA requer pelo menos dois ou mais tipos de prova para uma identidade, como algo que você sabe, algo que você possui ou validação biométrica para o usuário autenticar.
A autenticação de dois fatores (2FA) é como um subconjunto da MFA, mas a diferença é que a MFA pode exigir dois ou mais fatores para provar a identidade.
2FA é suportado por padrão ao usar ASP.NET Core Identity. Para habilitar ou desabilitar o 2FA para um usuário específico, defina a propriedade IdentityUser<TKey>.TwoFactorEnabled. A interface do usuário padrão do ASP.NET Core Identity inclui páginas para configurar o 2FA.
MFA TOTP (Algoritmo de senha única baseado no tempo)
MFA usando TOTP é suportado por padrão ao usar ASP.NET Core Identity. Essa abordagem pode ser usada em conjunto com qualquer aplicativo autenticador compatível, incluindo:
- Microsoft Authenticator
- Autenticador do Google
Para obter detalhes de implementação, consulte Habilitar a geração de código QR para aplicativos autenticadores TOTP no ASP.NET Core.
Para desativar o suporte para MFA TOTP, configure a autenticação usando AddIdentity em vez de AddDefaultIdentity.
AddDefaultIdentity chama AddDefaultTokenProviders internamente, que regista múltiplos fornecedores de tokens, incluindo um para MFA TOTP. Para registrar apenas provedores de token específicos, chame AddTokenProvider para cada provedor necessário. Para obter mais informações sobre os provedores de token disponíveis, consulte a fonte de referência AddDefaultTokenProviders (repositóriodotnet/aspnetcore GitHub).
Observação
Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa ramificações ou tags. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Chaves de acesso MFA/FIDO2 ou sem senha
passkeys/FIDO2 é atualmente:
- A forma mais segura de alcançar a AMF.
- MFA que protege contra ataques de phishing. (Bem como autenticação de certificado e Windows para empresas)
No momento, o ASP.NET Core não suporta chaves de acesso/FIDO2 diretamente. Passkeys/FIDO2 podem ser usados para MFA ou fluxos sem senha.
O Microsoft Entra ID fornece suporte para chaves de acesso/FIDO2 e fluxos sem senha. Para obter mais informações, consulte Opções de autenticação sem senha.
Outras formas de MFA sem senha não protegem ou podem não proteger contra phishing.
Autenticação Multi-Fator (MFA) via SMS
MFA com SMS aumenta a segurança maciçamente em comparação com a autenticação de senha (fator único). No entanto, o uso do SMS como um segundo fator não é mais recomendado. Existem muitos vetores de ataque conhecidos para esse tipo de implementação.
Configurar MFA para páginas de administração usando ASP.NET Core Identity
A autenticação multifator pode ser obrigatória para os utilizadores acederem a páginas sensíveis dentro de uma aplicação ASP.NET Core Identity. Isso pode ser útil para aplicativos onde existem diferentes níveis de acesso para as diferentes identidades. Por exemplo, os usuários podem visualizar os dados do perfil usando um logon de senha, mas um administrador seria obrigado a usar MFA para acessar as páginas administrativas.
Estender o login com uma declaração de MFA
O código de demonstração está configurado usando o ASP.NET Core com as Páginas Identity e Razor. O método AddIdentity é usado em vez de AddDefaultIdentity, portanto, uma implementação de IUserClaimsPrincipalFactory pode ser usada para adicionar declarações à identidade após um login bem-sucedido.
Advertência
Este artigo mostra o uso de cadeias de conexão. Com um banco de dados local, o usuário não precisa ser autenticado, mas na produção, as cadeias de conexão às vezes incluem uma senha para autenticar. Uma credencial de senha de proprietário de recurso (ROPC) é um risco de segurança que deve ser evitado em bancos de dados de produção. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos implantados em ambientes de teste ou produção, consulte Fluxos de autenticação segura.
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();
A classe AdditionalUserClaimsPrincipalFactory adiciona a declaração amr às declarações de usuário somente após um login bem-sucedido. O valor da reclamação é lido da base de dados. A declaração é adicionada aqui porque o utilizador só deve acessar a vista protegida avançada se a identidade tiver feito login com MFA. Se a exibição do banco de dados for lida diretamente do banco de dados em vez de usar a declaração, é possível acessar a exibição sem MFA diretamente após ativar o 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;
}
}
}
Como a configuração do serviço Identity foi alterada na classe Startup, os layouts do Identity precisam ser atualizados. Estruture as páginas Identity no aplicativo. Defina o layout no arquivo Identity/Account/Manage/_Layout.cshtml.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Atribua também o layout para todas as páginas de gerenciamento das páginas Identity:
@{
Layout = "_Layout.cshtml";
}
Validar o requisito de autenticação multifator (MFA) na página de administração
A página de administração Razor valida que o utilizador fez login usando MFA. No método OnGet, a identidade é usada para acessar as declarações de usuário. A reclamação amr é verificada para o valor mfa. Se a identidade não contiver esta declaração ou estiver false, a página redirecionará para a página Ativar MFA. Isso é possível porque o usuário já fez login, mas sem 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();
}
}
}
Lógica da interface para gerir as informações de login do utilizador
Uma política de autorização foi adicionada na inicialização. A política requer o pedido de indemnização amr com o valor mfa.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Essa política pode ser usada na visualização _Layout para mostrar ou ocultar o menu Admin com o aviso:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Se a identidade tiver efetuado login usando MFA, o menu Admin será exibido sem o aviso de *tooltip*. Quando o utilizador faz login sem MFA, o menu Admin (Não ativado) é exibido junto com a dica de ferramenta que informa o utilizador (explicando o aviso).
@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>
}
}
Se o usuário fizer login sem MFA, o aviso será exibido:
O utilizador é redirecionado para a vista de ativação de MFA ao clicar no link Admin.
O 
Enviar requisito de login MFA para o servidor OpenID Connect
O parâmetro acr_values pode ser usado para passar o valor mfa necessário do cliente para o servidor em uma solicitação de autenticação.
Observação
O parâmetro acr_values precisa ser manipulado no servidor OpenID Connect para que isso funcione.
Cliente OpenID Connect ASP.NET Core
O aplicativo cliente OpenID Connect do ASP.NET Core Razor Pages usa o método AddOpenIdConnect para fazer login no servidor OpenID Connect. O parâmetro acr_values é definido com o valor mfa e enviado com a solicitação de autenticação. O OpenIdConnectEvents é usado para adicionar isso.
Para obter os valores de parâmetro acr_values recomendados, consulte Valores de Referência de Métodos de Autenticação.
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");
});
Exemplo de servidor OpenID Connect Duende IdentityServer com ASP.NET Core Identity
No servidor OpenID Connect, que é implementado usando ASP.NET Core Identity com Razor Pages, uma nova página chamada ErrorEnable2FA.cshtml é criada. A vista:
- Exibe se o Identity vem de um aplicativo que requer MFA, mas o usuário não ativou isso no Identity.
- Informa o usuário e adiciona um link para ativá-lo.
@{
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>
No método Login, a implementação da interface IIdentityServerInteractionService_interaction é usada para acessar os parâmetros de solicitação do OpenID Connect. O parâmetro acr_values é acessado usando a propriedade AcrValues. Como o cliente enviou isso com mfa definido, isso pode ser verificado.
Se a MFA for necessária e o usuário no ASP.NET Core Identity tiver a MFA habilitada, o login continuará. Quando o usuário não tem MFA habilitado, o usuário é redirecionado para o modo de exibição personalizado ErrorEnable2FA.cshtml. Em seguida, ASP.NET Core Identity inicia a sessão do utilizador.
O Fido2Store é usado para verificar se o usuário ativou o MFA usando um provedor de token FIDO2 personalizado.
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();
}
Se o usuário já estiver conectado, o aplicativo cliente:
- Ainda valida a alegação
amr. - Pode configurar o MFA com um link para a vista ASP.NET Core Identity.
Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
Este exemplo mostra como um aplicativo ASP.NET Core Razor Page, que usa o OpenID Connect para entrar, pode exigir que os usuários tenham se autenticado usando MFA.
Para validar o requisito de MFA, é criado um requisito IAuthorizationRequirement. Isso será adicionado às páginas usando uma política que requer MFA.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
É implementada uma AuthorizationHandler que utilizará a declaração amr e verificará o valor mfa. O amr é retornado no id_token de uma autenticação bem-sucedida e pode ter muitos valores diferentes, conforme definido na especificação Authentication Method Reference Values.
O valor retornado depende de como a identidade foi autenticada e da implementação do servidor OpenID Connect.
O AuthorizationHandler usa o requisito RequireMfa e valida a reivindicação amr. O servidor OpenID Connect pode ser implementado usando o Duende Identity Server com ASP.NET Core Identity. Quando um usuário efetua login usando TOTP, a declaração amr é retornada com um valor MFA. Se estiver usando uma implementação de servidor OpenID Connect diferente ou um tipo de MFA diferente, a declaração de amr terá, ou poderá, um valor diferente. O código deve ser estendido para aceitar isso também.
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;
}
}
No arquivo de programa, o método AddOpenIdConnect é usado como o esquema de desafio padrão. O manipulador de autorização, que é usado para verificar a declaração de amr, é adicionado ao contêiner Inversion of Control. Em seguida, é criada uma política que adiciona o requisito 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();
Esta política é então usada na página Razor, conforme necessário. A política também pode ser adicionada globalmente para todo o aplicativo.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Se o utilizador se autenticar sem MFA, a declaração amr provavelmente terá um valor pwd. O pedido não será autorizado para aceder à página. Usando os valores padrão, o usuário será redirecionado para a página Account/AccessDenied. Esse comportamento pode ser alterado ou você pode implementar sua própria lógica personalizada aqui. Neste exemplo, um link é adicionado para que o usuário válido possa configurar o MFA para sua conta.
@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>
Agora, apenas os usuários que se autenticam com MFA podem acessar a página ou o site. Se diferentes tipos de MFA forem usados ou se 2FA estiver bem, a declaração de amr terá valores diferentes e precisará ser processada corretamente. Diferentes servidores OpenID Connect também retornam valores diferentes para essa declaração e podem não seguir a especificação Valores de Referência do Método de Autenticação.
Ao fazer login sem MFA (por exemplo, usando apenas uma senha):
O
amrtem opwdvalor:
O acesso é negado:
Como alternativa, faça login usando OTP com Identity:
Personalização de parâmetros OIDC e OAuth
A opção AdditionalAuthorizationParameters dos manipuladores de autenticação OAuth e OIDC permite a personalização dos parâmetros das mensagens de autorização que geralmente são incluídos como parte da cadeia de consulta de redirecionamento.
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Recursos adicionais
Por Damien Bowden
Exibir ou baixar código de exemplo (repositório GitHub damienbod/AspNetCoreHybridFlowWithApi)
A autenticação multifator (MFA) é um processo no qual um usuário é solicitado durante um evento de entrada para formas adicionais de identificação. Esse prompt pode ser para inserir um código de um telemóvel, usar uma chave FIDO2 ou fornecer uma leitura de impressão digital. Quando você precisa de uma segunda forma de autenticação, a segurança é aprimorada. O fator adicional não é facilmente obtido ou duplicado por um ciberatacante.
Este artigo abrange as seguintes áreas:
- O que é MFA e quais fluxos de MFA são recomendados
- Configurar MFA para páginas de administração usando ASP.NET Core Identity
- Enviar requisito de login MFA para o servidor OpenID Connect
- Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
MFA (Autenticação Multifator), 2FA (Autenticação de Dois Fatores)
A MFA requer pelo menos dois ou mais tipos de prova para uma identidade, como algo que você sabe, algo que você possui ou validação biométrica para o usuário autenticar.
A autenticação de dois fatores (2FA) é como um subconjunto da MFA, mas a diferença é que a MFA pode exigir dois ou mais fatores para provar a identidade.
2FA é suportado por padrão ao usar ASP.NET Core Identity. Para habilitar ou desabilitar o 2FA para um usuário específico, defina a propriedade IdentityUser<TKey>.TwoFactorEnabled. A interface do usuário padrão do ASP.NET Core Identity inclui páginas para configurar o 2FA.
MFA TOTP (Algoritmo de senha única baseado no tempo)
MFA usando TOTP é suportado por padrão ao usar ASP.NET Core Identity. Essa abordagem pode ser usada em conjunto com qualquer aplicativo autenticador compatível, incluindo:
- Microsoft Authenticator
- Autenticador do Google
Para obter detalhes de implementação, consulte Habilitar a geração de código QR para aplicativos autenticadores TOTP no ASP.NET Core.
Para desativar o suporte para MFA TOTP, configure a autenticação usando AddIdentity em vez de AddDefaultIdentity.
AddDefaultIdentity chama AddDefaultTokenProviders internamente, que regista múltiplos fornecedores de tokens, incluindo um para MFA TOTP. Para registrar apenas provedores de token específicos, chame AddTokenProvider para cada provedor necessário. Para obter mais informações sobre provedores de token disponíveis, consulte os métodos de extensão em IdentityBuilderExtensions.
Chaves de acesso MFA/FIDO2 ou sem senha
passkeys/FIDO2 é atualmente:
- A forma mais segura de alcançar a AMF.
- MFA que protege contra ataques de phishing. (Bem como autenticação de certificado e Windows para empresas)
No momento, o ASP.NET Core não suporta chaves de acesso/FIDO2 diretamente. Passkeys/FIDO2 podem ser usados para MFA ou fluxos sem senha.
O Microsoft Entra ID fornece suporte para chaves de acesso/FIDO2 e fluxos sem senha. Para obter mais informações, consulte Opções de autenticação sem senha.
Outras formas de MFA sem senha não protegem ou podem não proteger contra phishing.
Autenticação Multi-Fator (MFA) via SMS
MFA com SMS aumenta a segurança maciçamente em comparação com a autenticação de senha (fator único). No entanto, o uso do SMS como um segundo fator não é mais recomendado. Existem muitos vetores de ataque conhecidos para esse tipo de implementação.
Configurar MFA para páginas de administração usando ASP.NET Core Identity
A autenticação multifator pode ser obrigatória para os utilizadores acederem a páginas sensíveis dentro de uma aplicação ASP.NET Core Identity. Isso pode ser útil para aplicativos onde existem diferentes níveis de acesso para as diferentes identidades. Por exemplo, os usuários podem visualizar os dados do perfil usando um logon de senha, mas um administrador seria obrigado a usar MFA para acessar as páginas administrativas.
Estender o login com uma declaração de MFA
O código de demonstração está configurado usando o ASP.NET Core com as Páginas Identity e Razor. O método AddIdentity é usado em vez de AddDefaultIdentity, portanto, uma implementação de IUserClaimsPrincipalFactory pode ser usada para adicionar declarações à identidade após um login bem-sucedido.
Advertência
Este artigo mostra o uso de cadeias de conexão. Com um banco de dados local, o usuário não precisa ser autenticado, mas na produção, as cadeias de conexão às vezes incluem uma senha para autenticar. Uma credencial de senha de proprietário de recurso (ROPC) é um risco de segurança que deve ser evitado em bancos de dados de produção. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos implantados em ambientes de teste ou produção, consulte Fluxos de autenticação segura.
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();
A classe AdditionalUserClaimsPrincipalFactory adiciona a declaração amr às declarações de usuário somente após um login bem-sucedido. O valor da reclamação é lido da base de dados. A declaração é adicionada aqui porque o utilizador só deve acessar a vista protegida avançada se a identidade tiver feito login com MFA. Se a exibição do banco de dados for lida diretamente do banco de dados em vez de usar a declaração, é possível acessar a exibição sem MFA diretamente após ativar o 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;
}
}
}
Como a configuração do serviço Identity foi alterada na classe Startup, os layouts do Identity precisam ser atualizados. Estruture as páginas Identity no aplicativo. Defina o layout no arquivo Identity/Account/Manage/_Layout.cshtml.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Atribua também o layout para todas as páginas de gerenciamento das páginas Identity:
@{
Layout = "_Layout.cshtml";
}
Validar o requisito de autenticação multifator (MFA) na página de administração
A página de administração Razor valida que o utilizador fez login usando MFA. No método OnGet, a identidade é usada para acessar as declarações de usuário. A reclamação amr é verificada para o valor mfa. Se a identidade não contiver esta declaração ou estiver false, a página redirecionará para a página Ativar MFA. Isso é possível porque o usuário já fez login, mas sem 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();
}
}
}
Lógica da interface para gerir as informações de login do utilizador
Uma política de autorização foi adicionada na inicialização. A política requer o pedido de indemnização amr com o valor mfa.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Essa política pode ser usada na visualização _Layout para mostrar ou ocultar o menu Admin com o aviso:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Se a identidade tiver efetuado login usando MFA, o menu Admin será exibido sem o aviso de *tooltip*. Quando o utilizador faz login sem MFA, o menu Admin (Não ativado) é exibido junto com a dica de ferramenta que informa o utilizador (explicando o aviso).
@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>
}
}
Se o usuário fizer login sem MFA, o aviso será exibido:
O utilizador é redirecionado para a vista de ativação de MFA ao clicar no link Admin.
O 
Enviar requisito de login MFA para o servidor OpenID Connect
O parâmetro acr_values pode ser usado para passar o valor mfa necessário do cliente para o servidor em uma solicitação de autenticação.
Observação
O parâmetro acr_values precisa ser manipulado no servidor OpenID Connect para que isso funcione.
Cliente OpenID Connect ASP.NET Core
O aplicativo cliente OpenID Connect do ASP.NET Core Razor Pages usa o método AddOpenIdConnect para fazer login no servidor OpenID Connect. O parâmetro acr_values é definido com o valor mfa e enviado com a solicitação de autenticação. O OpenIdConnectEvents é usado para adicionar isso.
Para obter os valores de parâmetro acr_values recomendados, consulte Valores de Referência de Métodos de Autenticação.
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);
}
};
});
Exemplo de servidor OpenID Connect Duende IdentityServer com ASP.NET Core Identity
No servidor OpenID Connect, que é implementado usando ASP.NET Core Identity com Razor Pages, uma nova página chamada ErrorEnable2FA.cshtml é criada. A vista:
- Exibe se o Identity vem de um aplicativo que requer MFA, mas o usuário não ativou isso no Identity.
- Informa o usuário e adiciona um link para ativá-lo.
@{
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>
No método Login, a implementação da interface IIdentityServerInteractionService_interaction é usada para acessar os parâmetros de solicitação do OpenID Connect. O parâmetro acr_values é acessado usando a propriedade AcrValues. Como o cliente enviou isso com mfa definido, isso pode ser verificado.
Se a MFA for necessária e o usuário no ASP.NET Core Identity tiver a MFA habilitada, o login continuará. Quando o usuário não tem MFA habilitado, o usuário é redirecionado para o modo de exibição personalizado ErrorEnable2FA.cshtml. Em seguida, ASP.NET Core Identity inicia a sessão do utilizador.
O Fido2Store é usado para verificar se o usuário ativou o MFA usando um provedor de token FIDO2 personalizado.
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();
}
Se o usuário já estiver conectado, o aplicativo cliente:
- Ainda valida a alegação
amr. - Pode configurar o MFA com um link para a vista ASP.NET Core Identity.
Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
Este exemplo mostra como um aplicativo ASP.NET Core Razor Page, que usa o OpenID Connect para entrar, pode exigir que os usuários tenham se autenticado usando MFA.
Para validar o requisito de MFA, é criado um requisito IAuthorizationRequirement. Isso será adicionado às páginas usando uma política que requer MFA.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
É implementada uma AuthorizationHandler que utilizará a declaração amr e verificará o valor mfa. O amr é retornado no id_token de uma autenticação bem-sucedida e pode ter muitos valores diferentes, conforme definido na especificação Authentication Method Reference Values.
O valor retornado depende de como a identidade foi autenticada e da implementação do servidor OpenID Connect.
O AuthorizationHandler usa o requisito RequireMfa e valida a reivindicação amr. O servidor OpenID Connect pode ser implementado usando o Duende Identity Server com ASP.NET Core Identity. Quando um usuário efetua login usando TOTP, a declaração amr é retornada com um valor MFA. Se estiver usando uma implementação de servidor OpenID Connect diferente ou um tipo de MFA diferente, a declaração de amr terá, ou poderá, um valor diferente. O código deve ser estendido para aceitar isso também.
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;
}
}
No arquivo de programa, o método AddOpenIdConnect é usado como o esquema de desafio padrão. O manipulador de autorização, que é usado para verificar a declaração de amr, é adicionado ao contêiner Inversion of Control. Em seguida, é criada uma política que adiciona o requisito 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();
Esta política é então usada na página Razor, conforme necessário. A política também pode ser adicionada globalmente para todo o aplicativo.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Se o utilizador se autenticar sem MFA, a declaração amr provavelmente terá um valor pwd. O pedido não será autorizado para aceder à página. Usando os valores padrão, o usuário será redirecionado para a página Account/AccessDenied. Esse comportamento pode ser alterado ou você pode implementar sua própria lógica personalizada aqui. Neste exemplo, um link é adicionado para que o usuário válido possa configurar o MFA para sua conta.
@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>
Agora, apenas os usuários que se autenticam com MFA podem acessar a página ou o site. Se diferentes tipos de MFA forem usados ou se 2FA estiver bem, a declaração de amr terá valores diferentes e precisará ser processada corretamente. Diferentes servidores OpenID Connect também retornam valores diferentes para essa declaração e podem não seguir a especificação Valores de Referência do Método de Autenticação.
Ao fazer login sem MFA (por exemplo, usando apenas uma senha):
O
amrtem opwdvalor:
O acesso é negado:
Como alternativa, faça login usando OTP com Identity:
Recursos adicionais
Por Damien Bowden
Exibir ou baixar código de exemplo (repositório GitHub damienbod/AspNetCoreHybridFlowWithApi)
A autenticação multifator (MFA) é um processo no qual um usuário é solicitado durante um evento de entrada para formas adicionais de identificação. Esse prompt pode ser para inserir um código de um telemóvel, usar uma chave FIDO2 ou fornecer uma leitura de impressão digital. Quando você precisa de uma segunda forma de autenticação, a segurança é aprimorada. O fator adicional não é facilmente obtido ou duplicado por um ciberatacante.
Este artigo abrange as seguintes áreas:
- O que é MFA e quais fluxos de MFA são recomendados
- Configurar MFA para páginas de administração usando ASP.NET Core Identity
- Enviar requisito de login MFA para o servidor OpenID Connect
- Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
MFA (Autenticação Multifator), 2FA (Autenticação de Dois Fatores)
A MFA requer pelo menos dois ou mais tipos de prova para uma identidade, como algo que você sabe, algo que você possui ou validação biométrica para o usuário autenticar.
A autenticação de dois fatores (2FA) é como um subconjunto da MFA, mas a diferença é que a MFA pode exigir dois ou mais fatores para provar a identidade.
MFA TOTP (Algoritmo de senha única baseado no tempo)
MFA usando TOTP é uma implementação suportada usando ASP.NET Core Identity. Isso pode ser usado em conjunto com qualquer aplicativo autenticador compatível, incluindo:
- Aplicativo Microsoft Authenticator
- Aplicação Google Authenticator
Consulte o link a seguir para obter detalhes sobre a implementação:
Habilitar a geração de QR Code para aplicativos autenticadores TOTP no ASP.NET Core
Chaves de acesso MFA/FIDO2 ou sem senha
passkeys/FIDO2 é atualmente:
- A forma mais segura de alcançar a AMF.
- MFA que protege contra ataques de phishing. (Bem como autenticação de certificado e Windows para empresas)
No momento, o ASP.NET Core não suporta chaves de acesso/FIDO2 diretamente. Passkeys/FIDO2 podem ser usados para MFA ou fluxos sem senha.
O Microsoft Entra ID fornece suporte para chaves de acesso/FIDO2 e fluxos sem senha. Para obter mais informações, consulte Opções de autenticação sem senha.
Outras formas de MFA sem senha não protegem ou podem não proteger contra phishing.
Autenticação Multi-Fator (MFA) via SMS
MFA com SMS aumenta a segurança maciçamente em comparação com a autenticação de senha (fator único). No entanto, o uso do SMS como um segundo fator não é mais recomendado. Existem muitos vetores de ataque conhecidos para esse tipo de implementação.
Configurar MFA para páginas de administração usando ASP.NET Core Identity
A autenticação multifator pode ser obrigatória para os utilizadores acederem a páginas sensíveis dentro de uma aplicação ASP.NET Core Identity. Isso pode ser útil para aplicativos onde existem diferentes níveis de acesso para as diferentes identidades. Por exemplo, os usuários podem visualizar os dados do perfil usando um logon de senha, mas um administrador seria obrigado a usar MFA para acessar as páginas administrativas.
Estender o login com uma declaração de MFA
O código de demonstração está configurado usando o ASP.NET Core com as Páginas Identity e Razor. O método AddIdentity é usado em vez de AddDefaultIdentity, portanto, uma implementação de IUserClaimsPrincipalFactory pode ser usada para adicionar declarações à identidade após um login bem-sucedido.
Advertência
Este artigo mostra o uso de cadeias de conexão. Com um banco de dados local, o usuário não precisa ser autenticado, mas na produção, as cadeias de conexão às vezes incluem uma senha para autenticar. Uma credencial de senha de proprietário de recurso (ROPC) é um risco de segurança que deve ser evitado em bancos de dados de produção. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos implantados em ambientes de teste ou produção, consulte Fluxos de autenticação segura.
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();
}
A classe AdditionalUserClaimsPrincipalFactory adiciona a declaração amr às declarações de usuário somente após um login bem-sucedido. O valor da reclamação é lido da base de dados. A declaração é adicionada aqui porque o utilizador só deve acessar a vista protegida avançada se a identidade tiver feito login com MFA. Se a exibição do banco de dados for lida diretamente do banco de dados em vez de usar a declaração, é possível acessar a exibição sem MFA diretamente após ativar o 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;
}
}
}
Como a configuração do serviço Identity foi alterada na classe Startup, os layouts do Identity precisam ser atualizados. Estruture as páginas Identity no aplicativo. Defina o layout no arquivo Identity/Account/Manage/_Layout.cshtml.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Atribua também o layout para todas as páginas de gerenciamento das páginas Identity:
@{
Layout = "_Layout.cshtml";
}
Validar o requisito de autenticação multifator (MFA) na página de administração
A página de administração Razor valida que o utilizador fez login usando MFA. No método OnGet, a identidade é usada para acessar as declarações de usuário. A reclamação amr é verificada para o valor mfa. Se a identidade não contiver esta declaração ou estiver false, a página redirecionará para a página Ativar MFA. Isso é possível porque o usuário já fez login, mas sem 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();
}
}
}
Lógica da interface para gerir as informações de login do utilizador
Uma política de autorização foi adicionada no arquivo de programa. A política requer o pedido de indemnização amr com o valor mfa.
builder.Services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Essa política pode ser usada na visualização _Layout para mostrar ou ocultar o menu Admin com o aviso:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Se a identidade tiver efetuado login usando MFA, o menu Admin será exibido sem o aviso de *tooltip*. Quando o utilizador faz login sem MFA, o menu Admin (Não ativado) é exibido junto com a dica de ferramenta que informa o utilizador (explicando o aviso).
@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>
}
}
Se o usuário fizer login sem MFA, o aviso será exibido:
O utilizador é redirecionado para a vista de ativação de MFA ao clicar no link Admin.
O 
Enviar requisito de login MFA para o servidor OpenID Connect
O parâmetro acr_values pode ser usado para passar o valor mfa necessário do cliente para o servidor em uma solicitação de autenticação.
Observação
O parâmetro acr_values precisa ser manipulado no servidor OpenID Connect para que isso funcione.
Cliente OpenID Connect ASP.NET Core
O aplicativo cliente OpenID Connect do ASP.NET Core Razor Pages usa o método AddOpenIdConnect para fazer login no servidor OpenID Connect. O parâmetro acr_values é definido com o valor mfa e enviado com a solicitação de autenticação. O OpenIdConnectEvents é usado para adicionar isso.
Para obter os valores de parâmetro acr_values recomendados, consulte Valores de Referência de Métodos de Autenticação.
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);
}
};
});
Exemplo de servidor OpenID Connect IdentityServer 4 com ASP.NET Core Identity
No servidor OpenID Connect, que é implementado usando ASP.NET Core Identity com visualizações MVC, uma nova exibição chamada ErrorEnable2FA.cshtml é criada. A vista:
- Exibe se o Identity vem de um aplicativo que requer MFA, mas o usuário não ativou isso no Identity.
- Informa o usuário e adiciona um link para ativá-lo.
@{
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>
No método Login, a implementação da interface IIdentityServerInteractionService_interaction é usada para acessar os parâmetros de solicitação do OpenID Connect. O parâmetro acr_values é acessado usando a propriedade AcrValues. Como o cliente enviou isso com mfa definido, isso pode ser verificado.
Se a MFA for necessária e o usuário no ASP.NET Core Identity tiver a MFA habilitada, o login continuará. Quando o usuário não tem MFA habilitado, o usuário é redirecionado para o modo de exibição personalizado ErrorEnable2FA.cshtml. Em seguida, ASP.NET Core Identity inicia a sessão do utilizador.
//
// 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
O método ExternalLoginCallback funciona como o login Identity local. A propriedade AcrValues é verificada para o valor mfa. Se o valor mfa estiver presente, o MFA será necessário antes de concluir o login (por exemplo, redirecionado para a vista 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
Se o usuário já estiver conectado, o aplicativo cliente:
- Ainda valida a alegação
amr. - Pode configurar o MFA com um link para a vista ASP.NET Core Identity.
Forçar cliente ASP.NET Core OpenID Connect a exigir autenticação multifator
Este exemplo mostra como um aplicativo ASP.NET Core Razor Page, que usa o OpenID Connect para entrar, pode exigir que os usuários tenham se autenticado usando MFA.
Para validar o requisito de MFA, é criado um requisito IAuthorizationRequirement. Isso será adicionado às páginas usando uma política que requer MFA.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc
{
public class RequireMfa : IAuthorizationRequirement{}
}
É implementada uma AuthorizationHandler que utilizará a declaração amr e verificará o valor mfa. O amr é retornado no id_token de uma autenticação bem-sucedida e pode ter muitos valores diferentes, conforme definido na especificação Authentication Method Reference Values.
O valor retornado depende de como a identidade foi autenticada e da implementação do servidor OpenID Connect.
O AuthorizationHandler usa o requisito RequireMfa e valida a reivindicação amr. O servidor OpenID Connect pode ser implementado usando IdentityServer4 com ASP.NET Core Identity. Quando um usuário efetua login usando TOTP, a declaração amr é retornada com um valor MFA. Se estiver usando uma implementação de servidor OpenID Connect diferente ou um tipo de MFA diferente, a declaração de amr terá, ou poderá, um valor diferente. O código deve ser estendido para aceitar isso também.
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;
}
}
No método Startup.ConfigureServices, o método AddOpenIdConnect é usado como o esquema de desafio padrão. O manipulador de autorização, que é usado para verificar a declaração de amr, é adicionado ao contêiner Inversion of Control. Em seguida, é criada uma política que adiciona o requisito 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();
}
Esta política é então usada na página Razor, conforme necessário. A política também pode ser adicionada globalmente para todo o aplicativo.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Se o utilizador se autenticar sem MFA, a declaração amr provavelmente terá um valor pwd. O pedido não será autorizado para aceder à página. Usando os valores padrão, o usuário será redirecionado para a página Account/AccessDenied. Esse comportamento pode ser alterado ou você pode implementar sua própria lógica personalizada aqui. Neste exemplo, um link é adicionado para que o usuário válido possa configurar o MFA para sua conta.
@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>
Agora, apenas os usuários que se autenticam com MFA podem acessar a página ou o site. Se diferentes tipos de MFA forem usados ou se 2FA estiver bem, a declaração de amr terá valores diferentes e precisará ser processada corretamente. Diferentes servidores OpenID Connect também retornam valores diferentes para essa declaração e podem não seguir a especificação Valores de Referência do Método de Autenticação.
Ao fazer login sem MFA (por exemplo, usando apenas uma senha):
O
amrtem opwdvalor:
O acesso é negado:
Como alternativa, faça login usando OTP com Identity: