Delen via


Accountbevestiging en wachtwoordherstel in ASP.NET Core Blazor

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

In dit artikel wordt uitgelegd hoe u een ASP.NET Core-Blazor Web App configureert met e-mailbevestiging en wachtwoordherstel.

Notitie

Dit artikel is alleen van toepassing op Blazor Web Apps. Als u e-mailbevestiging en wachtwoordherstel wilt implementeren voor zelfstandige Blazor WebAssembly-apps met ASP.NET Core Identity, raadpleegt u accountbevestiging en wachtwoordherstel in ASP.NET Core-Blazor WebAssembly met ASP.NET Core Identity.

Namespace

De naamruimte van de app die in het voorbeeld in dit artikel wordt gebruikt, is BlazorSample. Werk de codevoorbeelden bij om de naamruimte van uw app te gebruiken.

Een e-mailprovider selecteren en configureren

In dit artikel wordt Transactionele API van Mailchimp via Mandrill.net gebruikt om e-mail te verzenden. U wordt aangeraden een e-mailservice te gebruiken om e-mail te verzenden in plaats van SMTP. SMTP is moeilijk te configureren en goed te beveiligen. Welke e-mailservice u ook gebruikt, krijg toegang tot hun richtlijnen voor .NET-apps, maak een account, configureer een API-sleutel voor hun service en installeer eventuele Vereiste NuGet-pakketten.

Maak een klasse voor het opslaan van de API-sleutel van de geheime e-mailprovider. In het voorbeeld in dit artikel wordt een klasse met de naam AuthMessageSenderOptions met een eigenschap EmailAuthKey gebruikt om de sleutel vast te houden.

AuthMessageSenderOptions.cs:

namespace BlazorSample;

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

Registreer het AuthMessageSenderOptions configuratie-exemplaar in het Program-bestand:

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

Een geheim configureren voor de beveiligingssleutel van de e-mailprovider

Ontvang de beveiligingssleutel van de e-mailprovider van de provider en gebruik deze in de volgende richtlijnen.

Gebruik een of beide van de volgende methoden om het geheim aan de app te leveren:

  • Secret Manager-hulpprogramma: het secret manager-hulpprogramma slaat persoonlijke gegevens op de lokale computer op en wordt alleen gebruikt tijdens de lokale ontwikkeling.
  • Azure Key Vault-: u kunt het geheim opslaan in een sleutelkluis voor gebruik in elke omgeving, inclusief voor de ontwikkelomgeving wanneer u lokaal werkt. Sommige ontwikkelaars gebruiken liever sleutelkluizen voor faserings- en productie-implementaties en gebruiken het hulpprogramma Secret Manager voor lokale ontwikkeling.

We raden u ten zeerste aan om te vermijden geheimen op te slaan in projectcode of configuratiebestanden. Gebruik beveiligde verificatiestromen, zoals een of beide benaderingen in deze sectie.

Hulpprogramma Secret Manager

Als het project al is geïnitialiseerd voor het hulpprogramma Secret Manager, heeft het al een app-geheimen-id (<AppSecretsId>) in het projectbestand (.csproj). In Visual Studio kunt u zien of de ID van de app-geheimen aanwezig is door in het deelvenster Eigenschappen te kijken wanneer het project is geselecteerd in de Oplossingsverkenner. Als de app niet is geïnitialiseerd, voert u de volgende opdracht uit in een opdrachtshell die is geopend in de map van het project. In Visual Studio kunt u de Developer PowerShell-opdrachtprompt gebruiken.

dotnet user-secrets init

Stel de API-sleutel in met het hulpprogramma Secret Manager. In het volgende voorbeeld is de sleutelnaam EmailAuthKey om overeen te komen met AuthMessageSenderOptions.EmailAuthKeyen wordt de sleutel vertegenwoordigd door de placeholder {KEY}. Voer de volgende opdracht uit met de API-sleutel:

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

Als u Visual Studio gebruikt, kunt u bevestigen dat het geheim is ingesteld door met de rechtermuisknop op het serverproject te klikken in Solution Explorer- en Gebruikersgeheimen beheren te selecteren.

Zie Veilige opslag van app-geheimen in ontwikkeling in ASP.NET Corevoor meer informatie.

Waarschuwing

