Confirmação de conta e de recuperação de senha no ASP.NET Core

Por Rick Anderson, Ponant e Joe Audette

Esse tutorial mostra como criar um aplicativo ASP.NET Core com confirmação por email e redefinição de senha. Este tutorial não é um tópico inicial. Você deve estar familiarizado com:

Pré-requisitos

Criar e testar um aplicativo Web com autenticação

Execute os comandos a seguir para criar um aplicativo Web com autenticação.

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

Registrar usuário com confirmação de email simulado

Execute o aplicativo, selecione o link Registrar e registre um usuário. Depois de registrado, você será redirecionado para a página /Identity/Account/RegisterConfirmation, que contém um link para simular a confirmação por email:

  • Selecione o link Click here to confirm your account.
  • Selecione o link Fazer logon e entre com as mesmas credenciais.
  • Selecione o link Hello YourEmail@provider.com!, que redireciona para a página /Identity/Account/Manage/PersonalData.
  • Selecione a guia Dados pessoais à esquerda e, em seguida, selecione Excluir.

O link Click here to confirm your account é exibido porque um IEmailSender não foi implementado e registrado com o contêiner de injeção de dependência. Consulte a fonte de RegisterConfirmation.

Observação

Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Configurar um provedor de email

Neste tutorial, o SendGrid é usado para enviar email. Uma conta do SendGrid e uma chave são necessárias para enviar emails. É recomendável usar o SendGrid ou outro serviço de email para enviar email em vez de SMTP. O SMTP é difícil de proteger e configurar corretamente.

A conta do SendGrid pode exigir a adição de um Remetente.

Crie uma classe para buscar a chave de email segura. Para este exemplo, crie Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Configurar segredos do usuário do SendGrid

Defina o SendGridKey com a ferramenta Secret Manager. Por exemplo:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

No Windows, o Secret Manager armazena pares de chave/valor em um arquivo secrets.json no diretório %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

O conteúdo do arquivo secrets.json não é criptografado. A seguinte marcação mostra o arquivo secrets.json. O valor de SendGridKey foi removido.

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

Para obter mais informações, consulte o padrão de Opções e configuração.

Instalar o SendGrid

Esse tutorial mostra como adicionar notificações por email por meio do SendGrid, mas outros provedores de email podem ser usados.

Instale o pacote SendGrid do NuGet.

No Console do Gerenciador de Pacotes, insira o seguinte comando:

Install-Package SendGrid

Confira Introdução ao SendGrid gratuito para se registrar em uma conta gratuita do SendGrid.

Implementar o IEmailSender

Para implementar o IEmailSender, crie Services/EmailSender.cs com o código semelhante ao seguinte:

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

Configurar o aplicativo para dar suporte ao email

Adicione o seguinte código ao arquivo Program.cs:

  • Adicione o EmailSender como um serviço transitório.
  • Registre a instância de configuração 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();

Desabilitar a verificação de conta padrão quando Account.RegisterConfirmation tiver sido gerado por scaffolding

Esta seção só se aplica quando Account.RegisterConfirmation é gerado por scaffolding. Ignore esta seção se você não tiver gerado Account.RegisterConfirmation por scaffolding.

O usuário é redirecionado para a Account.RegisterConfirmation, no qual pode selecionar um link para que a conta seja confirmada. O padrão Account.RegisterConfirmation é usado apenas para teste. A verificação automática de conta deve ser desabilitada em um aplicativo de produção.

Para exigir uma conta confirmada e impedir o logon imediato durante o registro, defina DisplayConfirmAccountLink = false no arquivo /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs gerado por 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();
        }
    }
}

Essa etapa só é necessária quando Account.RegisterConfirmation é gerado por scaffolding. O RegisterConfirmation não gerado por scaffolding detecta automaticamente quando um IEmailSender foi implementado e registrado com o contêiner de injeção de dependência.

Registrar, confirmar email e redefinir senha

Execute o aplicativo Web e teste o fluxo de confirmação e recuperação de senha da conta.

  • Execute o aplicativo e registre um novo usuário
  • Verifique seu email para obter o link de confirmação da conta. Consulte Depurar email se você não receber o email.
  • Clique no link para confirmar seu email.
  • Entre com o endereço de email e senha.
  • Saia.

Testar redefinição de senha

  • Se você estiver conectado, selecione Sair.
  • Selecione o link Fazer logon e selecione o link Esqueceu sua senha?.
  • Insira o email que você usou para registrar a conta.
  • Um email com um link para redefinir sua senha é enviado. Verifique seu email e clique no link para redefinir sua senha. Depois que sua senha tiver sido redefinida com sucesso, você poderá entrar com seu email e a nova senha.

Reenviar confirmação de email

Selecione o link Reenviar confirmação de email na página Fazer logon.

Alterar o tempo limite de email e de atividade

O tempo limite de inatividade padrão é de 14 dias. O código a seguir define o tempo limite de inatividade como 5 dias:

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

Alterar todos os tempos de vida do token de proteção de dados

O código a seguir altera o tempo limite de todos os tokens de proteção de dados para 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.

