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 Lernprogramm wird gezeigt, wie Sie eine ASP.NET Core-App mit E-Mail-Bestätigung und Kennwortzurücksetzung erstellen. Dieses Lernprogramm ist kein Anfangsthema. Sie sollten mit den folgenden Punkten vertraut sein:

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 einen Benutzer. Nach der Registrierung werden Sie zur /Identity/Account/RegisterConfirmation Seite umgeleitet, 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 Anmeldelink aus, und melden Sie sich mit den gleichen Anmeldeinformationen an.
  • Wählen Sie den Hello YourEmail@provider.com! Link aus, der zur /Identity/Account/Manage/PersonalData Seite umgeleitet wird.
  • Wählen Sie auf der linken Seite die Registerkarte "Persönliche Daten " aus, und wählen Sie dann "Löschen" aus.

Der Click here to confirm your account Link wird angezeigt, da ein IEmailSender nicht implementiert und mit dem Verzeichniseinfügungscontainer registriert wurde. Siehe die RegisterConfirmation-Quelle.

Konfigurieren eines E-Mail-Anbieters

In diesem Lernprogramm wird SendGrid zum Senden von E-Mails verwendet. Zum Senden von E-Mails ist ein SendGrid-Konto und ein Schlüssel erforderlich. Andere E-Mail-Anbieter. Es wird empfohlen, SendGrid oder einen anderen E-Mail-Dienst zum Senden von E-Mails anstelle von SMTP zu verwenden. SMTP ist schwer zu sichern und richtig einzurichten.

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

Erstellen Sie eine Klasse zum Abrufen des sicheren E-Mail-Schlüssels. Erstellen Sie Services/AuthMessageSenderOptions.csfür dieses Beispiel :

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzerschlüsseln

Legen Sie das SendGridKey Tool mit dem geheimen Manager fest. Beispiel:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

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

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

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

Weitere Informationen finden Sie im Optionsmuster und in der Konfiguration.

SendGrid installieren

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

Installieren Sie das SendGrid NuGet Paket:

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

Install-Package SendGrid

Weitere Informationen finden Sie unter Erste Schritte mit SendGrid kostenlos, um sich für ein kostenloses SendGrid-Konto zu registrieren.

Implementieren von IEmailSender

Erstellen Services/EmailSender.cs Sie zum Implementieren IEmailSendermit Code ähnlich wie folgt:

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:

  • Als vorübergehender Dienst hinzufügen EmailSender .
  • 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 Account.RegisterConfirmation gerüstet wurde

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

Der Benutzer wird an die Stelle weitergeleitet, an der Account.RegisterConfirmation er einen Link auswählen kann, um das Konto bestätigt zu haben. Der Standardwert Account.RegisterConfirmation wird nur zum Testen verwendet, die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu erfordern und die sofortige Anmeldung bei der Registrierung zu verhindern, legen DisplayConfirmAccountLink = false Sie 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 Account.RegisterConfirmation ein Gerüst erstellt wird. Die nicht gerüstierte RegisterConfirmation erkennt automatisch, wann ein IEmailSender implementiert und mit dem Verzeichniseinfügungscontainercontainer-Container registriert wurde.

Registrieren, Bestätigen von E-Mails und Zurücksetzen des Kennworts

Führen Sie die Web-App aus, und testen Sie den Kontobestätigungs- und Kennwortwiederherstellungsfluss.

  • Führen Sie die App aus, und registrieren Sie einen neuen Benutzer
  • Überprüfen Sie Ihre E-Mail auf den Bestätigungslink des Kontos. Lesen Sie Debug-E-Mails , wenn Sie die E-Mail nicht erhalten.
  • Klicken Sie auf den Link, um Ihre E-Mail zu bestätigen.
  • Melden Sie sich mit Ihrer E-Mail und Ihrem Kennwort an.
  • Melden Sie sich ab.

