Account confirmation and password recovery in ASP.NET Core (Kontobestätigung und Kennwortwiederherstellung in ASP.NET Core)

Von Rick Anderson, Ponant und Joe Audette

In diesem Tutorial erfahren Sie, wie Sie eine ASP.NET Core-App mit E-Mail-Bestätigung und Kennwortzurücksetzung erstellen. Dieses Tutorial ist kein Artikel für Anfänger. Sie sollten mit den folgenden Punkten vertraut sein:

Blazor Anleitungen, die den Leitfaden in diesem Artikel hinzufügen oder ersetzen, finden Sie in den folgenden Ressourcen:

Voraussetzungen

Erstellen und Testen einer Web-App mit Authentifizierung

Führen Sie die folgenden Befehle aus, um eine Web-App mit Authentifizierung zu erstellen:

dotnet new webapp -au Individual -o WebPWrecover
cd WebPWrecover
dotnet run

Registrieren eines Benutzers mit simulierter E-Mail-Bestätigung

Führen Sie die App aus, wählen Sie den Link Registrieren aus, und registrieren Sie eine*n Benutzer*in.

Nach Abschluss der /Identity/Account/RegisterConfirmation Registrierung werden Sie zur Seite umgeleitet, die einen Link zum Simulieren der E-Mail-Bestätigung enthält.

  1. Wählen Sie den Link Click here to confirm your account aus.

  2. Wählen Sie den Link Login (Anmelden) aus, und melden Sie sich mit denselben Anmeldeinformationen an.

  3. Wählen Sie den Link Hello YourEmail@provider.com! aus, der eine Weiterleitung zur Seite /Identity/Account/Manage/PersonalData durchführt.

  4. Wählen Sie die Registerkarte " Persönliche Daten " und dann "Löschen" aus.

Der Click here to confirm your account Link wird angezeigt, da die IEmailSender-Schnittstelle noch nicht implementiert und beim Container zum Einfügen von Abhängigkeiten registriert ist. Weitere Informationen finden Sie in der RegisterConfirmation-Quelle.

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)).

Konfigurieren eines E-Mail-Anbieters

In diesem Lernprogramm wird Twilio SendGrid zum Senden von E-Mails verwendet. Zum Senden von E-Mails sind ein SendGrid-Konto und ein Schlüssel erforderlich. Es wird empfohlen, SendGrid oder einen anderen E-Mail-Dienst anstelle von SMTP zu verwenden, um E-Mails zu senden. Es ist schwierig, SMTP richtig zu schützen und einzurichten.

Das SendGrid-Konto erfordert möglicherweise das Hinzufügen eines Absenders.

Erstellen Sie eine Klasse, um den sicheren E-Mail-Schlüssel abzurufen. Erstellen Sie für dieses Beispiel die Datei "Dienste/AuthMessageSenderOptions.cs ":

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzergeheimnissen

Legen Sie den SendGridKey Wert mit dem Tool für geheimen Manager fest. Beispiel:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

In Windows speichert der geheime Manager Schlüssel-Wert-Paare in einer Datei secrets.json im Verzeichnis %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

Der Inhalt der secrets.json Datei ist nicht verschlüsselt. Das folgende Markup zeigt die secrets.json Datei. Der SendGridKey Wert wird aus dem Beispiel entfernt.

{
  "SendGridKey": "<key removed>"
}

Weitere Informationen finden Sie im Muster Options und Configuration in ASP.NET Core.

Installieren von SendGrid

In diesem Tutorial wird gezeigt, wie Sie E-Mail-Benachrichtigungen über SendGrid hinzufügen. Es können aber auch andere E-Mail-Anbieter verwendet werden.

Installieren Sie das NuGet-Paket SendGrid:

Geben Sie in der Paket-Manager-Konsole den folgenden Befehl ein:

Install-Package SendGrid

Um sich für ein kostenloses SendGrid-Konto zu registrieren, beginnen Sie mit dem Senden mit einer kostenlosen SendGrid-E-Mail-API-Testversion.

Implementieren von IEmailSender

