Compartir a través de


Confirmación de la cuenta y recuperación de contraseñas en ASP.NET Core Blazor

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión de .NET 9 de este artículo.

En este artículo se explica cómo configurar una Blazor Web App de ASP.NET Core con confirmación de correo electrónico y recuperación de contraseña.

Nota:

Este artículo solo se aplica a Blazor Web Apps. Para implementar la confirmación de correo electrónico y la recuperación de contraseñas para aplicaciones independientes Blazor WebAssembly con ASP.NET Core Identity, consulte Confirmación de la cuenta y recuperación de contraseñas en ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity.

Espacio de nombres

El espacio de nombres de la aplicación usado por el ejemplo de este artículo es BlazorSample. Actualiza los ejemplos de código para usar el espacio de nombres de la aplicación.

Selección y configuración de un proveedor de correo electrónico

En este artículo, API transaccional de Mailchimp se usa a través de Mandrill.net para enviar correo electrónico. Se recomienda usar un servicio de correo electrónico para enviar correo electrónico en lugar de SMTP. SMTP es difícil de configurar y proteger correctamente. Independientemente del servicio de correo electrónico que uses, accede a sus instrucciones para aplicaciones .NET, crea una cuenta, configura una clave de API para su servicio e instala los paquetes NuGet necesarios.

Cree una clase para contener la clave de API del proveedor de correo electrónico secreto. En el ejemplo de este artículo se usa una clase denominada AuthMessageSenderOptions con una EmailAuthKey propiedad para contener la clave.

AuthMessageSenderOptions.cs:

namespace BlazorSample;

public class AuthMessageSenderOptions
{
    public string? EmailAuthKey { get; set; }
}

Registra la instancia de configuración AuthMessageSenderOptions en el archivoProgram:

builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

Configuración de un secreto de usuario para la clave de seguridad del proveedor

Si el proyecto ya se ha inicializado para la herramienta Secret Manager, ya tendrá un identificador de secretos de aplicación (<AppSecretsId>) en su archivo de proyecto (.csproj). En Visual Studio, puede indicar si el identificador de secretos de la aplicación está presente examinando el panel Propiedades cuando el proyecto está seleccionado en Explorador de soluciones. Si la aplicación no se ha inicializado, ejecute el siguiente comando en un shell de comandos abierto en el directorio del proyecto. En Visual Studio, puede usar el símbolo del sistema de PowerShell para desarrolladores.

dotnet user-secrets init

Establezca la clave de API con la herramienta Administrador de secretos. En el ejemplo siguiente, el nombre de clave es EmailAuthKey coincidir AuthMessageSenderOptions.EmailAuthKeycon y la clave se representa mediante el {KEY} marcador de posición. Ejecute el siguiente comando con la clave de API:

dotnet user-secrets set "EmailAuthKey" "{KEY}"

Si usa Visual Studio, puede confirmar que el secreto está establecido haciendo clic con el botón derecho en el proyecto de servidor en Explorador de soluciones y seleccionando Administrar secretos de usuario.

Para obtener más información, consulta Almacenamiento seguro de secretos de aplicación en el desarrollo en ASP.NET Core

Advertencia

No almacene secretos de aplicación, cadena de conexión s, credenciales, contraseñas, números de identificación personal (PIN), código C#/.NET privado o claves o tokens privados en el código del lado cliente, lo que siempre es inseguro. En entornos de prueba/ensayo y producción, el código del lado Blazor servidor y las API web deben usar flujos de autenticación seguros que eviten mantener las credenciales dentro del código del proyecto o los archivos de configuración. Fuera de las pruebas de desarrollo local, se recomienda evitar el uso de variables de entorno para almacenar datos confidenciales, ya que las variables de entorno no son el enfoque más seguro. Para las pruebas de desarrollo local, se recomienda la herramienta Secret Manager para proteger los datos confidenciales. Para obtener más información, consulte Mantener de forma segura los datos confidenciales y las credenciales.

Implemente IEmailSender

El ejemplo siguiente se basa en la API transaccional de Mailchimp mediante Mandrill.net. Para un proveedor diferente, consulte su documentación sobre cómo implementar el envío de un mensaje de correo electrónico.

Agregue el paquete NuGet Mandrill.net al proyecto.

Agregue la siguiente EmailSender clase para implementar IEmailSender<TUser>. En el ejemplo siguiente, ApplicationUser es .IdentityUser El marcado HTML del mensaje se puede personalizar aún más. Siempre que el message pasado empiece MandrillMessage por el < carácter , la API de Mandrill.net asume que el cuerpo del mensaje se compone en HTML.

Components/Account/EmailSender.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Mandrill;
using Mandrill.Model;
using BlazorSample.Data;

namespace BlazorSample.Components.Account;