Testen der Kennwortzurücksetzung

  • Wenn Sie angemeldet sind, wählen Sie "Abmelden" aus.
  • Wählen Sie den Link "Anmelden " aus, und wählen Sie den Link " Kennwort vergessen" aus.
  • Geben Sie die E-Mail ein, die Sie zum Registrieren des Kontos verwendet haben.
  • Eine E-Mail mit einem Link zum Zurücksetzen Ihres Kennworts wird gesendet. Überprüfen Sie Ihre E-Mail, und klicken Sie auf den Link, um Ihr Kennwort zurückzusetzen. Nachdem Ihr Kennwort erfolgreich zurückgesetzt wurde, können Sie sich mit Ihrer E-Mail und ihrem neuen Kennwort anmelden.

E-Mail-Bestätigung erneut senden

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

Ändern von E-Mail- und Aktivitätstimeout

Das Standardmäßige Inaktivitätstimeout 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 aller Lebensdauer des Datenschutztokens

Der folgende Code ändert den Timeoutzeitraum aller 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 )verfügen über ein Tag-Timeout.

Ändern der Lebensdauer des E-Mail-Tokens

Die Standardtokenlaufzeit der Identity Benutzertoken ist ein Tag. In diesem Abschnitt wird gezeigt, wie Sie die Lebensdauer des E-Mail-Tokens ändern.

Hinzufügen einer benutzerdefinierten DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptions:

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 Sie keine E-Mail-Arbeit erhalten können:

  • Legen Sie einen Haltepunkt fest EmailSender.Execute , um zu überprüfen, dass er SendGridClient.SendEmailAsync aufgerufen wird.
  • Erstellen Sie eine Konsolen-App, um E-Mails mit ähnlichem Code zu senden.EmailSender.Execute
  • Überprüfen Sie die Seite "E-Mail-Aktivität ".
  • Überprüfen Sie Ihren Spamordner.
  • Versuchen Sie einen anderen E-Mail-Alias auf einem anderen E-Mail-Anbieter (Microsoft, Yahoo, Gmail usw.)
  • Versuchen Sie, an verschiedene E-Mail-Konten zu senden.

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

Kombinieren von Sozialen und lokalen Anmeldekonten

Um diesen Abschnitt abzuschließen, müssen Sie zuerst einen externen Authentifizierungsanbieter aktivieren. Siehe Facebook-, Google- und externe Anbieterauthentifizierung.

Sie können lokale und soziale Konten kombinieren, indem Sie auf Ihren E-Mail-Link klicken. In der folgenden Sequenz wird "RickAndMSFT@gmail.com" zuerst als lokale Anmeldung erstellt. Sie können das Konto jedoch zuerst als soziale Anmeldung erstellen und dann eine lokale Anmeldung hinzufügen.

Web application: RickAndMSFT@gmail.com user authenticated

Klicken Sie auf den Link "Verwalten" . Beachten Sie die 0 externen (sozialen Anmeldungen), die diesem Konto zugeordnet sind.

Manage view

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

Manage your external logins view listing Facebook

Die beiden Konten wurden kombiniert. Sie können sich mit beiden Konten anmelden. Möglicherweise möchten Sie, dass Ihre Benutzer lokale Konten hinzufügen, falls der Authentifizierungsdienst für soziale Netzwerke deaktiviert ist, oder wahrscheinlicher, dass sie den Zugriff auf ihr soziales Konto verloren haben.

Aktivieren der Kontobestätigung, nachdem eine Website Benutzer hat

