Confirmação de conta e 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
Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica como configurar um ASP.NET Core Blazor Web App com confirmação de e-mail e recuperação de senha.
Observação
Este artigo aplica-se apenas a Blazor Web Apps. Para implementar a confirmação de e-mail e a recuperação de senha para aplicativos Blazor WebAssembly autônomos com 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.
Espaço de nomes
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 e-mail
Neste artigo, o da API Transacional do Mailchimp é usado via Mandrill.net para enviar e-mails. Recomendamos o uso de um serviço de e-mail para enviar e-mails em vez de SMTP. SMTP é difícil de configurar e proteger corretamente. Seja qual for o serviço de email usado, acesse as orientações para aplicativos .NET, crie uma conta, configure uma chave de API para o serviço e instale todos os pacotes NuGet necessários.
Crie uma classe para manter a chave de API do provedor de e-mail secreto. O exemplo neste artigo usa uma classe chamada AuthMessageSenderOptions
com uma propriedade EmailAuthKey
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 para a chave de segurança do provedor de email
** Receba a chave de segurança do provedor de e-mail e use-a nas instruções seguintes.
Use uma ou ambas as seguintes abordagens para fornecer o segredo ao aplicativo:
- ferramenta Secret Manager: A ferramenta Secret Manager armazena dados privados na máquina local e só é usada durante o desenvolvimento local.
- Azure Key Vault: Você pode armazenar o segredo em um cofre de chaves para uso em qualquer ambiente, inclusive para o ambiente de desenvolvimento ao trabalhar localmente. Alguns desenvolvedores preferem usar cofres de chaves para implantações de preparação e produção e usar a ferramenta Secret Manager para desenvolvimento local.
É altamente recomendável que você evite armazenar segredos no código do projeto ou em arquivos de configuração. Use fluxos de autenticação seguros, como uma ou ambas as abordagens nesta seção.
Ferramenta Secret Manager
Se o projeto já tiver sido inicializado para a ferramenta Secret Manager, ele já terá um identificador de segredos do aplicativo (<AppSecretsId>
) em seu arquivo de projeto (.csproj
). No Visual Studio, é possível verificar a presença da ID dos segredos da aplicação examinando o painel Propriedades quando o projeto é selecionado no Gerenciador de Soluções. Se o aplicativo não tiver sido inicializado, execute o seguinte comando em um shell de comando aberto no diretório do projeto. No Visual Studio, você pode usar o prompt de comando do Developer PowerShell.
dotnet user-secrets init
Defina a chave da API com a ferramenta Secret Manager. No exemplo a seguir, o nome da chave é EmailAuthKey
para corresponder a AuthMessageSenderOptions.EmailAuthKey
e a chave é representada pelo espaço reservado {KEY}
. Execute o seguinte comando com a chave API:
dotnet user-secrets set "EmailAuthKey" "{KEY}"
Se estiver usando o Visual Studio, você poderá confirmar que o segredo está definido clicando com o botão direito do mouse no projeto de servidor no Gerenciador de Soluções e selecionando Gerenciar Segredos de Usuário.
Para obter mais informações, consulte Armazenamento seguro de segredos de aplicativos em desenvolvimento no ASP.NET Core.
Advertência
Não armazene segredos de aplicativos, cadeias de conexão, credenciais, senhas, números de identificação pessoal (PINs), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparação e produção, o código Blazor do lado do servidor e as APIs da Web devem usar fluxos de autenticação seguros que evitem 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 Secret Manager é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter com segurança dados confidenciais e credenciais.
Azure Key Vault
Azure Key Vault fornece uma abordagem segura para fornecer o segredo do cliente do aplicativo para o aplicativo.
Para criar um cofre de chaves e definir um segredo, consulte Sobre segredos do Cofre de Chaves do Azure (documentação do Azure), que vincula recursos para começar a usar o Cofre de Chaves do Azure. Para implementar o código nesta seção, registre o URI do cofre de chaves e o nome secreto do Azure ao criar o cofre de chaves e o segredo. Quando defines a política de acesso para o segredo no painel Políticas de acesso:
- Apenas é necessária a permissão secreta Obter.
- Selecione o aplicativo como o Principal para o segredo.
Confirme no portal do Azure ou do Entra que o aplicativo recebeu acesso ao segredo que você criou para a chave do provedor de email.
Importante
Um segredo do cofre de chaves é criado com uma data de validade. Certifique-se de controlar quando um segredo do cofre de chaves vai expirar e crie um novo segredo para o aplicativo antes que essa data passe.
Adicione a seguinte classe AzureHelper
ao projeto de servidor. O método GetKeyVaultSecret
recupera um segredo de um cofre de chaves. Ajuste o namespace (BlazorSample.Helpers
) para corresponder ao esquema de namespace do projeto.
Helpers/AzureHelper.cs
:
using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
{
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = tenantId,
SharedTokenCacheTenantId = tenantId
};
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
Onde os serviços são registados no ficheiro de Program
do projeto de servidor, obtenha e associe o segredo com a configuração de Opções :
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
var emailAuthKey = AzureHelper.GetKeyVaultSecret(
tenantId, vaultUri, "EmailAuthKey");
var authMessageSenderOptions =
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
.Bind(authMessageSenderOptions);
Se você deseja controlar o ambiente onde o código anterior opera, por exemplo, para evitar executar o código localmente porque optou por usar a ferramenta Secret Manager para desenvolvimento local, você pode encapsular o código anterior em uma instrução condicional que verifica o ambiente:
if (!context.HostingEnvironment.IsDevelopment())
{
...
}
Na seção AzureAd
de appsettings.json
no projeto de servidor, confirme a presença do TenantId
Entra ID do aplicativo e adicione a seguinte chave de configuração VaultUri
e valor, se ainda não estiver presente:
"VaultUri": "{VAULT URI}"
No exemplo anterior, o marcador de posição {VAULT URI}
é o Identificador de Recurso Uniforme (URI) do cofre de chaves. Inclua a barra final no URI.
Exemplo:
"VaultUri": "https://contoso.vault.azure.net/"
A configuração é usada para facilitar o fornecimento de cofres de chaves dedicados e nomes secretos com base nos arquivos de configuração ambiental do aplicativo. Por exemplo, você pode fornecer valores de configuração diferentes para appsettings.Development.json
em desenvolvimento, appsettings.Staging.json
durante o preparo e appsettings.Production.json
para a implantação de produção. Para obter mais informações, consulte ASP.NETde configuração do Core Blazor .
Implementar 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 Mandrill.net pacote NuGet ao projeto.
Adicione a seguinte classe EmailSender
para implementar IEmailSender<TUser>. No exemplo a seguir, ApplicationUser
é um IdentityUser. A marcação HTML da mensagem pode ser ainda mais personalizada. Desde que o message
passado para MandrillMessage
comece com o caractere <
, a API Mandrill.net assume 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 e-mail. 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 a aplicação para suportar o e-mail
No arquivo Program
, altere a implementação do remetente de e-mail para o 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 e-mail ...
- @if (emailConfirmationLink is not null)
- {
- ...
- }
- else
- {
<p>Please check your email to confirm your account.</p>
- }
@code {
- private string? emailConfirmationLink;
...
}
Ativar a confirmação da conta depois de um site ter utilizadores
Habilitar a confirmação de 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 são 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 e-mails em lote com links de confirmação.
Tempo limite para e-mail e atividade
O tempo limite de inatividade padrão é de 14 dias. O código a seguir define o tempo limite de inatividade para cinco dias com expiração deslizante:
builder.Services.ConfigureApplicationCookie(options => {
options.ExpireTimeSpan = TimeSpan.FromDays(5);
options.SlidingExpiration = true;
});
Alterar todas as durações de vida dos tokens de Proteção de Dados do ASP.NET Core
O código a seguir altera o período de 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 usuário Identity 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 a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, utilize a lista pendente de ramificações ou tags. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Alterar a vida útil do token de e-mail
O tempo de vida padrão dos tokens de utilizador Identity é um dia.
Observação
Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma libertação específica, use a lista suspensa Alternar entre ramificações ou tags. Para obter mais informações, consulte 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 e-mail, adicione um DataProtectorTokenProvider<TUser> personalizado 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>>();
Solução de problemas
Se não conseguir que o e-mail funcione:
- Defina um ponto de interrupção no
EmailSender.Execute
para verificar seSendEmailAsync
é chamado. - Crie um aplicativo de console para enviar e-mails usando um código semelhante ao
EmailSender.Execute
para depurar o problema. - Analise as páginas do histórico de e-mail da conta no site do provedor de e-mail.
- Verifique se há mensagens na sua pasta de spam.
- Experimente outro alias de e-mail num fornecedor de e-mail diferente, como a Microsoft, o Yahoo ou o Gmail.
- Tente enviar para diferentes contas de e-mail.