Sla geen app-geheimen, verbindingsreeksen, referenties, wachtwoorden, persoonlijke identificatienummers (PINCODE's), persoonlijke C#/.NET-code of persoonlijke sleutels/tokens op in code aan de clientzijde. Dit is altijd onveilig. In test-, staging- en productieomgevingen moeten server-side Blazor-code en web-API's gebruikmaken van beveiligde verificatiestromen die voorkomen dat referenties in projectcode of configuratiebestanden worden opgeslagen. Buiten het testen van lokale ontwikkeling raden we u aan het gebruik van omgevingsvariabelen voor het opslaan van gevoelige gegevens te vermijden, omdat omgevingsvariabelen niet de veiligste benadering zijn. Voor het testen van lokale ontwikkeling wordt het hulpprogramma Secret Manager aanbevolen voor het beveiligen van gevoelige gegevens. Zie Gevoelige gegevens en referenties veilig onderhoudenvoor meer informatie.

Azure Key Vault

Azure Key Vault- biedt een veilige benadering voor het leveren van het clientgeheim van de app aan de app.

Als u een sleutelkluis wilt maken en een geheim wilt instellen, raadpleegt u Over Azure Key Vault-geheimen (Azure-documentatie), die resources kruislings koppelt om aan de slag te gaan met Azure Key Vault. Als u de code in deze sectie wilt implementeren, registreert u de sleutelkluis-URI en de geheime naam van Azure wanneer u de sleutelkluis en het geheim maakt. Wanneer u het toegangsbeleid voor het geheim instelt in het Toegangsbeleid deelvenster:

  • Alleen het verkrijgen van de geheime toestemming is vereist.
  • Selecteer de toepassing als de Principal- voor het geheim.

Controleer in de Azure- of Entra-portal of de app toegang heeft gekregen tot het geheim dat u hebt gemaakt voor de sleutel van de e-mailprovider.

Belangrijk

Er wordt een sleutelkluisgeheim gemaakt met een vervaldatum. Zorg ervoor dat u bijhoudt wanneer een sleutelkluisgeheim verloopt en maak een nieuw geheim voor de app voordat die datum is verstreken.

Voeg de volgende AzureHelper klasse toe aan het serverproject. De GetKeyVaultSecret-methode haalt een geheim op uit een sleutelkluis. Pas de naamruimte (BlazorSample.Helpers) aan zodat deze overeenkomt met het projectnaamruimteschema.

Helpers/AzureHelper.cs:

using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace BlazorSample.Helpers;

public static class AzureHelper
{
    public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
    {
        DefaultAzureCredentialOptions options = new()
        {
            // Specify the tenant ID to use the dev credentials when running the app locally
            // in Visual Studio.
            VisualStudioTenantId = tenantId,
            SharedTokenCacheTenantId = tenantId
        };

        var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
        var secret = client.GetSecretAsync(secretName).Result;

        return secret.Value.Value;
    }
}

Wanneer services zijn geregistreerd in het Program-bestand van het serverproject, verkrijg en verbind het geheim met de Opties-configuratie.

var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;

var emailAuthKey = AzureHelper.GetKeyVaultSecret(
    tenantId, vaultUri, "EmailAuthKey");

var authMessageSenderOptions = 
    new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
    .Bind(authMessageSenderOptions);

Als u de omgeving wilt beheren waarin de voorgaande code werkt, bijvoorbeeld om te voorkomen dat de code lokaal wordt uitgevoerd omdat u ervoor hebt gekozen om het hulpprogramma Secret Manager te gebruiken voor lokale ontwikkeling, kunt u de voorgaande code verpakken in een voorwaardelijke instructie waarmee de omgeving wordt gecontroleerd:

if (!context.HostingEnvironment.IsDevelopment())
{
    ...
}

Bevestig in de sectie AzureAd van appsettings.json in het serverproject de aanwezigheid van de Entra-id van de app TenantId en voeg de volgende VaultUri configuratiesleutel en -waarde toe, als deze nog niet aanwezig is:

"VaultUri": "{VAULT URI}"

In het voorgaande voorbeeld is de tijdelijke aanduiding {VAULT URI} de Key Vault-URI. Voeg de afsluitende slash toe aan de URI.

Voorbeeld:

"VaultUri": "https://contoso.vault.azure.net/"

Configuratie wordt gebruikt om het leveren van toegewezen sleutelkluizen en geheime namen te vergemakkelijken op basis van de omgevingsconfiguratiebestanden van de app. U kunt bijvoorbeeld verschillende configuratiewaarden opgeven voor appsettings.Development.json in ontwikkeling, appsettings.Staging.json bij fasering en appsettings.Production.json voor de productie-implementatie. Zie ASP.NET Core Blazor-configuratievoor meer informatie.

IEmailSender implementeren

Het volgende voorbeeld is gebaseerd op de Transactionele API van Mailchimp met behulp van Mandrill.net. Raadpleeg voor een andere provider de documentatie over het implementeren van het verzenden van een e-mailbericht.

Voeg het Mandrill.net NuGet-pakket toe aan het project.

Voeg de volgende EmailSender klasse toe om IEmailSender<TUser>te implementeren. In het volgende voorbeeld is ApplicationUser een IdentityUser. De HTML-opmaak van het bericht kan verder worden aangepast. Zolang de message doorgegeven aan MandrillMessage begint met het < teken, gaat de Mandrill.net API ervan uit dat de hoofdtekst van het bericht is samengesteld in HTML.

Components/Account/EmailSender.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Mandrill;
using Mandrill.Model;
using BlazorSample.Data;

namespace BlazorSample.Components.Account;

public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
    ILogger<EmailSender> logger) : IEmailSender<ApplicationUser>
{
    private readonly ILogger logger = logger;

    public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;

    public Task SendConfirmationLinkAsync(AppUser user, string email,
        string confirmationLink) => SendEmailAsync(email, "Confirm your email",
        "<html lang=\"en\"><head></head><body>Please confirm your account by " +
        $"<a href='{confirmationLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetLinkAsync(AppUser user, string email,
        string resetLink) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password by " +
        $"<a href='{resetLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetCodeAsync(AppUser user, string email,
        string resetCode) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password " +
        $"using the following code:<br>{resetCode}</body></html>");

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

        await Execute(Options.EmailAuthKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, 
        string toEmail)
    {
        var api = new MandrillApi(apiKey);
        var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail, 
            subject, message);
        await api.Messages.SendAsync(mandrillMessage);

        logger.LogInformation("Email to {EmailAddress} sent!", toEmail);
    }
}

