Compartir a través de


Confirmación de la cuenta y recuperación de contraseñas con ASP.NET Identity (C#)

Antes de realizar este tutorial, primero debe completar Crear una aplicación web segura ASP.NET MVC 5 con inicio de sesión, confirmación por correo electrónico y restablecimiento de contraseña. Este tutorial contiene más detalles y le mostrará cómo configurar el correo electrónico para la confirmación de la cuenta local y permitir que los usuarios restablezcan su contraseña olvidada en ASP.NET Identidad.

Una cuenta de usuario local requiere que el usuario cree una contraseña para la cuenta y esa contraseña se almacene (de forma segura) en la aplicación web. ASP.NET Identity también admite cuentas sociales, que no requieren que el usuario cree una contraseña para la aplicación. Cuentas sociales usa un tercero (como Google, Twitter, Facebook o Microsoft) para autenticar a los usuarios. En este tema se describe lo siguiente:

Los nuevos usuarios registran su alias de correo electrónico, que crea una cuenta local.

imagen de la ventana de registro de la cuenta

Al seleccionar el botón Registrar, se envía un correo electrónico de confirmación que contiene un token de validación a su dirección de correo electrónico.

Imagen que muestra confirmación de envío de correo electrónico

El usuario se envía un correo electrónico con un token de confirmación para su cuenta.

imagen del token de confirmación

Al seleccionar el vínculo se confirma la cuenta.

imagen que confirma la dirección de correo electrónico

Recuperación y restablecimiento de contraseñas

Los usuarios locales que olvidan su contraseña pueden tener un token de seguridad enviado a su cuenta de correo electrónico, lo que les permite restablecer su contraseña.

imagen de la ventana de restablecimiento de contraseña olvidada

El usuario pronto recibirá un correo electrónico con un vínculo que les permitirá restablecer su contraseña.

Imagen que muestra un correo electrónico de restablecimiento de contraseña
Al seleccionar el enlace, serán dirigidos a la página de restablecimiento.

imagen de Imagen que muestra la ventana de restablecimiento de contraseña de usuario

Al seleccionar el botón Restablecer, se confirmará que se ha restablecido la contraseña.

Imagen de confirmación de restablecimiento de contraseña mostrando Imagen que muestra la confirmación de restablecimiento de contraseña

Creación de una aplicación web de ASP.NET

Empiece por instalar y ejecutar visual Studio 2017.

  1. Cree un nuevo proyecto web de ASP.NET y seleccione la plantilla MVC. Web Forms también admite ASP.NET Identity, por lo que puede seguir pasos similares en una aplicación de formularios web.

  2. Cambie la autenticación a Cuentas de usuario individual.

  3. Ejecute la aplicación, seleccione el vínculo Registrar y registre un usuario. En este momento, la única validación del correo electrónico es con el atributo [EmailAddress].

  4. En el Explorador de servidores, vaya a Conexiones de datos\DefaultConnection\Tables\AspNetUsers, haga clic con el botón derecho y seleccione Abrir definición de tabla.

    En la imagen siguiente se muestra el esquema AspNetUsers:

    Imagen que muestra el esquema de usuarios de ASP.NET

  5. Haga clic con el botón derecho en la tabla AspNetUsers y seleccione Mostrar datos de tabla.

    Imagen que muestra los datos de tabla

    En este momento no se ha confirmado el correo electrónico.

El almacén de datos predeterminado para ASP.NET Identity es Entity Framework, pero puede configurarlo para usar otros almacenes de datos y agregar campos adicionales. Consulte la sección Recursos adicionales al final de este tutorial.

Cuando se inicia la aplicación, se llama a la clase de inicio de OWIN ( Startup.cs ) e invoca al método en App_Start\Startup.Auth.cs, que configura el flujo de OWIN e inicializa ASP.NET Identity. Examine el método ConfigureAuth. Cada llamada CreatePerOwinContext registra una devolución de llamada (guardada en OwinContext) a la que se llamará una vez por solicitud para crear una instancia del tipo especificado. Puede establecer un punto de interrupción en el constructor y el método Create de cada tipo (ApplicationDbContext, ApplicationUserManager) y verificar que se invoquen en cada solicitud. Una instancia de ApplicationDbContext y ApplicationUserManager se almacena en el contexto de OWIN, al que se puede acceder en toda la aplicación. ASP.NET Identity se enlaza a la canalización de OWIN a través del middleware de cookies. Para obtener más información, consulte Administración por duración de solicitudes para la clase UserManager en ASP.NET Identity.

Al cambiar el perfil de seguridad, se genera una nueva marca de seguridad y se almacena en el campo SecurityStamp de la tabla AspNetUsers. Tenga en cuenta que el campo SecurityStamp es diferente de la cookie de seguridad. La cookie de seguridad no se almacena en la tabla AspNetUsers (o en cualquier otro lugar de la base de datos de identidad). El token de cookie de seguridad se autofirma mediante DPAPI y se crea con UserId, SecurityStamp y la información de tiempo de expiración.

El middleware de cookies comprueba la cookie en cada solicitud. El método SecurityStampValidator de la clase Startup alcanza la base de datos y comprueba periódicamente la marca de seguridad, tal como se especifica con el validateInterval. Esto solo ocurre cada 30 minutos (en nuestro ejemplo) a menos que cambie su perfil de seguridad. Se eligió el intervalo de 30 minutos para minimizar los viajes a la base de datos. Consulte mi tutorial de autenticación en dos fases para obtener más información.

Según los comentarios del código, el método UseCookieAuthentication admite la autenticación de cookies. El campo SecurityStamp y el código asociado proporcionan una capa adicional de seguridad a la aplicación, al cambiar la contraseña, se cerrará la sesión del explorador con el que inició sesión. El método SecurityStampValidator.OnValidateIdentity permite a la aplicación validar el token de seguridad cuando el usuario inicia sesión, que se usa al cambiar una contraseña o usar el inicio de sesión externo. Esto es necesario para asegurarse de que los tokens (cookies) generados con la contraseña antigua se invalidan. En el proyecto de ejemplo, si cambia la contraseña de los usuarios, se genera un nuevo token para el usuario, se invalidan los tokens anteriores y se actualiza el campo SecurityStamp.

El sistema de identidades le permite configurar la aplicación para que cuando cambie el perfil de seguridad de los usuarios (por ejemplo, cuando el usuario cambia su contraseña o cambia el inicio de sesión asociado (por ejemplo, desde Facebook, Google, cuenta microsoft, etc.), el usuario se cerrará la sesión de todas las instancias del explorador. Por ejemplo, la imagen siguiente muestra la aplicación de cierre de sesión único de ejemplo, que permite al usuario cerrar sesión en todas las instancias del explorador (en este caso, IE, Firefox y Chrome) seleccionando un botón. Como alternativa, el ejemplo solo permite cerrar sesión de una instancia específica del explorador.

Imagen que muestra la ventana de la aplicación de ejemplo de cierre de sesión único

La aplicación de cierre de sesión único de ejemplo muestra cómo ASP.NET Identity le permite volver a generar el token de seguridad. Esto es necesario para asegurarse de que los tokens (cookies) generados con la contraseña antigua se invalidan. Esta característica proporciona una capa adicional de seguridad a la aplicación; cuando cambie la contraseña, se cerrará la sesión en la que ha iniciado sesión en esta aplicación.

El archivo App_Start\IdentityConfig.cs contiene las clases ApplicationUserManager, EmailService y SmsService. Las clases EmailService y SmsService implementan la interfaz IIdentityMessageService, por lo que tiene métodos comunes en cada clase para configurar correo electrónico y SMS. Aunque en este tutorial solo se muestra cómo agregar notificaciones por correo electrónico a través de SendGrid, puede enviar correo electrónico mediante SMTP y otros mecanismos.

La clase Startup también contiene código estándar para agregar inicios de sesión con redes sociales (Facebook, Twitter, etc.), consulta mi tutorial sobre la aplicación MVC 5 con Facebook, Twitter, LinkedIn y el inicio de sesión con Google OAuth2 para obtener más información.

Examine la clase ApplicationUserManager, que contiene la información de identidad de los usuarios y configura las siguientes características:

  • Requisitos de seguridad de contraseña.
  • Bloqueo del usuario (intentos y tiempo).
  • Autenticación en dos fases (2FA). Trataré 2FA y SMS en otro tutorial.
  • Conexión de los servicios de correo electrónico y SMS. (Voy a cubrir SMS en otro tutorial).

La clase ApplicationUserManager deriva de la clase UserManager<ApplicationUser> genérica. ApplicationUser deriva de IdentityUser. IdentityUser deriva de la clase IdentityUser genérica:

//     Default EntityFramework IUser implementation
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
   where TLogin : IdentityUserLogin<TKey>
   where TRole : IdentityUserRole<TKey>
   where TClaim : IdentityUserClaim<TKey>
{
   public IdentityUser()
   {
      Claims = new List<TClaim>();
      Roles = new List<TRole>();
      Logins = new List<TLogin>();
   }

   ///     User ID (Primary Key)
   public virtual TKey Id { get; set; }

   public virtual string Email { get; set; }
   public virtual bool EmailConfirmed { get; set; }

   public virtual string PasswordHash { get; set; }

   ///     A random value that should change whenever a users credentials have changed (password changed, login removed)
   public virtual string SecurityStamp { get; set; }

   public virtual string PhoneNumber { get; set; }
   public virtual bool PhoneNumberConfirmed { get; set; }

   public virtual bool TwoFactorEnabled { get; set; }

   ///     DateTime in UTC when lockout ends, any time in the past is considered not locked out.
   public virtual DateTime? LockoutEndDateUtc { get; set; }

   public virtual bool LockoutEnabled { get; set; }

   ///     Used to record failures for the purposes of lockout
   public virtual int AccessFailedCount { get; set; }
   
   ///     Navigation property for user roles
   public virtual ICollection<TRole> Roles { get; private set; }

   ///     Navigation property for user claims
   public virtual ICollection<TClaim> Claims { get; private set; }

   ///     Navigation property for user logins
   public virtual ICollection<TLogin> Logins { get; private set; }
   
   public virtual string UserName { get; set; }
}

Las propiedades anteriores coinciden con las propiedades de la tabla AspNetUsers, mostrada anteriormente.

Los argumentos genéricos de IUser permiten derivar una clase mediante tipos diferentes para la clave principal. Consulte el ejemplo de ChangePK que muestra cómo cambiar la clave principal de cadena a entero o GUID.

ApplicationUser

ApplicationUser (public class ApplicationUserManager : UserManager<ApplicationUser>) se define en Models\IdentityModels.cs como:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
        UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in 
       //   CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, 
    DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

El código resaltado anterior genera una ClaimsIdentity. ASP.NET Identity y OWIN Cookie Authentication se basan en declaraciones, por lo que el framework requiere que la aplicación genere un ClaimsIdentity para el usuario. ClaimsIdentity tiene información sobre todas las reclamaciones del usuario, como su nombre, edad y a qué roles pertenece. También puede agregar más notificaciones para el usuario en esta fase.

El método OWIN AuthenticationManager.SignIn pasa en ClaimsIdentity e inicia sesión en el usuario:

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties(){
       IsPersistent = isPersistent }, 
       await user.GenerateUserIdentityAsync(UserManager));
}

