Partager via


Confirmation de compte et récupération de mot de passe dans ASP.NET Core Blazor

Cet article explique comment configurer une application web ASP.NET Core Blazor avec confirmation par e-mail et réinitialisation de mot de passe.

Espace de noms

L’espace de noms de l’application utilisé par l’exemple de cet article est BlazorSample. Mettez à jour les exemples de code pour utiliser l’espace de noms de votre application.

Sélectionner et configurer un fournisseur de messagerie électronique

Dans cet article, l’API transactionnelle de Mailchimp est utilisée via Mandrill.net pour envoyer un e-mail. Nous vous recommandons d’utiliser un service de messagerie pour envoyer des e-mails plutôt que SMTP. SMTP est difficile à configurer et à sécuriser correctement. Quel que soit le service de messagerie que vous utilisez, accédez à leurs conseils pour les applications .NET, créez un compte, configurez une clé API pour leur service et installez les packages NuGet requis.

Créez une classe pour récupérer (fetch) la clé API de messagerie sécurisée. L’exemple de cet article utilise une classe nommée AuthMessageSenderOptions avec une propriété EmailAuthKey pour contenir la clé.

AuthMessageSenderOptions:

namespace BlazorSample;

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

Enregistrer l’instance de configuration AuthMessageSenderOptions dans le fichier Program :

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

Configurer un secret utilisateur pour la clé de sécurité du fournisseur

Définissez la clé avec l’outil secret-manager. Dans l’exemple suivant, le nom de la clé est EmailAuthKey et la clé est représentée par l’espace réservé {KEY}. Dans un interpréteur de commandes, accédez au dossier racine de l’application et exécutez la commande suivante avec la clé API :

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

Pour plus d’informations, consultez Stockage sécurisé des secrets d’application dans le développement en ASP.NET Core.

Implémentez IEmailSender

Implémentez IEmailSender pour le fournisseur. L’exemple suivant est basé sur l’API transactionnelle de Mailchimp à l’aide de Mandrill.net. Pour un autre fournisseur, reportez-vous à leur documentation sur la façon d’implémenter l’envoi d’un message dans la méthode Execute.

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

Remarque

Le contenu du corps des messages peut nécessiter un encodage spécial pour le fournisseur du service de messagerie. Si certains liens dans le corps du message ne fonctionnent pas, consultez la documentation du fournisseur de services.

Configurer l’application pour prendre en charge les e-mails

Dans le fichier Program, remplacez l’implémentation de l’expéditeur d’e-mail par EmailSender :

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

Supprimez le IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) de l’application.

Dans le composant RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor), supprimez le bloc conditionnel dans le bloc @code qui vérifie si EmailSender est IdentityNoOpEmailSender :

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

En outre, dans le composant RegisterConfirmation, supprimez le balisage Razor et le code pour vérifier le champ emailConfirmationLink, en laissant uniquement la ligne demandant à l’utilisateur de vérifier son e-mail ...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

E-mail et délai d’expiration d’activité

Le délai d'inactivité par défaut est de 14 jours. Le code suivant définit le délai d’expiration d’activité à 5 jours avec une fenêtre d’expiration glissante :

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

Modifier les durées de vie de tous les jetons de protection des données

Le code suivant modifie la période de délai d’expiration de tous les jetons de protection des données à 3 heures :

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

Les jetons utilisateur Identity intégrés (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) ont un délai d’expiration d’un jour.

Remarque

Les liens de documentation vers la source de référence .NET chargent généralement la branche par défaut du référentiel, qui représente le développement actuel pour la prochaine version de .NET. Pour sélectionner une balise pour une version spécifique, utilisez la liste déroulante Échanger les branches ou les balises. Pour plus d’informations, consultez Comment sélectionner une balise de version du code source ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Modifier la durée de vie du jeton d’e-mail

La durée de vie par défaut des jetons utilisateur Identity est d’un jour.

Remarque

Les liens de documentation vers la source de référence .NET chargent généralement la branche par défaut du référentiel, qui représente le développement actuel pour la prochaine version de .NET. Pour sélectionner une balise pour une version spécifique, utilisez la liste déroulante Échanger les branches ou les balises. Pour plus d’informations, consultez Comment sélectionner une balise de version du code source ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Pour modifier la durée de vie du jeton d’e-mail, ajoutez un DataProtectorTokenProvider<TUser> et DataProtectionTokenProviderOptions personnalisés :

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

Configurer les services pour utiliser le fournisseur de jetons personnalisé dans le fichier Program :

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

Résolution des problèmes

Si vous ne parvenez pas à faire fonctionner les e-mails :

  • Définissez un point d’arrêt dans EmailSender.Execute pour vérifier que SendEmailAsync est appelé.
  • Créez une application console pour envoyer un e-mail à l’aide de code similaire à EmailSender.Execute pour déboguer le problème.
  • Passez en revue les pages d’historique des e-mails du compte sur le site web du fournisseur de messagerie.
  • Vérifiez les messages dans votre dossier de courrier indésirable.
  • Essayez un autre alias de messagerie sur un autre fournisseur de messagerie, tel que Microsoft, Yahoo, ou Gmail.
  • Essayez de faire un envoi à d’autres comptes de messagerie.

Avertissement

N’utilisez pas de secrets de production dans le test et le développement. Si vous publiez l’application sur Azure, définissez les secrets comme paramètres d’application dans le portail d’application web Azure. Le système de configuration est configuré pour lire les clés des variables d'environnement.

Activer la confirmation de compte après qu’un site a des utilisateurs

L’activation de la confirmation de compte sur un site avec des utilisateurs bloque tous les utilisateurs existants. Les utilisateurs existants sont bloqués, car leurs comptes ne sont pas confirmés. Pour contourner un blocage utilisateur existant, utilisez l’une des approches suivantes :

  • Mettez à jour la base de données pour marquer tous les utilisateurs existants comme étant confirmés.
  • Confirmer les utilisateurs existants. Par exemple, envoyez des e-mails par lots avec des liens de confirmation.

Ressources supplémentaires