Aktivieren der Kontobestätigung auf einer Website mit Benutzern sperrt alle vorhandenen Benutzer. Vorhandene Benutzer sind gesperrt, da ihre Konten nicht bestätigt werden. Verwenden Sie eine der folgenden Ansätze, um vorhandene Benutzersperrungen zu umgehen:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer als bestätigt zu markieren.
  • Bestätigen Sie vorhandene Benutzer. Beispiel: Batch-Senden von 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 einen Benutzer. Nach der Registrierung werden Sie zur /Identity/Account/RegisterConfirmation Seite umgeleitet, 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 Anmeldelink aus, und melden Sie sich mit den gleichen Anmeldeinformationen an.
  • Wählen Sie den Hello YourEmail@provider.com! Link aus, der Sie zur /Identity/Account/Manage/PersonalData Seite umleitet.
  • Wählen Sie auf der linken Seite die Registerkarte "Persönliche Daten " aus, und wählen Sie dann "Löschen" aus.

Konfigurieren eines E-Mail-Anbieters

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

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

Erstellen Sie eine Klasse zum Abrufen des sicheren E-Mail-Schlüssels. Erstellen Sie Services/AuthMessageSenderOptions.csfür dieses Beispiel :

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzerschlüsseln

Legen Sie das SendGridKey Tool mit dem geheimen Manager fest. Beispiel:

dotnet user-secrets set SendGridKey <SG.key>

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

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

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

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

Weitere Informationen finden Sie im Optionsmuster und in der Konfiguration.

SendGrid installieren

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

Installieren Sie das SendGrid NuGet Paket:

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

Install-Package SendGrid

Weitere Informationen finden Sie unter Erste Schritte mit SendGrid kostenlos, um sich für ein kostenloses SendGrid-Konto zu registrieren.

Implementieren von IEmailSender

Erstellen Services/EmailSender.cs Sie zum Implementieren IEmailSendermit Code ähnlich wie folgt:

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

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

  • Als vorübergehender Dienst hinzufügen EmailSender .
  • 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üstregisterConfirmation

Folgen Sie den Anweisungen für Gerüst und Gerüst IdentityAccount\RegisterConfirmation.

Deaktivieren der Standardkontoüberprüfung, wenn Account.RegisterConfirmation gerüstet wurde

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

Der Benutzer wird an die Stelle weitergeleitet, an der Account.RegisterConfirmation er einen Link auswählen kann, um das Konto bestätigt zu haben. Der Standardwert Account.RegisterConfirmation wird nur zum Testen verwendet, die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu erfordern und die sofortige Anmeldung bei der Registrierung zu verhindern, legen DisplayConfirmAccountLink = false Sie 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 Account.RegisterConfirmation ein Gerüst erstellt wird. Die nicht gerüstierte RegisterConfirmation erkennt automatisch, wann ein IEmailSender implementiert und mit dem Verzeichniseinfügungscontainercontainer-Container registriert wurde.

Registrieren, Bestätigen von E-Mails und Zurücksetzen des Kennworts

Führen Sie die Web-App aus, und testen Sie den Kontobestätigungs- und Kennwortwiederherstellungsfluss.

  • Führen Sie die App aus, und registrieren Sie einen neuen Benutzer
  • Überprüfen Sie Ihre E-Mail auf den Bestätigungslink des Kontos. Lesen Sie Debug-E-Mails , wenn Sie die E-Mail nicht erhalten.
  • Klicken Sie auf den Link, um Ihre E-Mail zu bestätigen.
  • Melden Sie sich mit Ihrer E-Mail und Ihrem Kennwort an.
  • Melden Sie sich ab.

Testen der Kennwortzurücksetzung

  • Wenn Sie angemeldet sind, wählen Sie "Abmelden" aus.
  • Wählen Sie den Link "Anmelden " aus, und wählen Sie den Link " Kennwort vergessen" aus.
  • Geben Sie die E-Mail ein, die Sie zum Registrieren des Kontos verwendet haben.
  • Eine E-Mail mit einem Link zum Zurücksetzen Ihres Kennworts wird gesendet. Überprüfen Sie Ihre E-Mail, und klicken Sie auf den Link, um Ihr Kennwort zurückzusetzen. Nachdem Ihr Kennwort erfolgreich zurückgesetzt wurde, können Sie sich mit Ihrer E-Mail und ihrem neuen Kennwort anmelden.