Inicio de sesión en la aplicación MVC 5 con Facebook, Twitter, LinkedIn y Google OAuth2 muestra cómo puede agregar propiedades adicionales a la clase ApplicationUser.

Confirmación por correo electrónico

Es una buena idea confirmar el correo electrónico con el que un nuevo usuario se registra para comprobar que no suplantan a otra persona (es decir, no se han registrado con el correo electrónico de otra persona). Supongamos que ha tenido un foro de discusión, le gustaría impedir que "bob@example.com" se registre como "joe@contoso.com". Sin confirmación por correo electrónico, "joe@contoso.com" podría recibir correo electrónico no deseado de la aplicación. Supongamos que Bob se registró accidentalmente como "bib@example.com" y no lo había notado, no podría usar la recuperación de contraseñas porque la aplicación no tiene su correo electrónico correcto. La confirmación de correo electrónico solo proporciona protección limitada contra bots y no proporciona protección contra los spammers determinados, tienen muchos alias de correo electrónico de trabajo que pueden usar para registrarse. En el ejemplo siguiente, el usuario no podrá cambiar su contraseña hasta que se haya confirmado su cuenta (seleccionando un vínculo de confirmación recibido en la cuenta de correo electrónico con la que se registró). Puede aplicar este flujo de trabajo a otros escenarios, por ejemplo, enviando un vínculo para confirmar y restablecer la contraseña en las cuentas nuevas creadas por el administrador, enviando al usuario un correo electrónico cuando haya cambiado su perfil, etc. Por lo general, desea impedir que los nuevos usuarios publiquen datos en su sitio web antes de que se hayan confirmado por correo electrónico, un mensaje de texto SMS u otro mecanismo.

