Autenticação de dois fatores usando SMS e email com o ASP.NET Identity
por Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi
Este tutorial mostrará como configurar a 2FA (autenticação de dois fatores) usando SMS e email.
Este artigo foi escrito por Rick Anderson (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung e Suhas Joshi. A amostra do NuGet foi escrita principalmente por Hao Kung.
Este tópico aborda o seguinte:
- Compilando o exemplo de identidade
- Configurar o SMS para autenticação de dois fatores
- Habilitar a autenticação de dois fatores
- Como registrar um provedor de autenticação de dois fatores
- Combinar contas de logon locais e sociais
- Bloqueio de conta de ataques de força bruta
Compilando o exemplo de identidade
Nesta seção, você usará o NuGet para baixar um exemplo com o qual trabalharemos. Comece instalando e executando o Visual Studio Express 2013 para Web ou Visual Studio 2013. Instale o Visual Studio 2013 Atualização 2 ou superior.
Observação
Aviso: você deve instalar o Visual Studio 2013 Atualização 2 para concluir este tutorial.
Crie um novo projeto Web ASP.NET vazio .
No Console do Gerenciador de Pacotes, insira os seguintes comandos:
Install-Package SendGrid
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
Neste tutorial, usaremos SendGrid para enviar email e Twilio ou ASPSMS para sms. O
Identity.Samples
pacote instala o código com o qual trabalharemos.Defina o projeto para usar SSL.
Opcional: siga as instruções no tutorial de confirmação do meu Email para conectar o SendGrid e, em seguida, executar o aplicativo e registrar uma conta de email.
Opcional: Remova o código de confirmação do link de email de demonstração do exemplo (o
ViewBag.Link
código no controlador de conta. Consulte osDisplayEmail
métodos de ação eForgotPasswordConfirmation
e as exibições razor ).Opcional: Remova o
ViewBag.Status
código dos controladores Gerenciar e Conta e das exibições razor Views\Account\VerifyCode.cshtml e Views\Manage\VerifyPhoneNumber.cshtml . Como alternativa, você pode manter aViewBag.Status
exibição para testar como esse aplicativo funciona localmente sem precisar conectar e enviar emails e mensagens SMS.
Observação
Aviso: se você alterar qualquer uma das configurações de segurança neste exemplo, os aplicativos de produção precisarão passar por uma auditoria de segurança que chame explicitamente as alterações feitas.
Configurar o SMS para autenticação de dois fatores
Este tutorial fornece instruções para usar o Twilio ou o ASPSMS, mas você pode usar qualquer outro provedor de SMS.
Criando uma conta de usuário com um provedor de SMS
Crie uma conta do Twilio ou do ASPSMS .
Instalar pacotes adicionais ou adicionar referências de serviço
Twilio:
No Console do Gerenciador de Pacotes, digite o seguinte comando:
Install-Package Twilio
ASPSMS:
A seguinte referência de serviço precisa ser adicionada:Endereço:
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Namespace:
ASPSMSX2
Descobrir as credenciais do usuário do provedor de SMS
Twilio:
Na guia Painel da sua conta do Twilio, copie o SID da conta e o token de autenticação.ASPSMS:
Nas configurações da sua conta, navegue até Userkey e copie-a junto com sua Senha autodefinida.Posteriormente, armazenaremos esses valores nas variáveis
SMSAccountIdentification
eSMSAccountPassword
.Especificando SenderID/Originator
Twilio:
Na guia Números , copie o número de telefone do Twilio.ASPSMS:
No Menu Desbloquear Originadores , desbloqueie um ou mais Originadores ou escolha um Originador alfanumérico (sem suporte de todas as redes).Posteriormente, armazenaremos esse valor na variável
SMSAccountFrom
.Transferindo credenciais do provedor de SMS para o aplicativo
Disponibilize as credenciais e o número de telefone do remetente para o aplicativo:
public static class Keys { public static string SMSAccountIdentification = "My Idenfitication"; public static string SMSAccountPassword = "My Password"; public static string SMSAccountFrom = "+15555551234"; }
Aviso
Segurança – Nunca armazene dados confidenciais em seu código-fonte. A conta e as credenciais são adicionadas ao código acima para manter o exemplo simples. Confira o ASP.NET MVC de Jon Atten : Manter configurações privadas fora do controle do código-fonte.
Implementação da transferência de dados para o provedor de SMS
Configure a
SmsService
classe no arquivo App_Start\IdentityConfig.cs .Dependendo do provedor de SMS usado, ative a seção Twilio ou ASPSMS :
public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Twilio Begin // var Twilio = new TwilioRestClient( // Keys.SMSAccountIdentification, // Keys.SMSAccountPassword); // var result = Twilio.SendMessage( // Keys.SMSAccountFrom, // message.Destination, message.Body // ); // Status is one of Queued, Sending, Sent, Failed or null if the number is not valid // Trace.TraceInformation(result.Status); // Twilio doesn't currently have an async API, so return success. // return Task.FromResult(0); // Twilio End // ASPSMS Begin // var soapSms = new WebApplication1.ASPSMSX2.ASPSMSX2SoapClient("ASPSMSX2Soap"); // soapSms.SendSimpleTextSMS( // Keys.SMSAccountIdentification, // Keys.SMSAccountPassword, // message.Destination, // Keys.SMSAccountFrom, // message.Body); // soapSms.Close(); // return Task.FromResult(0); // ASPSMS End } }
Execute o aplicativo e faça logon com a conta que você registrou anteriormente.
Clique em sua ID de Usuário, que ativa o método de
Index
ação noManage
controlador.Clique em Adicionar.
Em alguns segundos, você receberá uma mensagem de texto com o código de verificação. Insira-o e pressione Enviar.
O modo de exibição Gerenciar mostra que o número de telefone foi adicionado.
Examinar o código
// GET: /Account/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two factor provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "The phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId())
};
return View(model);
}
O Index
método de ação no Manage
controlador define a mensagem status com base em sua ação anterior e fornece links para alterar sua senha local ou adicionar uma conta local. O Index
método também exibe o estado ou seu número de telefone 2FA, logons externos, 2FA habilitado e lembre-se do método 2FA para este navegador (explicado posteriormente). Clicar em sua ID de usuário (email) na barra de título não passa uma mensagem. Clicando no número de telefone : remover passagens Message=RemovePhoneSuccess
de link como uma cadeia de caracteres de consulta.
https://localhost:44300/Manage?Message=RemovePhoneSuccess
[]
O AddPhoneNumber
método de ação exibe uma caixa de diálogo para inserir um número de telefone que pode receber mensagens SMS.
// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
Clicar no botão Enviar código de verificação posta o número de telefone no método de ação HTTP POST AddPhoneNumber
.
// POST: /Account/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Your security code is: " + code
};
// Send token
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}
O GenerateChangePhoneNumberTokenAsync
método gera o token de segurança que será definido na mensagem SMS. Se o serviço de SMS tiver sido configurado, o token será enviado como a cadeia de caracteres "Seu código de segurança é <token>". O SmsService.SendAsync
método para é chamado de forma assíncrona e, em seguida, o aplicativo é redirecionado para o VerifyPhoneNumber
método de ação (que exibe a caixa de diálogo a seguir), em que você pode inserir o código de verificação.
Depois de inserir o código e clicar em enviar, o código será postado no método de ação HTTP POST VerifyPhoneNumber
.
// POST: /Account/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInAsync(user, isPersistent: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "Failed to verify phone");
return View(model);
}
O ChangePhoneNumberAsync
método verifica o código de segurança postado. Se o código estiver correto, o número de telefone será adicionado ao PhoneNumber
campo da AspNetUsers
tabela. Se essa chamada for bem-sucedida, o SignInAsync
método será chamado:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
// Clear the temporary cookies used for external and two factor sign ins
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.TwoFactorCookie);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = isPersistent
},
await user.GenerateUserIdentityAsync(UserManager));
}
O isPersistent
parâmetro define se a sessão de autenticação é mantida em várias solicitações.
Quando você altera seu perfil de segurança, um novo carimbo de segurança é gerado e armazenado no SecurityStamp
campo da tabela AspNetUsers . Observe que o SecurityStamp
campo é diferente do cookie de segurança. O cookie de segurança não é armazenado na AspNetUsers
tabela (ou em qualquer outro lugar no Banco de Dados de Identidade). O token de cookie de segurança é autoassinado usando DPAPI e é criado com as UserId, SecurityStamp
informações de tempo de expiração e .
O middleware de cookie verifica o cookie em cada solicitação. O SecurityStampValidator
método na Startup
classe atinge o BD e verifica o carimbo de segurança periodicamente, conforme especificado com o validateInterval
. Isso só acontece a cada 30 minutos (em nosso exemplo), a menos que você altere seu perfil de segurança. O intervalo de 30 minutos foi escolhido para minimizar as viagens ao banco de dados.
O SignInAsync
método precisa ser chamado quando qualquer alteração é feita no perfil de segurança. Quando o perfil de segurança for alterado, o banco de dados atualizará o SecurityStamp
campo e, sem chamar o SignInAsync
método, você permanecerá conectado somente até a próxima vez que o pipeline OWIN atingir o banco de dados (o validateInterval
). Você pode testar isso alterando o SignInAsync
método para retornar imediatamente e definindo a propriedade cookie validateInterval
de 30 minutos para 5 segundos:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
return;
// Clear any partial cookies from external or two factor partial sign ins
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.TwoFactorCookie);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = isPersistent
},
await user.GenerateUserIdentityAsync(UserManager));
}
public void ConfigureAuth(IAppBuilder app) {
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a
// third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add
// an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
//validateInterval: TimeSpan.FromMinutes(30),
validateInterval: TimeSpan.FromSeconds(5),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Com as alterações de código acima, você pode alterar seu perfil de segurança (por exemplo, alterando o estado de Dois Fatores Habilitados) e você será conectado em 5 segundos quando o SecurityStampValidator.OnValidateIdentity
método falhar. Remova a linha de retorno no SignInAsync
método , faça outra alteração de perfil de segurança e você não será desconectado. O SignInAsync
método gera um novo cookie de segurança.
Habilitar a autenticação de dois fatores
No aplicativo de exemplo, você precisa usar a interface do usuário para habilitar a 2FA (autenticação de dois fatores). Para habilitar a 2FA, clique em sua ID de usuário (alias de email) na barra de navegação.
Clique em habilitar 2FA. Faça logoff e faça logon novamente. Se você habilitou o email (consulte meu tutorial anterior), selecione o SMS ou email para 2FA. A página Verificar Código é exibida onde você pode inserir o código (por SMS ou email). Clicar na caixa Lembrar este navegador marcar isentará você de precisar usar a 2FA para fazer logon com esse computador e navegador. Habilitar a 2FA e clicar no navegador Lembrar deste navegador fornecerá uma forte proteção 2FA contra usuários mal-intencionados que tentam acessar sua conta, desde que não tenham acesso ao computador. Você pode fazer isso em qualquer computador privado usado regularmente. Ao definir Lembrar este navegador, você obtém a segurança adicional da 2FA de computadores que não usa regularmente e obtém a conveniência de não precisar passar pela 2FA em seus próprios computadores.
Como registrar um provedor de autenticação de dois fatores
Quando você cria um novo projeto MVC, o arquivo IdentityConfig.cs contém o seguinte código para registrar um provedor de autenticação de dois fatores:
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager = new ApplicationUserManager(
new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Register two factor authentication providers. This application uses Phone and Emails as a
// step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is: {0}"
});
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is: {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
Adicionar um número de telefone para 2FA
O AddPhoneNumber
método de ação no Manage
controlador gera um token de segurança e o envia para o número de telefone fornecido.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token and send it
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Your security code is: " + code
};
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}
Depois de enviar o token, ele redireciona para o VerifyPhoneNumber
método de ação, no qual você pode inserir o código para registrar o SMS para 2FA. SMS 2FA não é usado até que você tenha verificado o número de telefone.
Habilitando a 2FA
O EnableTFA
método de ação habilita a 2FA:
// POST: /Manage/EnableTFA
[HttpPost]
public async Task<ActionResult> EnableTFA()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInAsync(user, isPersistent: false);
}
return RedirectToAction("Index", "Manage");
}
Observe que o SignInAsync
deve ser chamado porque habilitar 2FA é uma alteração no perfil de segurança. Quando a 2FA estiver habilitada, o usuário precisará usar a 2FA para fazer logon, usando as abordagens 2FA que registrou (SMS e email no exemplo).
Você pode adicionar mais provedores de 2FA, como geradores de código QR ou pode escrever seus próprios.
Observação
Os códigos 2FA são gerados usando o Algoritmo de Senha Única baseado em tempo e os códigos são válidos por seis minutos. Se você levar mais de seis minutos para inserir o código, receberá uma mensagem de erro de código inválida.
Combinar contas de logon sociais e locais
Você pode combinar contas locais e sociais clicando em seu link de email. Na sequênciaRickAndMSFT@gmail.com "" a seguir é criada primeiro como um logon local, mas você pode criar a conta como um logon social primeiro e, em seguida, adicionar um logon local.
Clique no link Gerenciar . Observe as 0 externas (logons sociais) associadas a essa conta.
Clique no link para outro serviço de logon e aceite as solicitações do aplicativo. As duas contas foram combinadas, você poderá fazer logon com qualquer uma das contas. Talvez você queira que os usuários adicionem contas locais caso o serviço de autenticação de logon social esteja inativo ou provavelmente eles tenham perdido o acesso à conta social.
Na imagem a seguir, Tom é um logon social (que você pode ver nos Logons Externos: 1 mostrado na página).
Clicar em Escolher uma senha permite adicionar um logon local associado à mesma conta.
Bloqueio de conta de ataques de força bruta
Você pode proteger as contas em seu aplicativo contra ataques de dicionário habilitando o bloqueio do usuário. O código a seguir no método configura o ApplicationUserManager Create
bloqueio:
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
O código acima habilita o bloqueio somente para autenticação de dois fatores. Embora você possa habilitar o bloqueio para logons alterando shouldLockout
para true no Login
método do controlador de conta, recomendamos que você não habilite o bloqueio para logons porque ele torna a conta suscetível a ataques de logon dos DOS . No código de exemplo, o bloqueio é desabilitado para a conta de administrador criada no ApplicationDbInitializer Seed
método :
public static void InitializeIdentityForEF(ApplicationDbContext db)
{
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin@example.com";
const string roleName = "Admin";
//Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, GetSecurePassword());
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
Exigir que um usuário tenha uma conta de email validada
O código a seguir exige que um usuário tenha uma conta de email validada antes de fazer logon:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Require the user to have a confirmed email before they can log on.
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
ViewBag.errorMessage = "You must have a confirmed email to log on.";
return View("Error");
}
}
// This doen't count login failures towards lockout only two factor authentication
// To enable password failures to trigger lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Como SignInManager verifica o requisito de 2FA
O logon local e o logon social marcar para ver se a 2FA está habilitada. Se a 2FA estiver habilitada, o SignInManager
método de logon retornará SignInStatus.RequiresVerification
e o usuário será redirecionado para o SendCode
método de ação, no qual precisará inserir o código para concluir a sequência de logon. Se o usuário tiver RememberMe definido no cookie local dos usuários, o SignInManager
retornará SignInStatus.Success
e ele não precisará passar pela 2FA.
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Require the user to have a confirmed email before they can log on.
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
ViewBag.errorMessage = "You must have a confirmed email to log on.";
return View("Error");
}
}
// This doen't count login failures towards lockout only two factor authentication
// To enable password failures to trigger lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
O código a seguir mostra o método de SendCode
ação. Um SelectListItem é criado com todos os métodos 2FA habilitados para o usuário. O SelectListItem é passado para o auxiliar DropDownListFor , que permite que o usuário selecione a abordagem 2FA (normalmente email e SMS).
public async Task<ActionResult> SendCode(string returnUrl)
{
var userId = await SignInManager.GetVerifiedUserIdAsync();
if (userId == null)
{
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl });
}
Depois que o usuário posta a abordagem 2FA, o HTTP POST SendCode
método de ação é chamado, o SignInManager
envia o código 2FA e o usuário é redirecionado para o VerifyCode
método de ação em que pode inserir o código para concluir o logon.
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
// Generate the token and send it
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
return View("Error");
}
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl });
}
Bloqueio 2FA
Embora você possa definir o bloqueio de conta em falhas de tentativa de senha de logon, essa abordagem torna seu logon suscetível a bloqueios dos DOS . Recomendamos que você use o bloqueio de conta somente com 2FA. Quando o ApplicationUserManager
é criado, o código de exemplo define o bloqueio de 2FA e MaxFailedAccessAttemptsBeforeLockout
para cinco. Depois que um usuário faz logon (por meio de conta local ou conta social), cada tentativa com falha na 2FA é armazenada e, se as tentativas máximas forem atingidas, o usuário será bloqueado por cinco minutos (você pode definir o tempo de bloqueio com DefaultAccountLockoutTimeSpan
).
Recursos adicionais
- recursos recomendados para identidade ASP.NET Lista completa de blogs de identidade, vídeos, tutoriais e ótimos links SO.
- O aplicativo MVC 5 com Facebook, Twitter, LinkedIn e Google OAuth2 Sign-on também mostra como adicionar informações de perfil à tabela de usuários.
- ASP.NET MVC e Identidade 2.0: Noções básicas de John Atten.
- Confirmação da conta e recuperação de senha com identidade ASP.NET
- Introdução à Identidade do ASP.NET
- Anunciando a RTM de ASP.NET Identity 2.0.0 por Pranav Rastogi.
- ASP.NET Identity 2.0: Configurando a validação da conta e a autorização de Two-Factor por John Atten.