Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Este artigo explica como configurar um ASP.NET Core Blazor Web App com confirmação de email e recuperação de senha.
Observação
Este artigo só se aplica 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 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 email e use-a no seguinte guia.
Use uma ou ambas as abordagens a seguir para fornecer o segredo ao aplicativo:
- ferramenta Gerenciador de Segredos: A ferramenta Gerenciador de Segredos armazena dados privados no computador local e é usada apenas durante o desenvolvimento local.
-
Azure Key Vault: você pode armazenar o segredo em um Key Vault para uso em qualquer ambiente, inclusive quando estiver trabalhando localmente no
Developmentambiente. Alguns desenvolvedores preferem usar cofres de chaves para implantações de produção e preparação e usam a ferramenta Gerenciador de Segredos para desenvolvimento local.
É altamente recomendável que você evite armazenar segredos em arquivos de configuração ou código do projeto. Use fluxos de autenticação seguros, como uma ou ambas as abordagens nesta seção.
Ferramenta Do Gerenciador de Segredos
Se o projeto já tiver sido inicializado para a ferramenta gerenciador de segredos, ele já terá um identificador de segredos do usuário (<UserSecretsId>) em seu arquivo de projeto (.csproj). No Visual Studio, você pode informar se a ID de segredos do usuário 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 seguinte comando 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 é EmailAuthKey para corresponder AuthMessageSenderOptions.EmailAuthKey e a chave é representada pelo espaço reservado {KEY}. Execute o comando a seguir 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 de 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 (personal identification numbers, números de identificação pessoal), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente. Isso é sempre inseguro. Em ambientes de teste/preparo e produção, o código do lado do servidor Blazor 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.
Azure Key Vault
O Azure Key Vault fornece uma abordagem segura para disponibilizar o segredo do cliente ao aplicativo.
Para criar um cofre de chaves e definir um segredo, consulte Sobre os segredos do Azure Key Vault (documentação do Azure), que referencia recursos para iniciar o uso do Azure Key Vault. Para o exemplo nesta seção, o nome do segredo é "EmailAuthKey.
Ao configurar o cofre de chaves no portal do Microsoft Entra ou do Azure:
Configure o cofre de chaves para usar o RABC (controle de acesso baseado em função) do Azure. Se você não estiver operando em uma Rede Virtual do Azure, inclusive para desenvolvimento local e teste, confirme se o acesso público na etapa rede está habilitado (verificado). A habilitação do acesso público só expõe o ponto de extremidade do cofre de chaves. Contas autenticadas ainda são necessárias para acesso.
Crie um Recurso Gerenciado Identity do Azure (ou adicione uma função ao Recurso Gerenciado Identity existente que você planeja usar) com a função Usuário de Segredos do Key Vault. Atribua o Identity Gerenciado ao Serviço de Aplicativo do Azure que está hospedando a implantação: Configurações>Identity>Atribuído pelo usuário>Adicionar.
Observação
Se você também planeja executar um aplicativo localmente com um usuário autorizado para acesso ao cofre de chaves usando a CLI do Azure ou a Autenticação de Serviço do Azure do Visual Studio, adicione sua conta de usuário do Azure para desenvolvedor no Controle de Acesso (IAM) com a função de Usuário de Segredos do Key Vault . Se você quiser usar a CLI do Azure por meio do Visual Studio, execute o
az logincomando no painel do PowerShell do Desenvolvedor e siga os prompts para autenticar com o locatário.
Para implementar o código nesta seção, registre o URI do cofre de chaves (exemplo: "https://contoso.vault.azure.net/", barra à direita necessária) e o nome do segredo (exemplo: "EmailAuthKey") do Azure ao criar o cofre de chaves e o segredo.
Importante
Um segredo de cofre de chaves é criado com uma data de expiração. Monitore quando um segredo do cofre de chaves expirará e crie um segredo para o aplicativo antes que essa data passe.
Adicione a seguinte classe AzureHelper ao projeto do 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.Core;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string vaultUri,
TokenCredential credential, string secretName)
{
var client = new SecretClient(new Uri(vaultUri), credential);
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
Onde os serviços são registrados no arquivo Program do projeto do servidor, obtenha e vincule o segredo com Configuração de opções:
TokenCredential? credential;
if (builder.Environment.IsProduction())
{
credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
// Local development and testing only
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = "{TENANT ID}",
SharedTokenCacheTenantId = "{TENANT ID}"
};
credential = new DefaultAzureCredential(options);
}
var emailAuthKey = AzureHelper.GetKeyVaultSecret("{VAULT URI}", credential,
"EmailAuthKey");
var authMessageSenderOptions =
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
.Bind(authMessageSenderOptions);
Observação
Em ambientes que não utilizam Production, o exemplo anterior usa DefaultAzureCredential para simplificar a autenticação durante o desenvolvimento de aplicativos implantados no Azure, combinando credenciais utilizadas em ambientes de hospedagem no Azure com aquelas usadas no desenvolvimento local. Para obter mais informações, consulte Autenticar aplicativos .NET hospedados no Azure em recursos do Azure usando uma identidade gerenciada atribuída pelo sistema.
O exemplo anterior implica que a ID do Cliente Gerenciado Identity ({MANAGED IDENTITY CLIENT ID}), a ID do diretório (locatário) ({TENANT ID}) e o URI do cofre de chaves ({VAULT URI}por exemplo: https://contoso.vault.azure.net/, barra à direita necessária) são fornecidos por valores embutidos em código. Qualquer um ou todos esses valores podem ser fornecidos na configuração de configurações do aplicativo. Por exemplo, o seguinte obtém o URI do Key Vault a partir do nó de um arquivo de configurações de aplicativo, e AzureAd pode ser usado na chamada para vaultUri no exemplo anterior.
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
Para obter mais informações, consulte ASP.NET Core Blazor configuration.
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 classe EmailSender para implementar IEmailSender<TUser>. No seguinte exemplo, 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(ApplicationUser 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(ApplicationUser 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(ApplicationUser 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 seguinte código 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 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ário Identity é 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 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>>();
Solucionar problemas
Se você não conseguir usar o email:
- Defina um ponto de interrupção em
EmailSender.Executepara verificar seSendEmailAsyncé chamado. - Crie um aplicativo de console para enviar emails usando código semelhante a
EmailSender.Executepara 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.