Compilación de un ejemplo más completo

En esta sección, usarás NuGet para descargar un ejemplo más completo con el que trabajaremos.

  1. Cree un nuevo proyecto web ASP.NET vacío .

  2. En la consola del Administrador de paquetes, escriba los siguientes comandos:

    Install-Package SendGrid
    Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
    

    En este tutorial, usaremos SendGrid para enviar correo electrónico. El paquete Identity.Samples instala el código con el que trabajaremos.

  3. Establezca el proyecto para usar SSL.

  4. Pruebe la creación de la cuenta local mediante la ejecución de la aplicación, seleccione el vínculo Registrar y publique el formulario de registro.

  5. Seleccione el vínculo de correo electrónico de demostración, que simula la confirmación del correo electrónico.

  6. Quite el código de confirmación del vínculo de correo electrónico de demostración del ejemplo (el código ViewBag.Link del controlador de cuenta. Consulte los métodos de acción DisplayEmail y ForgotPasswordConfirmation y las vistas Razor).

Advertencia

Si cambia cualquiera de las opciones de seguridad de este ejemplo, las aplicaciones de producción deberán someterse a una auditoría de seguridad que llame explícitamente a los cambios realizados.

Examinar el código en App_Start\IdentityConfig.cs

En el ejemplo se muestra cómo crear una cuenta y agregarla al rol Admin. Debe reemplazar el correo electrónico del ejemplo por el correo electrónico que usará para la cuenta de administrador. La manera más fácil de crear una cuenta de administrador es mediante programación en el método Seed. Esperamos tener una herramienta en el futuro que le permita crear y administrar usuarios y roles. El código de ejemplo le permite crear y administrar usuarios y roles, pero primero debe tener una cuenta de administrador para ejecutar los roles y las páginas de administrador de usuario. En este ejemplo, la cuenta de administrador se crea cuando se inicializará la base de datos.

