Conferma dell'account e ripristino della password in ASP.NET Core Blazor

Questo articolo illustra come configurare un'app Web core Blazor di ASP.NET con la conferma tramite posta elettronica e il ripristino della password.

Spazio dei nomi

Lo spazio dei nomi dell'app usato dall'esempio in questo articolo è BlazorSample. Aggiornare gli esempi di codice per usare lo spazio dei nomi dell'app.

Selezionare e configurare un provider di posta elettronica

In questo articolo l'API transazionale di Mailchimp viene usata tramite Mandrill.net per inviare messaggi di posta elettronica. È consigliabile usare un servizio di posta elettronica per inviare messaggi di posta elettronica anziché SMTP. SMTP è difficile da configurare e proteggere correttamente. Indipendentemente dal servizio di posta elettronica usato, accedere alle linee guida per le app .NET, creare un account, configurare una chiave API per il servizio e installare i pacchetti NuGet necessari.

Creare una classe per recuperare la chiave API di posta elettronica sicura. L'esempio in questo articolo usa una classe denominata AuthMessageSenderOptions con una EmailAuthKey proprietà per contenere la chiave.

AuthMessageSenderOptions:

namespace BlazorSample;

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

Registrare l'istanza AuthMessageSenderOptions di configurazione nel Program file :

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

Configurare un segreto utente per la chiave di sicurezza del provider

Impostare la chiave con lo strumento secret-manager. Nell'esempio seguente il nome della chiave è EmailAuthKey e la chiave è rappresentata dal {KEY} segnaposto. In una shell dei comandi passare alla cartella radice dell'app ed eseguire il comando seguente con la chiave API:

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

Per altre informazioni, vedere Archiviazione sicura dei segreti delle app in fase di sviluppo in ASP.NET Core.

Implementare IEmailSender

Implementare IEmailSender per il provider. L'esempio seguente si basa sull'API transazionale di Mailchimp usando Mandrill.net. Per un provider diverso, vedere la relativa documentazione su come implementare l'invio di un messaggio nel Execute metodo .

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(ApplicationUser user, string email, 
        string confirmationLink) => SendEmailAsync(email, "Confirm your email", 
        $"Please confirm your account by " +
        "<a href='{confirmationLink}'>clicking here</a>.");

    public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, 
        string resetLink) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

    public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, 
        string resetCode) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password using the following code: {resetCode}");

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

Nota

Il contenuto del corpo per i messaggi potrebbe richiedere una codifica speciale per il provider di servizi di posta elettronica. Se non è possibile seguire i collegamenti nel corpo del messaggio, consultare la documentazione del provider di servizi.

Configurare l'app per supportare la posta elettronica

Program Nel file modificare l'implementazione del mittente del messaggio di posta elettronica in EmailSender:

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

Rimuovere (IdentityNoOpEmailSenderComponents/Account/IdentityNoOpEmailSender.cs) dall'app.

RegisterConfirmation Nel componente (Components/Account/Pages/RegisterConfirmation.razor) rimuovere il blocco condizionale nel @code blocco che controlla se EmailSender è un IdentityNoOpEmailSenderoggetto :

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

Anche nel RegisterConfirmation componente rimuovere il markup e il Razor codice per controllare il emailConfirmationLink campo, lasciando solo la riga che indica all'utente di controllare il proprio messaggio di posta elettronica ...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

Timeout di posta elettronica e attività

Il timeout di inattività predefinito è 14 giorni. Il codice seguente imposta il timeout di inattività su 5 giorni con scadenza scorrevole:

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

Modificare tutta la durata dei token di protezione dei dati

Il codice seguente modifica il periodo di timeout di tutti i token di protezione dei dati a 3 ore:

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

I token utente predefiniti Identity (AspNetCore/src//IdentityExtensions.Core/src/TokenOptions.cs) hanno un timeout di un giorno.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Modificare la durata del token di posta elettronica

La durata del token predefinita dei token utente è un giorno.Identity

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Per modificare la durata del token di posta elettronica, aggiungere un valore personalizzato DataProtectorTokenProvider<TUser> e DataProtectionTokenProviderOptions:

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

Configurare i servizi per l'uso del provider di token personalizzato nel Program file:

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

Risoluzione dei problemi

Se non è possibile ricevere messaggi di posta elettronica funzionanti:

  • Impostare un punto di interruzione in EmailSender.Execute per verificare SendEmailAsync che venga chiamato .
  • Creare un'app console per inviare messaggi di posta elettronica usando codice simile al EmailSender.Execute debug del problema.
  • Esaminare le pagine della cronologia dell'account nel sito Web del provider di posta elettronica.
  • Controllare la cartella della posta indesiderata per i messaggi.
  • Provare un altro alias di posta elettronica su un provider di posta elettronica diverso, ad esempio Microsoft, Yahoo o Gmail.
  • Provare a inviare a account di posta elettronica diversi.

Avviso

Non usare segreti di produzione in fase di test e sviluppo. Se si pubblica l'app in Azure, impostare i segreti come impostazioni dell'applicazione nel portale di App Web di Azure. Il sistema di configurazione è configurato per leggere le chiavi dalle variabili di ambiente.

Abilitare la conferma dell'account dopo che un sito ha utenti

L'abilitazione della conferma dell'account in un sito con gli utenti blocca tutti gli utenti esistenti. Gli utenti esistenti vengono bloccati perché gli account non vengono confermati. Per aggirare il blocco utente esistente, usare uno degli approcci seguenti:

  • Aggiornare il database per contrassegnare tutti gli utenti esistenti come confermati.
  • Confermare gli utenti esistenti. Ad esempio, inviare messaggi di posta elettronica in batch con collegamenti di conferma.

Risorse aggiuntive