Notitie

Hoofdtekstinhoud voor berichten vereist mogelijk speciale codering voor de e-mailserviceprovider. Als koppelingen in de berichttekst niet kunnen worden gevolgd in het e-mailbericht, raadpleegt u de documentatie van de serviceprovider om het probleem op te lossen.

App configureren ter ondersteuning van e-mail

Wijzig de e-mailafzenderimplementatie in het Program-bestand naar de EmailSender:

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

Verwijder de IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) uit de app.

Verwijder in het RegisterConfirmation onderdeel (Components/Account/Pages/RegisterConfirmation.razor) het voorwaardelijke blok in het @code blok dat controleert of de EmailSender een IdentityNoOpEmailSenderis:

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

Verwijder ook in het RegisterConfirmation onderdeel de Razor markeringen en code voor het controleren van het emailConfirmationLink veld, waarbij alleen de regel de gebruiker opdracht geeft om hun e-mail te controleren...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

Accountbevestiging inschakelen nadat een site gebruikers heeft

Als u de bevestiging van een account inschakelt op een site met gebruikers, worden alle bestaande gebruikers vergrendeld. Bestaande gebruikers zijn vergrendeld omdat hun accounts niet worden bevestigd. Gebruik een van de volgende methoden om bestaande gebruikersvergrendeling te omzeilen:

  • Werk de database bij om alle bestaande gebruikers te markeren zoals bevestigd.
  • Bevestig bestaande gebruikers. Stuur bijvoorbeeld e-mails in batches met links voor bevestiging.

E-mail- en activiteitstijdslimiet

De standaardtime-out voor inactiviteit is 14 dagen. Met de volgende code wordt de time-out voor inactiviteit ingesteld op vijf dagen met een schuifverlooptijd:

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

Alle levensduur van ASP.NET Core Data Protection-token wijzigen

Met de volgende code wordt de time-outperiode van data protection-tokens gewijzigd in drie uur:

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

De ingebouwde Identity gebruikerstokens (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) hebben een time-out van één dag.

Notitie

Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch-vertakkingen of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

De levensduur van het e-mailtoken wijzigen

De standaardtokenduur van de Identity gebruikerstokens is één dag.

Notitie

Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch-vertakkingen of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

Als u de levensduur van het e-mailtoken wilt wijzigen, voegt u een aangepaste DataProtectorTokenProvider<TUser> en DataProtectionTokenProviderOptionstoe.

CustomTokenProvider.cs:

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;

namespace BlazorSample;

public class CustomEmailConfirmationTokenProvider<TUser>
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class EmailConfirmationTokenProviderOptions 
    : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

public class CustomPasswordResetTokenProvider<TUser> 
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomPasswordResetTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<PasswordResetTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class PasswordResetTokenProviderOptions : 
    DataProtectionTokenProviderOptions
{
    public PasswordResetTokenProviderOptions()
    {
        Name = "PasswordResetDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(3);
    }
}

Configureer de services voor het gebruik van de aangepaste tokenprovider in het Program-bestand:

builder.Services.AddIdentityCore<ApplicationUser>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<ApplicationUser>)));
        options.Tokens.EmailConfirmationTokenProvider = 
            "CustomEmailConfirmation";
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

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

Problemen oplossen en diagnosticeren

Als u geen e-mail kunt ontvangen, werkt het volgende:

  • Stel een onderbrekingspunt in EmailSender.Execute in om te controleren of SendEmailAsync wordt aangeroepen.
  • Maak een console-app om e-mail te verzenden met behulp van code die vergelijkbaar is met EmailSender.Execute om het probleem op te sporen.
  • Controleer de e-mailgeschiedenispagina's van het account op de website van de e-mailprovider.
  • Controleer uw spammap op berichten.
  • Probeer een andere e-mailalias op een andere e-mailprovider, zoals Microsoft, Yahoo of Gmail.
  • Probeer naar verschillende e-mailaccounts te verzenden.

Aanvullende informatiebronnen