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 Anfängerthema. Sie sollten mit den folgenden Punkten vertraut sein:

Blazor-Anleitungen, die Anleitungen in diesem Artikel ergänzen oder ersetzen, finden Sie unter Kontobestätigung und Kennwortwiederherstellung in ASP.NET Core-Blazor.

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 von Benutzer*innen 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 der Registrierung werden Sie zur /Identity/Account/RegisterConfirmation-Seite weitergeleitet, die einen Link zum Simulieren der E-Mail-Bestätigung enthält:

  • Klicken Sie auf den Link Click here to confirm your account (Zentralisierte Protokollierung).
  • 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.

Der Link Click here to confirm your account wird angezeigt, da kein IEmailSender implementiert und mit dem Container zur Abhängigkeitsinjektion registriert wurde. Sehen Sie sich die RegisterConfirmation-Quelle an.

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 Tutorial wird zum Senden von E-Mails SendGrid verwendet. Zum Senden von E-Mails sind ein SendGrid-Konto und -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 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 <key>

Successfully saved SendGridKey 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 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

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 der App zur Unterstützung von E-Mails

Fügen Sie der Datei Program.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();

Deaktivieren der Standardkontoüberprüfung, wenn ein Gerüst für „Account.RegisterConfirmation“ erstellt wurde

Dieser Abschnitt gilt nur, wenn ein Gerüst für Account.RegisterConfirmation erstellt wurde. Überspringen Sie diesen Abschnitt, wenn Sie kein Gerüst für Account.RegisterConfirmation erstellt haben.

Die Benutzer*innen werden zu Account.RegisterConfirmation umgeleitet, wo sie einen Link auswählen können, um das Konto bestätigen zu lassen. Die Standardeinstellung von 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 erzwingen und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie DisplayConfirmAccountLink = false in der Gerüstdatei /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 ein Gerüst für Account.RegisterConfirmation erstellt wurde. Bei einer RegisterConfirmation ohne Gerüst wird automatisch erkannt, wenn ein IEmailSender implementiert und mit dem Container zur Abhängigkeitsinjektion registriert wurde.

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

Führen Sie die Web-App aus, und testen Sie den Flow 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.

Testen der Kennwortzurücksetzung

  • 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 auf der Anmeldeseite den Link Resend email confirmation (E-Mail-Bestätigung erneut senden) aus.

Ändern des E-Mail- und Aktivitätstimeouts

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:

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 3 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) 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:

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 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:

Verwalten der Anzeige externer Anmeldungen mit Facebook

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.

Voraussetzungen

.NET Core 3.0 SDK oder höher

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:

  • Klicken Sie auf den Link Click here to confirm your account (Zentralisierte Protokollierung).
  • 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();

Erstellen eines Gerüsts für RegisterConfirmation

Befolgen Sie die Anweisungen zum Erstellen eines Gerüsts für Identity und eines Gerüsts für Account\RegisterConfirmation.

Deaktivieren der Standardkontoüberprüfung, wenn ein Gerüst für „Account.RegisterConfirmation“ erstellt wurde

Dieser Abschnitt gilt nur, wenn ein Gerüst für Account.RegisterConfirmation erstellt wurde. Überspringen Sie diesen Abschnitt, wenn Sie kein Gerüst für Account.RegisterConfirmation erstellt haben.

Die Benutzer*innen werden zu Account.RegisterConfirmation umgeleitet, wo sie einen Link auswählen können, um das Konto bestätigen zu lassen. Die Standardeinstellung von 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 erzwingen und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie DisplayConfirmAccountLink = false in der Gerüstdatei /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 ein Gerüst für Account.RegisterConfirmation erstellt wurde. Bei einer RegisterConfirmation ohne Gerüst wird automatisch erkannt, wenn ein IEmailSender implementiert und mit dem Container zur Abhängigkeitsinjektion registriert wurde.

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

Führen Sie die Web-App aus, und testen Sie den Flow 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.

Testen der Kennwortzurücksetzung

  • 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 ASP.NET Core 5.0 und höher auf der Anmeldeseite den Link Resend email confirmation (E-Mail-Bestätigung erneut senden) aus.

Ändern des E-Mail- und Aktivitätstimeouts

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:

Verwalten der Anzeige externer Anmeldungen mit Facebook

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.