Cambie la contraseña y cambie el nombre a una cuenta donde puede recibir notificaciones por correo electrónico.

Advertencia

Seguridad: nunca almacene datos confidenciales en el código fuente.

Como se mencionó anteriormente, la llamada app.CreatePerOwinContext en la clase de inicio agrega devoluciones de llamada al método Create del contenido de la base de datos de la aplicación, y las clases de administrador de usuarios y de administrador de roles. La canalización OWIN llama al método Create en estas clases para cada solicitud y almacena el contexto de cada clase. El controlador de cuenta expone el administrador de usuarios desde el contexto HTTP (que contiene el contexto de OWIN):

public ApplicationUserManager UserManager
{
    get
    {
        return _userManager ?? 
    HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
    }
    private set
    {
        _userManager = value;
    }
}

Cuando un usuario registra una cuenta local, se llama al método HTTP Post Register:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
            var callbackUrl = Url.Action(
               "ConfirmEmail", "Account", 
               new { userId = user.Id, code = code }, 
               protocol: Request.Url.Scheme);

            await UserManager.SendEmailAsync(user.Id, 
               "Confirm your account", 
               "Please confirm your account by clicking this link: <a href=\"" 
                                               + callbackUrl + "\">link</a>");
            // ViewBag.Link = callbackUrl;   // Used only for initial demo.
            return View("DisplayEmail");
        }
        AddErrors(result);
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