Um die Schnittstelle zu implementieren, erstellen Sie die IEmailSenderDatei "Services/EmailSender.cs " mit Code, der dem folgenden Beispiel ähnelt:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.SendGridKey))
        {
            throw new Exception("Null SendGridKey");
        }
        await Execute(Options.SendGridKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

Konfigurieren der App zur Unterstützung von E-Mails

Fügen Sie der datei Program.cs den folgenden Code hinzu, der die folgenden Aufgaben ausführt:

  • Fügt die EmailSender Instanz als vorübergehenden Dienst hinzu.
  • Registriert die AuthMessageSenderOptions Konfigurationsinstanz.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Standardmäßige Kontobestätigung deaktivieren, wenn Account.RegisterConfirmation per Scaffolding generiert wird

Wenn Account.RegisterConfirmation ein Gerüst erstellt wird, führen Sie die Anweisungen in diesem Abschnitt aus.

Important

Wenn Account.RegisterConfirmationkein Gerüst erstellt wurde, überspringen Sie die folgenden Anweisungen, und fahren Sie mit dem nächsten Abschnitt fort.

Der Benutzer wird auf die /Identity/Account/RegisterConfirmationSeite umgeleitet, auf der er einen Link auswählen kann, mit dem das Konto bestätigt werden kann. Der Standardwert Account.RegisterConfirmation wird nur für Tests verwendet. Die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu verlangen und die unmittelbare Anmeldung bei der Registrierung zu verhindern, setzen Sie DisplayConfirmAccountLink = false in der generierten Datei /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs fest:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Dieser Schritt ist nur erforderlich, wenn Account.RegisterConfirmation per Scaffolding generiert wird.

Das nicht gerüstete RegisterConfirmation erkennt automatisch, wenn ein IEmailSender implementiert und beim Abhängigkeitseinfügungscontainer registriert wird.

Registrieren, Bestätigen per E-Mail und Zurücksetzen des Kennworts

Starten Sie die Web-App, und testen Sie den Ablauf zur Kontobestätigung und Kennwortwiederherstellung.

  1. Führen Sie die App aus, und registrieren Sie einen neuen Benutzer.

  2. Überprüfen Sie Ihre E-Mail auf einen Link zur Kontobestätigung. Wenn Sie die E-Mail nicht erhalten, lesen Sie den Abschnitt " Debug-E-Mail " zur Problembehandlung.

  3. Wählen Sie den Link aus, und bestätigen Sie Ihre E-Mail.

  4. Melden Sie sich mit Ihrer E-Mail-Adresse und Ihrem Kennwort an.

  5. Melden Sie sich ab.

Passwortzurücksetzung testen

  1. Wenn Sie angemeldet sind, wählen Sie Logout (Abmelden) aus.

  2. Wählen Sie den Link "Anmelden" und dann den Link " Kennwort vergessen" aus.

  3. Geben Sie die E-Mail-Adresse ein, die Sie zum Registrieren des Kontos verwendet haben. Die App sendet eine E-Mail mit einem Link, um Ihr Kennwort zurückzusetzen.

  4. Wechseln Sie zur gesendeten E-Mail-Nachricht.

  5. Wählen Sie den Link aus, und setzen Sie Ihr Kennwort zurück.

Nachdem Ihr Kennwort erfolgreich zurückgesetzt wurde, können Sie sich mit Ihrer E-Mail und ihrem neuen Kennwort anmelden.

Erneutes Senden von E-Mail-Bestätigungen

In diesem Abschnitt wird der Code beschrieben, der den E-Mail-Bestätigungsprozess und zugehörige Aufgaben unterstützt.

  • Wählen Sie zunächst auf der Anmeldeseite den Link "E-Mail-Bestätigung erneut senden" aus.

E-Mail- und Aktivitäts-Timeout ändern

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

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity

Ändern der Lebensdauer aller Datenschutztoken

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

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity.

Die integrierten Identity Benutzertoken (siehe AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs Source) verfügen über ein eintägiges Timeout.

Ändern der Lebensdauer von E-Mail-Token

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

Der folgende Code zeigt, wie Die Lebensdauer des E-Mail-Tokens geändert wird.

Fügen Sie eine benutzerdefinierte DataProtectorTokenProvider<TUser> Klasse und DataProtectionTokenProviderOptions Klasse hinzu:

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

Fügen Sie dem Dienstcontainer den benutzerdefinierten Anbieter hinzu:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;
using WebPWrecover.TokenProviders;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(config =>
{
    config.SignIn.RequireConfirmedEmail = true;
    config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
        new TokenProviderDescriptor(
            typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
    config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
}).AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

// Code removed for brevity.

Debuggen von E-Mails

Wenn der E-Mail-Prozess nicht wie erwartet funktioniert, probieren Sie die folgenden Schritte zur Problembehandlung aus:

  • Legen Sie einen Haltepunkt in der EmailSender.Execute Methode fest, und überprüfen Sie, ob die SendGridClient.SendEmailAsync Methode aufgerufen wird.

  • Erstellen Sie eine Konsolen-App, um E-Mails mithilfe eines ähnlichen Codes zu senden.EmailSender.Execute

  • Überprüfen Sie die Seite mit E-Mail-Aktivitäten.

  • Überprüfen Sie Ihren Spamordner.

  • Probieren Sie einen anderen E-Mail-Alias für einen anderen E-Mail-Anbieter aus, z. B. Microsoft, Yahoo, Gmail usw.

  • Versuchen Sie, die E-Mail an andere E-Mail-Konten zu senden.

Tipp

Eine bewährte Sicherheitsmethode besteht darin, keine Produktionsgeheimnisse bei Tests und der Entwicklung zu verwenden. Wenn Sie die App in Azure veröffentlichen, legen Sie die SendGrid-Geheimnisse im Azure-Web-App-Portal als Anwendungseinstellungen fest. Das Konfigurationssystem ist zum Lesen von Schlüsseln aus Umgebungsvariablen eingerichtet.

Kombinieren von Konten bei sozialen Medien und lokalen Anmeldekonten

Um diesen Abschnitt abzuschließen, müssen Sie zunächst einen externen Authentifizierungsanbieter aktivieren. Weitere Informationen finden Sie unter Verwenden von externen Anmeldeanbietern mit Identity in ASP.NET Core.

In dieser Sequenz wird die E-Mail-Adresse RickAndMSFT@gmail.com zuerst als lokale Anmeldung erstellt. Sie können das Konto jedoch zuerst als anmeldung für soziale Netzwerke erstellen und dann eine lokale Anmeldung hinzufügen.

  1. Um lokale und soziale Konten zu kombinieren, wählen Sie den Link "E-Mail-Adresse" aus.

    Screenshot, der zeigt, wie Sie den Link

  2. Wählen Sie auf der Seite "Konto verwalten " den Link "Verwalten" aus.

    Beachten Sie, dass dem authentifizierten Konto derzeit null (0) externe (soziale Anmeldungen) zugeordnet sind.

    Screenshot, der null externe (soziale Anmeldungen) zeigt, die mit dem authentifizierten Konto verknüpft sind. Der Link

  3. Wählen Sie auf der Seite "Externe Anmeldungen verwalten " den Link zu einem anderen Anmeldedienst aus. Folgen Sie den Dienstaufforderungen, und akzeptieren Sie die App-Anforderungen.

    In der folgenden Abbildung wird Facebook als externer Authentifizierungsanbieter hinzugefügt:

    Screenshot, der zeigt, dass Facebook als externe (soziale) Anmeldung für das authentifizierte Konto hinzugefügt wurde.

Die Authentifizierung für die E-Mail-Adresse des Benutzers kombiniert jetzt lokale und externe Konten (soziale Netzwerke). Der Benutzer kann sich mit beiden Konten anmelden.

Tipp

Es empfiehlt sich, Ihren Benutzern zu empfehlen, Ihrer App ein lokales Konto hinzuzufügen. Dieser Ansatz kann dazu beitragen, den fortgesetzten Zugriff sicherzustellen, falls der Authentifizierungsdienst für soziale Netzwerke deaktiviert ist, oder er verliert den Zugriff auf sein soziales Konto.

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

Wenn Sie die Kontobestätigung auf einer Website mit vorhandenen Benutzern aktivieren, sperren Sie sie, da ihre Konten nicht bestätigt werden.

Um das Problem der vorhandenen Benutzersperre zu umgehen, verwenden Sie einen der folgenden Ansätze:

  • 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.

Voraussetzungen

.NET Core 3.0 oder höher SDK

Erstellen und Testen einer Web-App mit Authentifizierung

Führen Sie die folgenden Befehle aus, um eine Web-App mit Authentifizierung zu erstellen.

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet run

Führen Sie die App aus, wählen Sie den Link Registrieren aus, und registrieren Sie eine*n Benutzer*in. Nach der Registrierung werden Sie zur /Identity/Account/RegisterConfirmation-Seite weitergeleitet, die einen Link zum Simulieren der E-Mail-Bestätigung enthält:

  • Wählen Sie den Link Click here to confirm your account aus.
  • Wählen Sie den Link Login (Anmelden) aus, und melden Sie sich mit denselben Anmeldeinformationen an.
  • Wählen Sie den Link Hello YourEmail@provider.com! aus, der eine Weiterleitung zur Seite /Identity/Account/Manage/PersonalData durchführt.
  • Wählen Sie links die Registerkarte Personal data (Persönliche Daten) und dann Delete (Löschen) aus.

Konfigurieren eines E-Mail-Anbieters

In diesem Tutorial wird zum Senden von E-Mails SendGrid verwendet. Sie können andere E-Mail-Anbieter verwenden. Es wird empfohlen, zum Senden von E-Mails SendGrid oder einen anderen E-Mail-Dienst zu verwenden. SMTP ist schwierig zu konfigurieren, damit E-Mails nicht als Spam gekennzeichnet werden.

Das SendGrid-Konto erfordert möglicherweise das Hinzufügen eines Absenders.

Erstellen Sie eine Klasse, um den sicheren E-Mail-Schlüssel abzurufen. Erstellen Sie für dieses Beispiel Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzergeheimnissen

Legen Sie SendGridKey mit dem Tool secret-manager fest. Beispiel:

dotnet user-secrets set SendGridKey <SG.key>

Successfully saved SendGridKey = SG.keyVal to the secret store.

Unter Windows speichert der Geheimnis-Manager Schlüssel-Wert-Paare in einer Datei secrets.json im Verzeichnis %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

Der Inhalt der Datei secrets.json wird nicht verschlüsselt. Das folgende Markup zeigt die Datei secrets.json. Der SendGridKey-Wert wurde entfernt.

{
  "SendGridKey": "<key removed>"
}

Weitere Informationen finden Sie unter Optionsmuster und Konfiguration.

Installieren von SendGrid

In diesem Tutorial erfahren Sie, wie Sie E-Mail-Benachrichtigungen über SendGrid hinzufügen. Sie können E-Mails aber auch mithilfe von SMTP und anderen Mechanismen senden.

Installieren Sie das NuGet-Paket SendGrid:

Geben Sie in der Paket-Manager-Konsole den folgenden Befehl ein:

Install-Package SendGrid

Informationen zur Registrierung eines kostenlosen SendGrid-Kontos finden Sie unter Get Started with SendGrid for Free (Erste Schritte mit dem kostenlosen SendGrid).

Implementieren von IEmailSender

Um IEmailSender zu implementieren, erstellen Sie Services/EmailSender.cs mit Code ähnlich dem folgenden:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.SendGridKey))
        {
            throw new Exception("Null SendGridKey");
        }
        await Execute(Options.SendGridKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

Konfigurieren des Starts zur Unterstützung von E-Mails

Fügen Sie der ConfigureServices-Methode in der Datei Startup.cs den folgenden Code hinzu:

  • Fügen Sie EmailSender als vorübergehenden Dienst hinzu.
  • Registrieren Sie die AuthMessageSenderOptions-Konfigurationsinstanz.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Gerüst für RegisterConfirmation erstellen

Befolgen Sie die Anweisungen für Gerüst Identity und Gerüst Account\RegisterConfirmation.

Standardmäßige Kontobestätigung deaktivieren, wenn Account.RegisterConfirmation per Scaffolding generiert wird

Wenn Account.RegisterConfirmation ein Gerüst erstellt wird, führen Sie die Anweisungen in diesem Abschnitt aus.

Important

Wenn Account.RegisterConfirmationkein Gerüst erstellt wurde, überspringen Sie die folgenden Anweisungen, und fahren Sie mit dem nächsten Abschnitt fort.

Der Benutzer wird auf die /Identity/Account/RegisterConfirmationSeite umgeleitet, auf der er einen Link auswählen kann, mit dem das Konto bestätigt werden kann. Der Standardwert Account.RegisterConfirmation wird nur für Tests verwendet. Die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu verlangen und die unmittelbare Anmeldung bei der Registrierung zu verhindern, setzen Sie DisplayConfirmAccountLink = false in der generierten Datei /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs fest:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Dieser Schritt ist nur erforderlich, wenn Account.RegisterConfirmation per Scaffolding generiert wird.

Das nicht gerüstete RegisterConfirmation erkennt automatisch, wenn ein IEmailSender implementiert und beim Abhängigkeitseinfügungscontainer registriert wird.

Registrieren, Bestätigen per E-Mail und Zurücksetzen des Kennworts

Starten Sie die Web-App, und testen Sie den Ablauf zur Kontobestätigung und Kennwortwiederherstellung.

  • Ausführen der App und Registrieren eine*r neue*n Benutzer*in
  • Überprüfen Sie Ihre E-Mail auf einen Link zur Kontobestätigung. Wenn Sie die E-Mail nicht erhalten haben, finden Sie unter Debuggen von E-Mails weitere Informationen.
  • Wählen Sie den Link aus, um Ihre E-Mail-Adresse zu bestätigen.
  • Melden Sie sich mit Ihrer E-Mail-Adresse und Ihrem Kennwort an.
  • Melden Sie sich ab.

Passwortzurücksetzung testen

  • Wenn Sie angemeldet sind, wählen Sie Logout (Abmelden) aus.
  • Wählen Sie den Link Log in (Anmelden) und dann den Link Forgot your password? (Kennwort vergessen?) aus.
  • Geben Sie die E-Mail-Adresse ein, die Sie zum Registrieren des Kontos verwendet haben.
  • Es wird eine E-Mail mit einem Link zum Zurücksetzen Ihres Kennworts gesendet. Überprüfen Sie Ihre E-Mails, und wählen Sie den Link aus, um Ihr Kennwort zurückzusetzen. Nachdem Ihr Kennwort erfolgreich zurückgesetzt wurde, können Sie sich mit Ihrer E-Mail-Adresse und dem neuen Kennwort anmelden.

Erneutes Senden von E-Mail-Bestätigungen

Wählen Sie in .NET 5 oder höher auf der Anmeldeseite den Link "E-Mail-Bestätigung erneut senden" aus.

E-Mail- und Aktivitäts-Timeout ändern

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

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

Ändern der Lebensdauer aller Datenschutztoken

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

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

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

Ändern der Lebensdauer von E-Mail-Token

Die Standardlebensdauer von Identity-Benutzertoken beträgt einen Tag. In diesem Abschnitt wird gezeigt, wie Sie die Lebensdauer von E-Mail-Token ändern.

Fügen Sie benutzerdefinierte DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptions hinzu:

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

Fügen Sie dem Dienstcontainer den benutzerdefinierten Anbieter hinzu:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
      }).AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