public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
    ILogger<EmailSender> logger) : IEmailSender<ApplicationUser>
{
    private readonly ILogger logger = logger;

    public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;

    public Task SendConfirmationLinkAsync(AppUser user, string email,
        string confirmationLink) => SendEmailAsync(email, "Confirm your email",
        "<html lang=\"en\"><head></head><body>Please confirm your account by " +
        $"<a href='{confirmationLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetLinkAsync(AppUser user, string email,
        string resetLink) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password by " +
        $"<a href='{resetLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetCodeAsync(AppUser user, string email,
        string resetCode) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password " +
        $"using the following code:<br>{resetCode}</body></html>");

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.EmailAuthKey))
        {
            throw new Exception("Null EmailAuthKey");
        }

        await Execute(Options.EmailAuthKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, 
        string toEmail)
    {
        var api = new MandrillApi(apiKey);
        var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail, 
            subject, message);
        await api.Messages.SendAsync(mandrillMessage);

        logger.LogInformation("Email to {EmailAddress} sent!", toEmail);
    }
}

Nota:

El contenido del cuerpo de los mensajes puede requerir una codificación especial para el proveedor de servicios de correo electrónico. Si los vínculos del cuerpo del mensaje no se pueden seguir en el mensaje de correo electrónico, consulte la documentación del proveedor de servicios para solucionar el problema.

Configuración de la aplicación para admitir correo electrónico

En el archivoProgram, cambia la implementación del remitente de correo electrónico a EmailSender:

- builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
+ builder.Services.AddSingleton<IEmailSender<ApplicationUser>, EmailSender>();

Quita el IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) de la aplicación.

En el componente RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor), quita el bloque condicional del bloque @code que comprueba si el EmailSender es un IdentityNoOpEmailSender:

- else if (EmailSender is IdentityNoOpEmailSender)
- {
-     ...
- }

Además, en el componente RegisterConfirmation, quita el marcado Razor y el código para comprobar el campo emailConfirmationLink, dejando solo la línea que indica al usuario que compruebe su correo electrónico...

- @if (emailConfirmationLink is not null)
- {
-     ...
- }
- else
- {
     <p>Please check your email to confirm your account.</p>
- }

@code {
-    private string? emailConfirmationLink;

     ...
}

Habilitación de la confirmación de la cuenta después de que un sitio tenga usuarios

Habilitar la confirmación de la cuenta en un sitio con usuarios bloquea todos los usuarios existentes. Los usuarios existentes están bloqueados porque sus cuentas no están confirmadas. Para solucionar el bloqueo de usuario existente, use uno de los métodos siguientes:

  • Actualiza la base de datos para marcar todos los usuarios existentes como confirmados.
  • Confirma los usuarios existentes. Por ejemplo, los correos electrónicos de envío por lotes con vínculos de confirmación.

Tiempo de espera de correo electrónico y actividad

El tiempo de inactividad por defecto es de 14 días. El código siguiente establece el tiempo de espera de inactividad en cinco días con expiración deslizante:

builder.Services.ConfigureApplicationCookie(options => {
    options.ExpireTimeSpan = TimeSpan.FromDays(5);
    options.SlidingExpiration = true;
});

Cambio de toda la duración del token de protección de datos de ASP.NET Core

El código siguiente cambia el período de tiempo de espera de los tokens de protección de datos a tres horas:

builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
    options.TokenLifespan = TimeSpan.FromHours(3));

Los tokens de usuario integrados Identity (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) tienen un tiempo de espera de un día.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, usa la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, consulta Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Cambiar la duración del token de correo electrónico

La duración predeterminada del token de los Identitytokens de usuario es de un día.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, usa la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, consulta Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Para cambiar la duración del token de correo electrónico, agregue un personalizado DataProtectorTokenProvider<TUser> y DataProtectionTokenProviderOptions.

CustomTokenProvider.cs:

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;

namespace BlazorSample;

public class CustomEmailConfirmationTokenProvider<TUser>
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class EmailConfirmationTokenProviderOptions 
    : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

public class CustomPasswordResetTokenProvider<TUser> 
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomPasswordResetTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<PasswordResetTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class PasswordResetTokenProviderOptions : 
    DataProtectionTokenProviderOptions
{
    public PasswordResetTokenProviderOptions()
    {
        Name = "PasswordResetDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(3);
    }
}

Configura los servicios para que usen el proveedor de tokens personalizado en el archivo Program:

builder.Services.AddIdentityCore<ApplicationUser>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<ApplicationUser>)));
        options.Tokens.EmailConfirmationTokenProvider = 
            "CustomEmailConfirmation";
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

builder.Services
    .AddTransient<CustomEmailConfirmationTokenProvider<ApplicationUser>>();

Solución de problemas

Si no puedes obtener el correo electrónico funcionando:

  • Establece un punto de interrupción en EmailSender.Execute para comprobar que se llama a SendEmailAsync.
  • Crea una aplicación de consola para enviar correo electrónico mediante código similar a EmailSender.Execute para depurar el problema.
  • Revisa las páginas del historial de correo electrónico de la cuenta en el sitio web del proveedor de correo electrónico.
  • Comprueba la carpeta de correo no deseado para ver los mensajes.
  • Prueba otro alias de correo electrónico en otro proveedor de correo electrónico, como Microsoft, Yahoo o Gmail.
  • Intenta enviar a diferentes cuentas de correo electrónico.

Recursos adicionales