Os tokens internos de usuário Identity (consulte AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) têm um tempo limite de um dia.

Alterar o tempo de vida do token de email

O tempo de vida do token padrão dos tokens Identity de usuário é de um dia. Esta seção mostra como alterar o tempo de vida do token de email.

Adicionar um DataProtectorTokenProvider<TUser> e 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);
    }
}

Adicione o provedor personalizado ao contêiner de serviço:

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 email

Se você não conseguir usar o email:

  • Defina um ponto de interrupção em EmailSender.Execute para verificar se SendGridClient.SendEmailAsync é chamado.
  • Crie um aplicativo de console para enviar emails usando um código semelhante a EmailSender.Execute.
  • Examine a página Atividade de Email.
  • Verifique sua pasta de spam.
  • Experimente outro alias de email em um provedor de email diferente (Microsoft, Yahoo, Gmail etc.)
  • Tente enviar para contas de email diferentes.

A melhor prática de segurança é não usar segredos de produção em ambiente de teste e de desenvolvimento. Se você publicar o aplicativo no Azure, defina os segredos do SendGrid como configurações de aplicativo no portal do Aplicativo Web do Azure. O sistema de configuração é definido para ler chaves de variáveis de ambiente.

Combinar contas de logon sociais e locais

Para concluir esta seção, primeiro você deve habilitar um provedor externo de autenticação. Consulte Autenticação do Facebook, Google e de provedor externo.

Você pode combinar contas locais e sociais clicando em seu link de email. Na sequência a seguir, “RickAndMSFT@gmail.com” é criado primeiro como um logon local. No entanto, você pode criar a conta como logon social primeiro e, em seguida, adicionar um logon local.

Web application: RickAndMSFT@gmail.com user authenticated

Clique no link Gerenciar. Observe 0 (logon social) externo associado a essa conta.

Manage view

Clique no link para outro serviço de logon e aceite as solicitações do aplicativo. Na imagem a seguir, o Facebook é o provedor externo de autenticação:

Manage your external logins view listing Facebook

As duas contas foram combinadas. Você pode fazer logon com qualquer conta. 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.

Habilitar a confirmação da conta depois que um site tiver usuários

Habilitar a confirmação da conta em um site com usuários bloqueia todos os usuários existentes. Os usuários existentes são bloqueados porque suas contas não foram confirmadas. Para contornar o bloqueio de usuário existente, use uma das seguintes abordagens:

  • Atualize o banco de dados para marcar todos os usuários existentes como sendo confirmados.
  • Confirme os usuários existentes. Por exemplo, enviar emails em lote com links de confirmação.

Pré-requisitos

SDK do .NET Core 3.0 ou posterior

Criar e testar um aplicativo Web com autenticação

Execute os comandos a seguir para criar um aplicativo Web com autenticação.

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

Execute o aplicativo, selecione o link Registrar e registre um usuário. Depois de registrado, você será redirecionado para a página /Identity/Account/RegisterConfirmation, que contém um link para simular a confirmação por email:

  • Selecione o link Click here to confirm your account.
  • Selecione o link Fazer logon e entre com as mesmas credenciais.
  • Selecione o link Hello YourEmail@provider.com!, que redireciona para a página /Identity/Account/Manage/PersonalData.
  • Selecione a guia Dados pessoais à esquerda e, em seguida, selecione Excluir.

Configurar um provedor de email

Neste tutorial, o SendGrid é usado para enviar email. Você pode usar outros provedores de email. Recomendamos usar o SendGrid ou outro serviço de email para enviar emails. É difícil configurar o SMTP para que o email não seja marcado como spam.

A conta do SendGrid pode exigir a adição de um Remetente.

Crie uma classe para buscar a chave de email segura. Para este exemplo, crie Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Configurar segredos do usuário do SendGrid

Defina o SendGridKey com a ferramenta Secret Manager. Por exemplo:

dotnet user-secrets set SendGridKey <SG.key>

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

No Windows, o Secret Manager armazena pares de chave/valor em um arquivo secrets.json no diretório %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

O conteúdo do arquivo secrets.json não é criptografado. A seguinte marcação mostra o arquivo secrets.json. O valor de SendGridKey foi removido.

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

Para obter mais informações, consulte o padrão de Opções e configuração.

Instalar o SendGrid

Este tutorial mostra como adicionar notificações por email através do SendGrid, mas você pode enviar emails usando o SMTP e outros mecanismos.

Instale o pacote SendGrid do NuGet.

No Console do Gerenciador de Pacotes, insira o seguinte comando:

Install-Package SendGrid

Confira Introdução ao SendGrid gratuito para se registrar em uma conta gratuita do SendGrid.

Implementar o IEmailSender

Para implementar o IEmailSender, crie Services/EmailSender.cs com o código semelhante ao seguinte:

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

Configurar a inicialização para dar suporte ao email

Adicione o seguinte código ao método ConfigureServices no arquivo Startup.cs:

  • Adicione o EmailSender como um serviço transitório.
  • Registre a instância de configuração 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();