Debuggen von E-Mails

Wenn E-Mails nicht funktionieren:

  • Legen Sie einen Breakpoint in EmailSender.Execute fest, um zu überprüfen, ob SendGridClient.SendEmailAsync aufgerufen wird.
  • Erstellen Sie eine Konsolen-App zum Senden von E-Mails mit ähnlichem Code wie EmailSender.Execute.
  • Überprüfen Sie die Seite mit E-Mail-Aktivitäten.
  • Überprüfen Sie Ihren Spamordner.
  • Versuchen Sie es mit einem anderen E-Mail-Alias bei einem anderen E-Mail-Anbieter (Microsoft, Yahoo, Gmail usw.).
  • Versuchen Sie, die E-Mail an andere E-Mail-Konten zu senden.

Eine bewährte Sicherheitsmethode besteht darin, keine Produktionsgeheimnisse bei Tests und der Entwicklung zu verwenden. Wenn Sie die App in Azure veröffentlichen, legen Sie die SendGrid-Geheimnisse im Azure-Web-App-Portal als Anwendungseinstellungen fest. Das Konfigurationssystem ist zum Lesen von Schlüsseln aus Umgebungsvariablen eingerichtet.

Kombinieren von Konten bei sozialen Medien und lokalen Anmeldekonten

Um diesen Abschnitt abzuschließen, müssen Sie zunächst einen externen Authentifizierungsanbieter aktivieren. Weitere Informationen finden Sie unter Authentifizierung über Facebook, Google und externe Anbieter.

