Compartilhar via


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

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

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

Observação

Este artigo se aplica apenas a Blazor Web Apps. Para implementar a confirmação de email e a recuperação de senha para aplicativos autônomos Blazor WebAssembly com o ASP.NET Core Identity, consulte Confirmação de conta e recuperação de senha no ASP.NET Core Blazor WebAssembly com ASP.NET Core Identity.

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 manter a chave de API do provedor de email secreto. O exemplo neste artigo usa uma classe chamada AuthMessageSenderOptions com uma EmailAuthKey propriedade para manter a chave.

AuthMessageSenderOptions.cs:

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

Se o projeto já tiver sido inicializado para a ferramenta Gerenciador de segredos, ele já terá um identificador de segredos do aplicativo (<AppSecretsId>) em seu arquivo de projeto (.csproj). No Visual Studio, você pode saber se a ID de segredos do aplicativo está presente examinando o painel Propriedades quando o projeto é selecionado no Gerenciador de Soluções. Se o aplicativo não tiver sido inicializado, execute o comando a seguir em um shell de comando aberto no diretório do projeto. No Visual Studio, você pode usar o prompt de comando do PowerShell do desenvolvedor.

dotnet user-secrets init

Defina a chave de API com a ferramenta Gerenciador de segredos. No exemplo a seguir, o nome da chave deve EmailAuthKey corresponder AuthMessageSenderOptions.EmailAuthKeye a chave é representada pelo espaço reservado {KEY} . Execute o seguinte comando com a chave de API:

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

Se estiver usando o Visual Studio, você poderá confirmar se o segredo está definido clicando com o botão direito do mouse no projeto do servidor no Gerenciador de Soluções e selecionando Gerenciar Segredos do Usuário.

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

Aviso

Não armazene segredos de aplicativo, cadeias de conexão, credenciais, senhas, PINs (números de identificação pessoal), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparo e produção, o código do lado do Blazor servidor e as APIs Web devem usar fluxos de autenticação seguros que evitam a manutenção de credenciais no código do projeto ou nos arquivos de configuração. Fora dos testes de desenvolvimento local, recomendamos evitar o uso de variáveis de ambiente para armazenar dados confidenciais, pois as variáveis de ambiente não são a abordagem mais segura. Para testes de desenvolvimento local, a ferramenta Gerenciador de segredos é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter dados e credenciais confidenciais com segurança.

Implementa IEmailSender

O exemplo a seguir é baseado na API Transacional do Mailchimp usando Mandrill.net. Para um provedor diferente, consulte a documentação sobre como implementar o envio de uma mensagem de email.

Adicione o pacote NuGet Mandrill.net ao projeto.

Adicione a seguinte EmailSender classe para implementar IEmailSender<TUser>. No exemplo a seguir, ApplicationUser é um IdentityUser. A marcação HTML da mensagem pode ser personalizada ainda mais. Desde que o message passado para MandrillMessage comece com o < caractere, a API do Mandrill.net pressupõe que o corpo da mensagem é composto em 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);
    }
}

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 na mensagem de email, consulte a documentação do provedor de serviços para solucionar o problema.

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;

     ...
}

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.

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 cinco dias com expiração variável:

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 do ASP.NET Core

O código a seguir altera o tempo limite dos tokens de proteção de dados para três horas:

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

Os tokens de Identity usuário internos (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 arquivo 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.

Recursos adicionais