Confirmación de las cuentas y recuperación de contraseñas en ASP.NET Core

Por Rick Anderson, Ponant, y Joe Audette

Este tutorial muestra cómo crear una aplicación ASP.NET Core con confirmación de correo electrónico y restablecimiento de contraseña. Este tutorial no es un tema inicial. Debería estar familiarizado con lo siguiente:

Requisitos previos

Creación y prueba de una aplicación web con autenticación

Ejecute los comandos siguientes para crear una aplicación web con autenticación.

dotnet new webapp -au Individual -o WebPWrecover
cd WebPWrecover
dotnet run

Registro de usuario con confirmación de correo electrónico simulado

Ejecute la aplicación, seleccione el vínculo Registrar y registre un usuario. Una vez registrado, se le redirigirá a la página a /Identity/Account/RegisterConfirmation que contiene un vínculo para simular la confirmación por correo electrónico:

  • Seleccione el vínculo Click here to confirm your account.
  • Seleccione el vínculo Inicio de sesión e inicie sesión con las mismas credenciales.
  • Seleccione el Hello YourEmail@provider.com! vínculo, que redirige a la /Identity/Account/Manage/PersonalData página.
  • Seleccione la pestaña Datos personales de la izquierda y, a continuación, seleccione Eliminar.

El Click here to confirm your account vínculo se muestra porque un IEmailSender no se ha implementado y registrado con el contenedor de inserción de dependencias. Consulte el RegisterConfirmation origen.

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, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Configuración de un proveedor de correo electrónico

En este tutorial, SendGrid se usa para enviar correo electrónico. Se necesita una cuenta y una clave de SendGrid para enviar correo electrónico. Se recomienda usar SendGrid u otro servicio de correo electrónico para enviar correo electrónico en lugar de SMTP. SMTP es difícil de proteger y configurar correctamente.

La cuenta de SendGrid puede requerir agregar un remitente.

Cree una clase para capturar la clave de correo electrónico segura. Para este ejemplo, cree Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Configuración de secretos de usuario de SendGrid

Configure el SendGridKey con laherramienta administrador de secretos. Por ejemplo:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

En Windows, Secret Manager almacena pares de claves y valor en un secrets.json archivo del %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> directorio.

El contenido del secrets.json archivo no está cifrado. El siguiente marcado muestra el archivo secrets.json. El valor SendGridKeyfue retirado.

{
  "SendGridKey": "<key removed>"
}

Para más información, consulte el patrón Opciones y configuración.

Instalación de SendGrid

En este tutorial se muestra cómo agregar notificaciones por correo electrónico a través de SendGrid, pero se pueden usar otros proveedores de correo electrónico.

Instale el paquete NuGet SendGrid:

En la consola del gestor de paquetes, introduzca el siguiente comando:

Install-Package SendGrid

Consulte Introducción a SendGrid gratis para registrarse para obtener una cuenta gratuita de SendGrid.

Implementar IEmailSender

Para implementar IEmailSender, cree Services/EmailSender.cs con código similar al siguiente:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

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

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

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

Agregue el siguiente código al archivo Program.cs:

  • Agregue EmailSender como un servicio transitorio.
  • Registre la instancia de configuración AuthMessageSenderOptions.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Deshabilite la comprobación predeterminada de la cuenta cuando account.RegisterConfirmation se haya aplicado scaffolding

Esta sección solo se aplica cuando Account.RegisterConfirmation se aplica scaffolding. Omita esta sección si no ha aplicado scaffolding a Account.RegisterConfirmation.

Se redirige al usuario al Account.RegisterConfirmation donde puede seleccionar un vínculo para que se confirme la cuenta. El Account.RegisterConfirmation predeterminado se usa solo para pruebas, la verificación automática de cuentas debería estar desactivada en una aplicación de producción.

Para requerir una cuenta confirmada e impedir el inicio de sesión inmediato en el registro, establezca DisplayConfirmAccountLink = false en el archivo /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs al que se le ha aplicado scaffolding:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Este paso solo es necesario cuando Account.RegisterConfirmation se aplica scaffolding. RegisterConfirmation no con scaffolding detecta automáticamente cuándo se ha implementado un IEmailSender y se ha registrado con el contenedor de inserción de dependencias.