Sie können lokale Konten und Konten bei sozialen Medien kombinieren, indem Sie Ihren E-Mail-Link auswählen. In der folgenden Sequenz wird „RickAndMSFT@gmail.com“ zuerst als lokaler Anmeldename erstellt. Sie können das Konto jedoch zuerst als Anmeldung für soziale Netzwerke erstellen und dann eine lokale Anmeldung hinzufügen.

Webanwendung: Benutzer RickAndMSFT@gmail.com authentifiziert

Wählen Sie den Link Verwalten aus. Beachten Sie, dass diesem Konto 0 (null) externe Konten (bei sozialen Medien) zugeordnet sind.

Verwalten der Ansicht

Wählen Sie den Link zu einem anderen Anmeldedienst aus, und akzeptieren Sie die App-Anforderungen. In der folgenden Abbildung ist der externe Authentifizierungsanbieter Facebook:

Ansicht zum Verwalten Ihrer externen Anmeldungen, in der Facebook aufgeführt wird

Die beiden Konten wurden kombiniert. Sie können sich mit beiden Konten anmelden. Möglicherweise möchten Sie, dass Ihre Benutzer*innen lokale Konten hinzufügen, falls ihr Authentifizierungsdienst beim sozialen Netzwerk ausfällt oder wahrscheinlicher, falls sie den Zugriff auf ihr Konto beim sozialen Netzwerk verloren haben.

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.