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 wird gezeigt, wie Sie eine ASP.NET Core-App mit E-Mail-Bestätigung und Kennwortzurücksetzung erstellen. Dieses Tutorial 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 Seite zu /Identity/Account/RegisterConfirmation 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 Anmelden aus, und melden Sie sich mit den gleichen Anmeldeinformationen an.
  • Wählen Sie den Hello YourEmail@provider.com! Link aus, der /Identity/Account/Manage/PersonalData zur Seite weitergeleitet wird.
  • Wählen Sie links die Registerkarte Persönliche Daten und dann Löschen aus.

Der Click here to confirm your account Link wird angezeigt, da kein IEmailSender implementiert und im Verzeichniseinschleusungscontainer registriert wurde. Weitere Informationen finden Sie in der RegisterConfirmation-Quelle.

Konfigurieren eines E-Mail-Anbieters

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

Für das SendGrid-Konto ist möglicherweise das Hinzufügen eines Absenders erforderlich.

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

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzergeheimnissen

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

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

Unter Windows speichert Secret Manager Schlüssel-Wert-Paare 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 unter Optionenmuster und Konfiguration.

Installieren von SendGrid

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

Installieren Sie das SendGrid NuGet-Paket:

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

Install-Package SendGrid

Informationen zum Registrieren eines kostenlosen SendGrid-Kontos finden Sie unter Erste Schritte mit SendGrid kostenlos .

Implementieren von IEmailSender

Um zu implementieren IEmailSender, 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 für die Unterstützung von E-Mails

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

  • Fügen Sie als vorübergehenden Dienst hinzu 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 nicht gerüstet Account.RegisterConfirmationhaben.

Der Benutzer wird zu dem Account.RegisterConfirmation weitergeleitet, wo er einen Link auswählen kann, um das Konto bestätigen zu lassen. 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 erfordern und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie in der Gerüstdatei /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs folgendes festDisplayConfirmAccountLink = false:

// 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üstete RegisterConfirmation erkennt automatisch, wenn ein IEmailSender implementiert und im Container für die Verzeichniseinschleusung registriert wurde.

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

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

  • Ausführen der App und Registrieren eines neuen Benutzers
  • Überprüfen Sie Ihre E-Mail auf den Link zur Kontobestätigung. Weitere Informationen finden Sie unter Debuggen von 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-Adresse 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-Adresse, 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-Adresse und dem neuen Kennwort anmelden.

E-Mail-Bestätigung erneut senden

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

Ändern des E-Mail- und Aktivitätstimeouts

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 Lebensdauern von 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 des E-Mail-Tokens

Die Standardtokenlebensdauer der Identity Benutzertoken beträgt einen Tag. In diesem Abschnitt wird gezeigt, wie Sie die Lebensdauer des E-Mail-Tokens ändern.

Fügen Sie einen benutzerdefinierten DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptionshinzu:

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 können:

  • Legen Sie einen Haltepunkt fest EmailSender.Execute , 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 Email Aktivität.
  • Überprüfen Sie Ihren Spamordner.
  • Versuchen Sie einen anderen E-Mail-Alias bei einem anderen E-Mail-Anbieter (Microsoft, Yahoo, Gmail usw.)
  • Versuchen Sie, an verschiedene E-Mail-Konten zu senden.

Eine bewährte Sicherheitsmethode besteht darin, keine Produktionsgeheimnisse bei Test und 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 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 Social Media-Anmeldung erstellen und dann eine lokale Anmeldung hinzufügen.

Webanwendung: RickAndMSFT@gmail.com Benutzerauthentifizierung

Klicken Sie auf den Link Verwalten . Notieren Sie sich die 0 externen (Social-Media-Anmeldungen), die diesem Konto zugeordnet sind.

Ansicht verwalten

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

Verwalten Ihrer externen Anmeldungen Ansicht Eintrag 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 ihr Authentifizierungsdienst für die soziale Anmeldung ausgefallen ist oder sie wahrscheinlich den Zugriff auf ihr Social Media-Konto verloren haben.

Aktivieren der Kontobestätigung, nachdem auf einer Website Benutzer vorhanden sind