Registro, confirmación de correo electrónico y restablecimiento de contraseña

Ejecute la aplicación web y pruebe el flujo de confirmación de la cuenta y recuperación de contraseñas.

  • Ejecute la aplicación y registre un usuario nuevo
  • Compruebe el correo electrónico del vínculo de confirmación de la cuenta. Consulte Depurar correo electrónico si no recibe el correo electrónico.
  • Haga clic en el vínculo para confirmar el correo electrónico.
  • Inicio de sesión con la dirección de correo electrónico y la contraseña.
  • Cierre la sesión.

Prueba del restablecimiento de contraseña

  • Si ha iniciado sesión, seleccione Cerrar sesión.
  • Seleccione el vínculo Iniciar sesión y seleccione el vínculo ¿Olvidó la contraseña?
  • Escriba el correo electrónico que usó para registrar la cuenta.
  • Se envía un correo electrónico con un vínculo para restablecer la contraseña. Compruebe el correo electrónico y haga clic en el vínculo para restablecer la contraseña. Una vez que la contraseña se haya restablecido correctamente, puede iniciar sesión con el correo electrónico y la nueva contraseña.

Reenviar confirmación por correo electrónico

Seleccione el vínculo Reenviar confirmación por correo electrónico en la página Inicio de sesión.

Cambio del 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 5 días:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity

Cambio de toda la duración del token de protección de datos

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

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity.

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

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

La duración predeterminada del token de los Identity tokens de usuario es un día. En esta sección se muestra cómo cambiar la duración del token de correo electrónico.

Agregar un DataProtectorTokenProvider<TUser> y DataProtectionTokenProviderOptions personalizado:

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);
    }
}

