Partilhar via


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

Este artigo explica como configurar um aplicativo Web do ASP.NET Core Blazor com confirmação de email e recuperação de senha.

Namespace

O namespace do aplicativo usado pelo exemplo neste artigo é BlazorSample. Atualize os exemplos de código para usar o namespace do seu aplicativo.

Selecionar e configurar um provedor de email

Neste artigo, a API Transacional do Mailchimp é usada via Mandrill.net para enviar emails. É recomendável usar um serviço de email para enviar email em vez de SMTP. SMTP é difícil de configurar e proteger corretamente. Seja qual for o serviço de email usado, acesse suas orientações para aplicativos .NET, crie uma conta, configure uma chave de API para seu serviço e instale os pacotes NuGet necessários.

Crie uma classe para buscar a chave de API de email segura. O exemplo neste artigo usa uma classe nomeada AuthMessageSenderOptions com uma propriedade EmailAuthKey para manter a chave.

AuthMessageSenderOptions:

namespace BlazorSample;

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

Registre a instância de configuração AuthMessageSenderOptions no arquivo Program:

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

Configurar um segredo do usuário para a chave de segurança do provedor

Defina a chave com a ferramenta secret-manager. No exemplo a seguir, o nome da chave é EmailAuthKey e a chave é representada pelo espaço reservado {KEY}. Em um shell de comando, navegue até a pasta raiz do aplicativo e execute o seguinte comando com a chave de API:

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

Para obter mais informações, confira Armazenamento seguro de segredos do aplicativo em desenvolvimento no ASP.NET Core.

Implementa IEmailSender

Implementar IEmailSender para o provedor. O exemplo a seguir é baseado na API Transacional do Mailchimp usando Mandrill.net. Para um provedor diferente, consulte sua documentação sobre como implementar o envio de uma mensagem no método Execute.

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(ApplicationUser user, string email, 
        string confirmationLink) => SendEmailAsync(email, "Confirm your email", 
        $"Please confirm your account by " +
        "<a href='{confirmationLink}'>clicking here</a>.");

    public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, 
        string resetLink) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

    public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, 
        string resetCode) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password using the following code: {resetCode}");

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

Observação

O conteúdo do corpo das mensagens pode exigir codificação especial para o provedor de serviços de email. Se os links no corpo da mensagem não puderem ser seguidos, confira a documentação do provedor de serviços.

Configurar o aplicativo para dar suporte ao email

No arquivo Program, altere a implementação do remetente de email para EmailSender:

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

Remova o IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) do aplicativo.

No componente RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor), remova o bloco condicional no bloco @code que verifica se o EmailSender é um IdentityNoOpEmailSender:

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

Também no componente RegisterConfirmation, remova a marcação Razor e o código para verificar o campo emailConfirmationLink, deixando apenas a linha instruindo o usuário a verificar seu email...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

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 com expiração variável (sliding expiration):

builder.Services.ConfigureApplicationCookie(options => {
    options.ExpireTimeSpan = TimeSpan.FromDays(5);
    options.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:

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

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

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).

Alterar o tempo de vida do token de email

O tempo de vida do token padrão dos tokens de usuárioIdentity é de um dia.

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).

Para alterar a vida útil do token de email, adicione um personalizado DataProtectorTokenProvider<TUser> e 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);
    }
}

Configure os serviços para usar o provedor de token personalizado no arquivo 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>>();

Solucionar problemas

Se você não conseguir usar o email:

  • Defina um ponto de interrupção em EmailSender.Execute para verificar se SendEmailAsync é chamado.
  • Crie um aplicativo de console para enviar emails usando código semelhante a EmailSender.Execute para depurar o problema.
  • Revise as páginas do histórico de email da conta no site do provedor de email.
  • Verifique se há mensagens na pasta de spam.
  • Experimente outro alias de email em um provedor de email diferente, como Microsoft, Yahoo ou Gmail.
  • Tente enviar para contas de email diferentes.

Aviso

Não use segredos de produção em teste e desenvolvimento. Se você publicar o aplicativo no Azure, defina os segredos 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.

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 confirmados.
  • Confirme os usuários existentes. Por exemplo, enviar emails em lote com links de confirmação.

Recursos adicionais