Wenn Sie die Kontobestätigung auf einer Website mit Benutzern aktivieren, werden alle vorhandenen Benutzer gesperrt. Vorhandene Benutzer werden gesperrt, da ihre Konten nicht bestätigt werden. Verwenden Sie einen der folgenden Ansätze, um vorhandene Benutzersperren zu umgehen:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer als bestätigt zu markieren.
  • Bestätigen Sie vorhandene Benutzer. Beispielsweise senden Sie E-Mails im Batch 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 Seite zu /Identity/Account/RegisterConfirmation 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 Anmelden 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 weiterleitet.
  • Wählen Sie links die Registerkarte Persönliche Daten und dann Löschen aus.

Konfigurieren eines E-Mail-Anbieters

In diesem Tutorial wird SendGrid zum Senden von E-Mails 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 schwer zu konfigurieren, sodass E-Mails nicht als Spam gekennzeichnet werden.

Für das SendGrid-Konto ist möglicherweise das Hinzufügen eines Absenders erforderlich.

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

namespace WebPWrecover.Services;

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

Konfigurieren von SendGrid-Benutzergeheimnissen

Legen Sie den 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 Secret Manager Schlüssel-Wert-Paare 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 unter Optionenmuster und Konfiguration.

Installieren von SendGrid

In diesem Tutorial 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

Informationen zum Registrieren eines kostenlosen SendGrid-Kontos finden Sie unter Erste Schritte mit SendGrid kostenlos .

Implementieren von IEmailSender

Um zu implementieren IEmailSender, 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 Startvorgangs zur Unterstützung von E-Mails

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

  • Fügen Sie als vorübergehenden Dienst hinzu 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();

Scaffold RegisterConfirmation

Befolgen Sie die Anweisungen für Gerüst Identity und Gerüst Account\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 nicht gerüstet Account.RegisterConfirmationhaben.

Der Benutzer wird zu dem Account.RegisterConfirmation weitergeleitet, wo er einen Link auswählen kann, um das Konto bestätigen zu lassen. 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 erfordern und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie in der Gerüstdatei /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs folgendes festDisplayConfirmAccountLink = false:

// 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üstete RegisterConfirmation erkennt automatisch, wenn ein IEmailSender implementiert und im Container für die Verzeichniseinschleusung registriert wurde.

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

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

  • Ausführen der App und Registrieren eines neuen Benutzers
  • Überprüfen Sie Ihre E-Mail auf den Link zur Kontobestätigung. Weitere Informationen finden Sie unter Debuggen von 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-Adresse 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-Adresse, 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-Adresse und dem neuen Kennwort anmelden.

E-Mail-Bestätigung erneut senden

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

Ändern des E-Mail- und Aktivitätstimeouts

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 Lebensdauern von 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 des E-Mail-Tokens

Die Standardtokenlebensdauer der Identity Benutzertoken beträgt einen Tag. In diesem Abschnitt wird gezeigt, wie Sie die Lebensdauer des E-Mail-Tokens ändern.

Fügen Sie eine benutzerdefinierte DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptionshinzu:

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 können:

  • Legen Sie einen Haltepunkt 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 Email Aktivität.
  • Ü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, an andere E-Mail-Konten zu senden.

Eine bewährte Sicherheitsmethode besteht darin, keine Produktionsgeheimnisse in Tests und in 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 sozialen und lokalen Anmeldekonten

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

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 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: RickAndMSFT@gmail.com Benutzerauthentifizierung

Klicken Sie auf den Link Verwalten . Notieren Sie sich die 0 externen (social logins), die diesem Konto zugeordnet sind.

Ansicht verwalten

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

Verwalten Ihrer externen Anmeldungen Ansicht Eintrag 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 ihr Authentifizierungsdienst für soziale Netzwerke ausgefallen ist oder wahrscheinlicher, dass sie den Zugriff auf ihr Social Media-Konto verloren haben.

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

Durch aktivieren der Kontobestätigung auf einer Website mit Benutzern werden alle vorhandenen Benutzer gesperrt. Vorhandene Benutzer werden gesperrt, weil ihre Konten nicht bestätigt werden. Verwenden Sie einen der folgenden Ansätze, um vorhandene Benutzersperrungen zu umgehen:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer als bestätigt zu markieren.
  • Bestätigen vorhandener Benutzer. Beispielsweise Batch-E-Mails mit Bestätigungslinks senden.