Kontobestätigung und Kennwortwiederherstellung in ASP.NET Core Blazor

In diesem Artikel wird erläutert, wie Sie eine ASP.NET Core Blazor-Web-App mit E-Mail-Bestätigung und Kennwortwiederherstellung konfigurieren.

Namespace

Im Beispiel in diesem Artikel wird der App-Namespace BlazorSample verwendet. Aktualisieren Sie die Codebeispiele, um den Namespace Ihrer App zu verwenden.

Auswählen und Konfigurieren eines E-Mail-Anbieters

In diesem Artikel wird die Transactional API von Mailchimp über Mandrill.net zum Senden von E-Mails verwendet. Es wird empfohlen, einen E-Mail-Dienst statt SMTP zum Senden von E-Mails zu verwenden. SMTP ist schwer zu konfigurieren und zu schützen. Je nachdem, welchen E-Mail-Dienst Sie verwenden, befolgen Sie dessen Anleitungen für .NET-Apps, erstellen ein Konto, konfigurieren einen API-Schlüssel für den Dienst und installieren alle erforderlichen NuGet-Pakete.

Erstellen Sie eine Klasse, um den sicheren E-Mail-API-Schlüssel abzurufen. Im Beispiel in diesem Artikel wird eine Klasse namens AuthMessageSenderOptions mit einer EmailAuthKey-Eigenschaft verwendet, die den Schlüssel enthält.

AuthMessageSenderOptions:

namespace BlazorSample;

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

Registrieren Sie die AuthMessageSenderOptions-Konfigurationsinstanz in der Program-Datei:

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

Konfigurieren eines Benutzergeheimnisses für den Sicherheitsschlüssel des Anbieters

Legen Sie den Schlüssel mit dem Tool secret-manager fest. Im folgenden Beispiel lautet der Schlüsselname EmailAuthKey, und der Schlüssel wird durch den Platzhalter {KEY} dargestellt. Navigieren Sie in einer Befehlsshell zum Stammordner der App, und führen Sie den folgenden Befehl mit dem API-Schlüssel aus:

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

Weitere Informationen finden Sie unter Sichere Speicherung von App-Geheimnissen bei der Entwicklung in ASP.NET Core.

Implementieren Sie IEmailSender.

Implementieren Sie IEmailSender für den Anbieter. Das folgende Beispiel basiert auf der Transactional API von Mailchimp unter Verwendung von Mandrill.net. Wenn Sie einen anderen Anbieter verwenden, lesen Sie in dessen Dokumentation nach, wie das Senden einer Nachricht in der Execute-Methode implementiert wird.

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

Hinweis

Textkörperinhalte für Nachrichten erfordern möglicherweise eine spezielle Codierung für den E-Mail-Dienstanbieter. Wenn Links im Nachrichtentext nicht befolgt werden können, lesen Sie in der Dokumentation des Dienstanbieters nach.

Konfigurieren der App zur Unterstützung von E-Mails

Ändern Sie in der Datei Program die Implementierung des E-Mail-Absenders in EmailSender:

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

Entfernen Sie IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) aus der App.

Entfernen Sie in der Komponente RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor) den Bedingungsblock aus dem @code-Block, der überprüft, ob EmailSender ein IdentityNoOpEmailSender-Element ist:

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

Entfernen Sie in der Komponente RegisterConfirmation ebenfalls das Razor-Markup und den Code zum Überprüfen des Felds emailConfirmationLink, und lassen Sie nur die Zeile, in der oder die Benutzer*in angewiesen wird, seine oder ihre E-Mail zu überprüfen.

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

@code {
-    private string? emailConfirmationLink;

     ...
}

Timeout für E-Mails und Aktivitäten

Der Standardzeitraum für die Inaktivität bis zum Timeout beträgt 14 Tage. Der folgende Code legt das Inaktivitätstimeout auf 5 Tage mit variablem Ablauf fest:

builder.Services.ConfigureApplicationCookie(options => {
    options.ExpireTimeSpan = TimeSpan.FromDays(5);
    options.SlidingExpiration = true;
});

Ändern der Lebensdauer aller Datenschutztoken

Der folgende Code ändert den Timeoutzeitraum für alle Datenschutztoken auf 3 Stunden:

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

Die integrierten Identity-Benutzertoken (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) weisen ein Timeout von einem Tag auf.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Ändern der Lebensdauer von E-Mail-Token

Die Standardlebensdauer von Identity-Benutzertoken beträgt einen Tag.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Um die Lebensdauer des E-Mail-Tokens zu ändern, fügen Sie benutzerdefinierte DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptions hinzu:

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

Konfigurieren Sie die Dienste in der Datei Program für die Verwendung des benutzerdefinierten Tokenanbieters:

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

Problembehandlung

Wenn E-Mails nicht funktionieren:

  • Legen Sie einen Breakpoint in EmailSender.Execute fest, um zu überprüfen, ob SendEmailAsync aufgerufen wird.
  • Erstellen Sie eine Konsolen-App, um E-Mails mithilfe von Code zu senden, der EmailSender.Execute zum Debuggen des Problems ähnelt.
  • Überprüfen Sie die E-Mail-Verlaufsseiten des Kontos auf der Website des E-Mail-Anbieters.
  • Überprüfen Sie Ihren Spamordner auf Nachrichten.
  • Versuchen Sie es mit einem anderen E-Mail-Alias bei einem anderen E-Mail-Anbieter, zum Beispiel Microsoft, Yahoo oder Gmail.
  • Versuchen Sie, die E-Mail an andere E-Mail-Konten zu senden.

Warnung

Verwenden Sie keine Produktionsgeheimnisse für Test- und Entwicklungszwecke. Wenn Sie die App in Azure veröffentlichen, legen Sie die Geheimnisse im Azure-Web-App-Portal als Anwendungseinstellungen fest. Das Konfigurationssystem ist zum Lesen von Schlüsseln aus Umgebungsvariablen eingerichtet.

Aktivieren der Kontobestätigung, nachdem eine Website Benutzer*innen enthält

Durch Aktivieren der Kontobestätigung auf einer Website mit Benutzer*innen werden alle vorhandenen Benutzer*innen gesperrt. Vorhandene Benutzer werden gesperrt, da ihre Konten nicht bestätigt wurden. Verwenden Sie einen der folgenden Ansätze, um die Sperrung vorhandener Benutzer*innen zu umgehen:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer*innen als bestätigt zu kennzeichnen.
  • Bestätigen Sie die vorhandenen Benutzer*innen. Senden Sie beispielsweise Batch-E-Mails mit Bestätigungslinks.

Zusätzliche Ressourcen