El código anterior usa los datos del modelo para crear una nueva cuenta de usuario mediante el correo electrónico y la contraseña especificados. Si el alias de correo electrónico está en el almacén de datos, se produce un error en la creación de la cuenta y se vuelve a mostrar el formulario. El método GenerateEmailConfirmationTokenAsync crea un token de confirmación seguro y lo almacena en el almacén de datos ASP.NET Identity. El método Url.Action crea un vínculo que contiene el UserId y el token de confirmación. A continuación, este vínculo se envía por correo electrónico al usuario; el usuario puede seleccionarlo en el vínculo de su aplicación de correo electrónico para confirmar su cuenta.

Configuración de la confirmación por correo electrónico

Vaya a la página de registro de SendGrid y regístrese para obtener una cuenta gratuita. Agregue código similar al siguiente para configurar SendGrid:

public class EmailService : IIdentityMessageService
{
   public Task SendAsync(IdentityMessage message)
   {
      return configSendGridasync(message);
   }

   private Task configSendGridasync(IdentityMessage message)
   {
      var myMessage = new SendGridMessage();
      myMessage.AddTo(message.Destination);
      myMessage.From = new System.Net.Mail.MailAddress(
                          "Joe@contoso.com", "Joe S.");
      myMessage.Subject = message.Subject;
      myMessage.Text = message.Body;
      myMessage.Html = message.Body;

      var credentials = new NetworkCredential(
                 ConfigurationManager.AppSettings["mailAccount"],
                 ConfigurationManager.AppSettings["mailPassword"]
                 );

      // Create a Web transport for sending email.
      var transportWeb = new Web(credentials);

      // Send the email.
      if (transportWeb != null)
      {
         return transportWeb.DeliverAsync(myMessage);
      }
      else
      {
         return Task.FromResult(0);
      }
   }
}

Nota

Los clientes de correo electrónico suelen aceptar solo mensajes de texto (sin HTML). Debe proporcionar el mensaje en texto y HTML. En el ejemplo de SendGrid anterior, esto se hace con el código myMessage.Text y myMessage.Html mostrado anteriormente.

En el código siguiente se muestra cómo enviar correo electrónico mediante la clase mailMessage , donde devuelve solo el vínculo.

void sendMail(Message message)
{
#region formatter
   string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
   string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";

   html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion

   MailMessage msg = new MailMessage();
   msg.From = new MailAddress("joe@contoso.com");
   msg.To.Add(new MailAddress(message.Destination));
   msg.Subject = message.Subject;
   msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
   msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));

   SmtpClient smtpClient = new SmtpClient("smtp.gmail.com", Convert.ToInt32(587));
   System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("joe@contoso.com", "XXXXXX");
   smtpClient.Credentials = credentials;
   smtpClient.EnableSsl = true;
   smtpClient.Send(msg);
}

Advertencia