Agregue el proveedor personalizado al contenedor de servicios:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;
using WebPWrecover.TokenProviders;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddDefaultIdentity<IdentityUser>(config =>
{
    config.SignIn.RequireConfirmedEmail = true;
    config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
        new TokenProviderDescriptor(
            typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
    config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
}).AddEntityFrameworkStores<ApplicationDbContext>();

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

builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

// Code removed for brevity.

Depurar correo electrónico

Si no puede obtener el correo electrónico funcionando:

  • Establezca un punto de interrupción en EmailSender.Execute para comprobar que se llama a SendGridClient.SendEmailAsync.
  • Cree una aplicación de consola para enviar correo electrónico con código similar a EmailSender.Execute.
  • Revise la página actividad de Email.
  • Compruebe la carpeta de correo no deseado.
  • Pruebe otro alias de correo electrónico en otro proveedor de correo electrónico (Microsoft, Yahoo, Gmail, etc.)
  • Intente enviar a diferentes cuentas de correo electrónico.

Un procedimiento recomendado de seguridad es no usar secretos de producción en pruebas y desarrollo. Si publica la aplicación en Azure, establezca los secretos de SendGrid como configuración de la aplicación en el portal de Azure Web App. El sistema de configuración se configura para leer las claves de las variables de entorno.

Combinar cuentas de inicio de sesión locales y sociales

Para completar esta sección, primero debe habilitar un proveedor de autenticación externo. Véase Autenticación de Facebook, Google y proveedores externos.

Puede combinar cuentas locales y sociales haciendo clic en el vínculo de correo electrónico. En la siguiente secuencia, "RickAndMSFT@gmail.com" se crea primero como inicio de sesión local; sin embargo, puede crear la cuenta como inicio de sesión social primero y, a continuación, agregar un inicio de sesión local.

Web application: RickAndMSFT@gmail.com user authenticated

Haga clic en el vínculo Administrar. Tenga en cuenta el 0 externo (inicios de sesión sociales) asociados a esta cuenta.

Manage view

Haga clic en el vínculo a otro servicio de inicio de sesión y acepte las solicitudes de la aplicación. En la imagen siguiente, Facebook es el proveedor de autenticación externo:

Manage your external logins view listing Facebook

Se han combinado las dos cuentas. Puede iniciar sesión con cualquiera de las cuentas. Es posible que quiera que los usuarios agreguen cuentas locales en caso de que su servicio de autenticación de inicio de sesión social esté inactivo o que haya perdido el acceso a su cuenta social.

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:

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

Prerrequisitos

SDK de .NET Core 3.0 o versiones posteriores

Creación y prueba de una aplicación web con autenticación

Ejecute los comandos siguientes para crear una aplicación web con autenticación.

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet run

Ejecute la aplicación, seleccione el vínculo Registrar y registre un usuario. Una vez registrado, se le redirigirá a la página a /Identity/Account/RegisterConfirmation que contiene un vínculo para simular la confirmación por correo electrónico:

  • Seleccione el vínculo Click here to confirm your account.
  • Seleccione el vínculo Inicio de sesión e inicie sesión con las mismas credenciales.
  • Seleccione el Hello YourEmail@provider.com! vínculo, que redirige a la /Identity/Account/Manage/PersonalData página.
  • Seleccione la pestaña Datos personales de la izquierda y, a continuación, seleccione Eliminar.

Configuración de un proveedor de correo electrónico

En este tutorial, SendGrid se usa para enviar correo electrónico. Puede usar otros proveedores de correo electrónico. Se recomienda usar SendGrid u otro servicio de correo electrónico para enviar correo electrónico. SMTP es difícil de configurar, por lo que el correo no está marcado como correo no deseado.

La cuenta de SendGrid puede requerir agregar un remitente.

Cree una clase para capturar la clave de correo electrónico segura. Para este ejemplo, cree Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Configuración de secretos de usuario de SendGrid

Configure el SendGridKey con laherramienta administrador de secretos. Por ejemplo:

dotnet user-secrets set SendGridKey <SG.key>

Successfully saved SendGridKey = SG.keyVal to the secret store.

En Windows, Secret Manager almacena pares de claves y valor en un secrets.json archivo del %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> directorio.

El contenido del secrets.json archivo no está cifrado. El siguiente marcado muestra el archivo secrets.json. El valor SendGridKeyfue retirado.

{
  "SendGridKey": "<key removed>"
}

Para más información, consulte el patrón Opciones y configuración.

Instalación de SendGrid

En este tutorial se muestra cómo agregar notificaciones por correo electrónico a través de SendGrid, pero puede enviar correo electrónico mediante SMTP y otros mecanismos.

Instale el paquete NuGet SendGrid:

En la consola del gestor de paquetes, introduzca el siguiente comando:

Install-Package SendGrid

Consulte Introducción a SendGrid gratis para registrarse para obtener una cuenta gratuita de SendGrid.

Implementar IEmailSender

Para implementar IEmailSender, cree Services/EmailSender.cs con código similar al siguiente:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

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

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

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

Agregue el código siguiente al ConfigureServices método en el archivo Startup.cs:

  • Agregue EmailSender como un servicio transitorio.
  • Registre la instancia de configuración AuthMessageSenderOptions.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

RegisterConfirmation del scaffolding

Siga las instrucciones de Scaffold Identity y scaffold Account\RegisterConfirmation.

Deshabilite la comprobación predeterminada de la cuenta cuando account.RegisterConfirmation se haya aplicado scaffolding

Esta sección solo se aplica cuando Account.RegisterConfirmation se aplica scaffolding. Omita esta sección si no ha aplicado scaffolding a Account.RegisterConfirmation.

Se redirige al usuario al Account.RegisterConfirmation donde puede seleccionar un vínculo para que se confirme la cuenta. El Account.RegisterConfirmation predeterminado se usa solo para pruebas, la verificación automática de cuentas debería estar desactivada en una aplicación de producción.

Para requerir una cuenta confirmada e impedir el inicio de sesión inmediato en el registro, establezca DisplayConfirmAccountLink = false en el archivo /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs al que se le ha aplicado scaffolding:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Este paso solo es necesario cuando Account.RegisterConfirmation se aplica scaffolding. RegisterConfirmation no con scaffolding detecta automáticamente cuándo se ha implementado un IEmailSender y se ha registrado con el contenedor de inserción de dependencias.

Registro, confirmación de correo electrónico y restablecimiento de contraseña

Ejecute la aplicación web y pruebe el flujo de confirmación de la cuenta y recuperación de contraseñas.

  • Ejecute la aplicación y registre un usuario nuevo
  • Compruebe el correo electrónico del vínculo de confirmación de la cuenta. Consulte Depurar correo electrónico si no recibe el correo electrónico.
  • Haga clic en el vínculo para confirmar el correo electrónico.
  • Inicio de sesión con la dirección de correo electrónico y la contraseña.
  • Cierre la sesión.

Prueba del restablecimiento de contraseña

  • Si ha iniciado sesión, seleccione Cerrar sesión.
  • Seleccione el vínculo Iniciar sesión y seleccione el vínculo ¿Olvidó la contraseña?
  • Escriba el correo electrónico que usó para registrar la cuenta.
  • Se envía un correo electrónico con un vínculo para restablecer la contraseña. Compruebe el correo electrónico y haga clic en el vínculo para restablecer la contraseña. Una vez que la contraseña se haya restablecido correctamente, puede iniciar sesión con el correo electrónico y la nueva contraseña.

Reenviar confirmación por correo electrónico

En ASP.NET Core 5.0 y versiones posteriores, seleccione el vínculo Reenviar confirmación de correo electrónico en la página Inicio de sesión.

Cambio del 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 5 días:

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

Cambio de toda la duración del token de protección de datos

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

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

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

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

La duración predeterminada del token de los Identity tokens de usuario es un día. En esta sección se muestra cómo cambiar la duración del token de correo electrónico.

Agregar un DataProtectorTokenProvider<TUser> y DataProtectionTokenProviderOptions personalizado:

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);
    }
}

