Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 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: Pode armazenar o segredo num cofre de chaves para uso em qualquer ambiente, incluindo quando trabalha localmente no
Developmentambiente. 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, já terá um identificador de secretos de utilizador (<UserSecretsId>) no ficheiro do projeto (.csproj). No Visual Studio, pode saber se o ID dos segredos de utilizador está presente olhando para o painel de Propriedades quando o projeto está selecionado no Explorador 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.EmailAuthKeye 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 o exemplo nesta seção, o nome secreto é "EmailAuthKey."
Ao configurar o cofre de chaves no portal Entra ou do Azure:
Configure o cofre de chaves para usar o controle de acesso baseado em função (RABC) do Azure. Se você não estiver operando em uma Rede Virtual do Azure, inclusive para desenvolvimento e teste locais, confirme se o acesso público na etapa Rede está habilitado (marcado). Ao habilitar o acesso público, expõe-se apenas o ponto de extremidade do cofre de chaves. Contas autenticadas ainda são necessárias para o acesso.
Crie um Gestor Identity do Azure (ou adicione uma função ao Gestor Identity existente que planeias usar) com a função de Usuário de Segredos do Cofre de Chaves. Atribua o Gerenciado Identity ao Serviço de Aplicativo do Azure que está hospedando a implantação: Configurações>Identity>Adição atribuída> pelo usuário.
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 do desenvolvedor no Controle de Acesso (IAM) com a função Usuário de Segredos do Cofre de Chaves . Se você quiser usar a CLI do Azure por meio do Visual Studio, execute o
az logincomando no painel Developer PowerShell 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 secreto (exemplo: "EmailAuthKey") do Azure ao criar o cofre de chaves e o segredo.
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.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 registados no ficheiro de Program do projeto de servidor, obtenha e associe o segredo com a 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 não-Production, o exemplo anterior utiliza DefaultAzureCredential para simplificar a autenticação ao desenvolver aplicações implementadas no Azure, combinando credenciais usadas em ambientes de alojamento Azure com credenciais 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 ao sistema.
O exemplo anterior implica que o ID do Cliente Gerido Identity ({MANAGED IDENTITY CLIENT ID}), o ID do diretório (inquilino) {TENANT ID} e o URI do cofre de chaves ({VAULT URI}, exemplo: https://contoso.vault.azure.net/, barra final obrigatória) são fornecidos por valores codificados. Qualquer um ou todos esses valores podem ser fornecidos a partir da configuração de configurações do aplicativo. Por exemplo, o seguinte obtém o URI do vault do AzureAd nó de um arquivo de configurações do aplicativo e vaultUri pode ser usado na chamada para GetKeyVaultSecret no exemplo anterior:
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
Para obter mais informações, consulte Blazorde configuração do Core .
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(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 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 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).
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.Executepara verificar seSendEmailAsyncé chamado. - Crie um aplicativo de console para enviar e-mails usando um código semelhante ao
EmailSender.Executepara 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.