Fazer scaffolding de RegisterConfirmation

Siga as instruções para Fazer scaffolding de Identity e fazer scaffolding de Account\RegisterConfirmation.

Desabilitar a verificação de conta padrão quando Account.RegisterConfirmation tiver sido gerado por scaffolding

Esta seção só se aplica quando Account.RegisterConfirmation é gerado por scaffolding. Ignore esta seção se você não tiver gerado Account.RegisterConfirmation por scaffolding.

O usuário é redirecionado para a Account.RegisterConfirmation, no qual pode selecionar um link para que a conta seja confirmada. O padrão Account.RegisterConfirmation é usado apenas para teste. A verificação automática de conta deve ser desabilitada em um aplicativo de produção.

Para exigir uma conta confirmada e impedir o logon imediato durante o registro, defina DisplayConfirmAccountLink = false no arquivo /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs gerado por 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();
        }
    }
}

Essa etapa só é necessária quando Account.RegisterConfirmation é gerado por scaffolding. O RegisterConfirmation não gerado por scaffolding detecta automaticamente quando um IEmailSender foi implementado e registrado com o contêiner de injeção de dependência.

Registrar, confirmar email e redefinir senha

Execute o aplicativo Web e teste o fluxo de confirmação e recuperação de senha da conta.

  • Execute o aplicativo e registre um novo usuário
  • Verifique seu email para obter o link de confirmação da conta. Consulte Depurar email se você não receber o email.
  • Clique no link para confirmar seu email.
  • Entre com o endereço de email e senha.
  • Saia.

Testar redefinição de senha

  • Se você estiver conectado, selecione Sair.
  • Selecione o link Fazer logon e selecione o link Esqueceu sua senha?.
  • Insira o email que você usou para registrar a conta.
  • Um email com um link para redefinir sua senha é enviado. Verifique seu email e clique no link para redefinir sua senha. Depois que sua senha tiver sido redefinida com sucesso, você poderá entrar com seu email e a nova senha.

Reenviar confirmação de email

No ASP.NET Core 5.0 e posterior, selecione o link Reenviar confirmação de email na página Fazer logon.

Alterar o tempo limite de email e de atividade

O tempo limite de inatividade padrão é de 14 dias. O código a seguir define o tempo limite de inatividade como 5 dias:

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

Alterar todos os tempos de vida do token de proteção de dados

O código a seguir altera o tempo limite de todos os tokens de proteção de dados para 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();
}

Os tokens internos de usuário Identity (consulte AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) têm um tempo limite de um dia.

Alterar o tempo de vida do token de email

O tempo de vida do token padrão dos tokens Identity de usuário é de um dia. Esta seção mostra como alterar o tempo de vida do token de email.

Adicionar um DataProtectorTokenProvider<TUser> e 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);
    }
}

Adicione o provedor personalizado ao contêiner de serviço:

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 email

Se você não conseguir usar o email:

  • Defina um ponto de interrupção em EmailSender.Execute para verificar se SendGridClient.SendEmailAsync é chamado.
  • Crie um aplicativo de console para enviar emails usando um código semelhante a EmailSender.Execute.
  • Examine a página Atividade de Email.
  • Verifique sua pasta de spam.
  • Experimente outro alias de email em um provedor de email diferente (Microsoft, Yahoo, Gmail etc.)
  • Tente enviar para contas de email diferentes.

A melhor prática de segurança é não usar segredos de produção em ambiente de teste e de desenvolvimento. Se você publicar o aplicativo no Azure, defina os segredos do SendGrid como configurações de aplicativo no portal do Aplicativo Web do Azure. O sistema de configuração é definido para ler chaves de variáveis de ambiente.

Combinar contas de logon sociais e locais

Para concluir esta seção, primeiro você deve habilitar um provedor externo de autenticação. Consulte Autenticação do Facebook, Google e de provedor externo.

Você pode combinar contas locais e sociais clicando em seu link de email. Na sequência a seguir, “RickAndMSFT@gmail.com” é criado primeiro como um logon local. No entanto, você pode criar a conta como logon social primeiro e, em seguida, adicionar um logon local.

Web application: RickAndMSFT@gmail.com user authenticated

Clique no link Gerenciar. Observe 0 (logon social) externo associado a essa conta.

Manage view

Clique no link para outro serviço de logon e aceite as solicitações do aplicativo. Na imagem a seguir, o Facebook é o provedor externo de autenticação:

Manage your external logins view listing Facebook

As duas contas foram combinadas. Você pode fazer logon com qualquer conta. 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.

Habilitar a confirmação da conta depois que um site tiver usuários

Habilitar a confirmação da conta em um site com usuários bloqueia todos os usuários existentes. Os usuários existentes são bloqueados porque suas contas não foram confirmadas. Para contornar o bloqueio de usuário existente, use uma das seguintes abordagens:

  • Atualize o banco de dados para marcar todos os usuários existentes como sendo confirmados.
  • Confirme os usuários existentes. Por exemplo, enviar emails em lote com links de confirmação.