Agregue el proveedor personalizado al contenedor de servicios:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
      }).AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

Depurar correo electrónico

Si no puede obtener el correo electrónico funcionando:

  • Establezca un punto de interrupción en EmailSender.Execute para comprobar que se llama a SendGridClient.SendEmailAsync.
  • Cree una aplicación de consola para enviar correo electrónico con código similar a EmailSender.Execute.
  • Revise la página actividad de Email.
  • Compruebe la carpeta de correo no deseado.
  • Pruebe otro alias de correo electrónico en otro proveedor de correo electrónico (Microsoft, Yahoo, Gmail, etc.)
  • Intente enviar a diferentes cuentas de correo electrónico.

Un procedimiento recomendado de seguridad es no usar secretos de producción en pruebas y desarrollo. Si publica la aplicación en Azure, establezca los secretos de SendGrid como configuración de la aplicación en el portal de Azure Web App. El sistema de configuración se configura para leer las claves de las variables de entorno.

Combinar cuentas de inicio de sesión locales y sociales

Para completar esta sección, primero debe habilitar un proveedor de autenticación externo. Véase Autenticación de Facebook, Google y proveedores externos.

Puede combinar cuentas locales y sociales haciendo clic en el vínculo de correo electrónico. En la siguiente secuencia, "RickAndMSFT@gmail.com" se crea primero como inicio de sesión local; sin embargo, puede crear la cuenta como inicio de sesión social primero y, a continuación, agregar un inicio de sesión local.

Web application: RickAndMSFT@gmail.com user authenticated

Haga clic en el vínculo Administrar. Tenga en cuenta el 0 externo (inicios de sesión sociales) asociados a esta cuenta.

Manage view

Haga clic en el vínculo a otro servicio de inicio de sesión y acepte las solicitudes de la aplicación. En la imagen siguiente, Facebook es el proveedor de autenticación externo:

Manage your external logins view listing Facebook

Se han combinado las dos cuentas. Puede iniciar sesión con cualquiera de las cuentas. Es posible que quiera que los usuarios agreguen cuentas locales en caso de que su servicio de autenticación de inicio de sesión social esté inactivo o que haya perdido el acceso a su cuenta social.

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:

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