Seguridad: nunca almacene datos confidenciales en el código fuente. La cuenta y las credenciales se almacenan en appSetting. En Azure, puede almacenar estos valores de forma segura en la pestaña Configurar en el portal de Azure. Consulte Procedimientos recomendados para implementar contraseñas y otros datos confidenciales en ASP.NET y Azure.

Introduzca sus credenciales de SendGrid, ejecute la aplicación y regístrese con un alias de correo electrónico. Luego, podrá seleccionar el enlace de confirmación en su correo electrónico. Para ver cómo hacerlo con su cuenta de correo electrónico de Outlook.com, consulte los post de John Atten Configuración SMTP de C# para host SMPT de Outlook.com y ASP.NET Identity 2.0: configurar validación de cuentas y autorización en dos fases.

Una vez que un usuario selecciona el botón Registrar, se envía un correo electrónico de confirmación que contiene un token de validación a su dirección de correo electrónico.

imagen de la ventana de confirmación enviada por correo electrónico

El usuario se envía un correo electrónico con un token de confirmación para su cuenta.

imagen del correo electrónico recibido

Examen del código

El código siguiente muestra el método POST ForgotPassword.

public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action("ResetPassword", "Account", 
    new { UserId = user.Id, code = code }, protocol: Request.Url.Scheme);
        await UserManager.SendEmailAsync(user.Id, "Reset Password", 
    "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");        
        return View("ForgotPasswordConfirmation");
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

El método produce un error en modo silencioso si no se ha confirmado el correo electrónico del usuario. Si se publicó un error para una dirección de correo electrónico no válida, los usuarios malintencionados podrían usar esa información para encontrar identificaciones de usuario válidas (alias de correo electrónico) para llevar a cabo un ataque.

El código siguiente muestra el método ConfirmEmail en el controlador de cuenta al que se llama cuando el usuario selecciona el vínculo de confirmación en el correo electrónico enviado a ellos:

public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    if (userId == null || code == null)
    {
        return View("Error");
    }
    var result = await UserManager.ConfirmEmailAsync(userId, code);
    if (result.Succeeded)
    {
        return View("ConfirmEmail");
    }
    AddErrors(result);
    return View();
}

Una vez que se ha usado un token de contraseña olvidado, se invalida. El siguiente cambio de código en el método Create (en el archivo App_Start\IdentityConfig.cs) establece los tokens para que expiren en 3 horas.

if (dataProtectionProvider != null)
 {
    manager.UserTokenProvider =
       new DataProtectorTokenProvider<ApplicationUser>
          (dataProtectionProvider.Create("ASP.NET Identity"))
          {                    
             TokenLifespan = TimeSpan.FromHours(3)
          };
 }

Con el código anterior, la contraseña olvidada y los tokens de confirmación de correo electrónico expirarán en 3 horas. El TokenLifespan predeterminado es un día.

El código siguiente muestra el método de confirmación de correo electrónico:

// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
   if (userId == null || code == null)
   {
      return View("Error");
   }
   IdentityResult result;
   try
   {
      result = await UserManager.ConfirmEmailAsync(userId, code);
   }
   catch (InvalidOperationException ioe)
   {
      // ConfirmEmailAsync throws when the userId is not found.
      ViewBag.errorMessage = ioe.Message;
      return View("Error");
   }

   if (result.Succeeded)
   {
      return View();
   }

   // If we got this far, something failed.
   AddErrors(result);
   ViewBag.errorMessage = "ConfirmEmail failed";
   return View("Error");
}

Para que la aplicación sea más segura, ASP.NET Identity admite la autenticación Two-Factor (2FA). Consulte ASP.NET Identity 2.0: configurar la validación de cuentas y la autorización en dos fases de John Atten. Aunque puede establecer el bloqueo de cuenta en los errores de contraseña en los intentos de inicio de sesión, ese enfoque hace que el inicio de sesión sea susceptible a los bloqueos DOS. Se recomienda usar el bloqueo de cuenta solo con 2FA.

Recursos adicionales