E-Mail-Bestätigung erneut senden

Wählen Sie in ASP.NET Core 5.0 und höher den Link zur E-Mail-Bestätigung erneut auf der Anmeldeseite aus.

Ändern von E-Mail- und Aktivitätstimeout

Das Standardmäßige Inaktivitätstimeout 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 aller Lebensdauer des Datenschutztokens

Der folgende Code ändert den Timeoutzeitraum aller 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 )verfügen über ein Tag-Timeout.

Ändern der Lebensdauer des E-Mail-Tokens

Die Standardtokenlaufzeit der Identity Benutzertoken ist ein Tag. In diesem Abschnitt wird gezeigt, wie Sie die Lebensdauer des E-Mail-Tokens ändern.

Hinzufügen einer benutzerdefinierten und DataProtectorTokenProvider<TUser>DataProtectionTokenProviderOptions:

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 Sie keine E-Mail-Arbeit erhalten können:

  • Legen Sie einen Haltepunkt EmailSender.Execute fest, um zu überprüfen SendGridClient.SendEmailAsync , dass er aufgerufen wird.
  • Erstellen Sie eine Konsolen-App, um E-Mails mit ähnlichem Code EmailSender.Executezu senden.
  • Überprüfen Sie die Seite "E-Mail-Aktivität" .
  • Überprüfen Sie Ihren Spamordner.
  • Versuchen Sie einen anderen E-Mail-Alias auf einem anderen E-Mail-Anbieter (Microsoft, Yahoo, Gmail usw.)
  • Versuchen Sie, an verschiedene E-Mail-Konten zu senden.

Eine bewährte Sicherheitspraxis besteht darin, keine Produktionsschlüssel in Test- und Entwicklungsgründen zu verwenden. Wenn Sie die App in Azure veröffentlichen, legen Sie die SendGrid-Schlüssel als Anwendungseinstellungen im Azure Web App-Portal fest. Das Konfigurationssystem ist zum Lesen von Schlüsseln aus Umgebungsvariablen eingerichtet.

Kombinieren von sozialen und lokalen Anmeldekonten

Um diesen Abschnitt abzuschließen, müssen Sie zuerst einen externen Authentifizierungsanbieter aktivieren. Siehe Facebook, Google und externe Anbieterauthentifizierung.

Sie können lokale und soziale Konten kombinieren, indem Sie auf Ihren E-Mail-Link klicken. In der folgenden Sequenz wird "RickAndMSFT@gmail.com" zuerst als lokale Anmeldung erstellt. Sie können das Konto jedoch zuerst als soziale Anmeldung erstellen und dann eine lokale Anmeldung hinzufügen.

Web application: RickAndMSFT@gmail.com user authenticated

Klicken Sie auf den Link "Verwalten" . Beachten Sie die externen (sozialen Anmeldungen), die diesem Konto zugeordnet sind.

Manage view

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

Manage your external logins view listing Facebook

Die beiden Konten wurden kombiniert. Sie können sich mit beiden Konten anmelden. Möglicherweise möchten Sie möchten, dass Ihre Benutzer lokale Konten hinzufügen, wenn ihr Dienst für die soziale Anmeldung heruntergeht, oder wahrscheinlicher, dass sie den Zugriff auf ihr soziales Konto verloren haben.

Aktivieren der Kontobestätigung, nachdem eine Website Über Benutzer verfügt

Die Aktivierung der Kontobestätigung auf einer Website mit Benutzern sperrt alle vorhandenen Benutzer. Vorhandene Benutzer sind gesperrt, da ihre Konten nicht bestätigt sind. Um vorhandene Benutzersperrungen zu umgehen, verwenden Sie eine der folgenden Ansätze:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer als bestätigt zu markieren.
  • Bestätigen Sie vorhandene Benutzer. Beispiel: Batch-Senden von E-Mails mit Bestätigungslinks.