Utiliser l'cookieauthentification sans ASP.NET CoreIdentity

Par Rick Anderson

ASP.NET Core Identity est un fournisseur d'authentification complet et complet pour la création et la maintenance des connexions. Cependant, un cookiefournisseur d'authentification basé sur – sans ASP.NET Core Identity peut être utilisé. Pour plus d’informations, consultez Présentation de Identity sur ASP.NET Core.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

À des fins de démonstration dans l'exemple d'application, le compte d'utilisateur de l'utilisateur hypothétique, Maria Rodriguez, est codé en dur dans l'application. Utilisez l'adresseEmail maria.rodriguez@contoso.com et un mot de passe pour vous connecter à l'utilisateur. L'utilisateur est authentifié dans la méthode AuthenticateUser du fichier Pages/Account/Login.cshtml.cs. Dans un exemple concret, l'utilisateur serait authentifié par rapport à un magasin de données.

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie();

builder.Services.AddHttpContextAccessor();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

AuthenticationScheme passé à AddAuthentication définit le schéma d'authentification par défaut pour l'application. AuthenticationScheme est utile lorsqu'il existe plusieurs instances d'authentification cookie et que l'application doit autoriser avec un schéma spécifique. La définition de AuthenticationScheme à CookieAuthenticationDefaults.AuthenticationScheme fournit une valeur "Cookies" pour le schéma. Toute valeur de chaîne peut être utilisée pour distinguer le schéma.

Le schéma d'authentification de l'application est différent du schéma d'authentification de l'application cookie. Lorsqu'un schéma d'authentification cookie n'est pas fourni à AddCookie, il utilise CookieAuthenticationDefaults.AuthenticationScheme. La CookieAuthenticationDefaults.AuthenticationScheme source GitHub indique qu'elle est définie sur "Cookies".

La propriété cookie authentification IsEssential est définie sur true par défaut. Les authentifications cookies sont autorisées lorsqu'un visiteur du site n'a pas consenti à la collecte de données. Pour plus d’informations, consultez Prise en charge du règlement général sur la protection des données (RGPD) de l’Union Européenne dans ASP.NET Core.

La classe CookieAuthenticationOptions est utilisée pour configurer les options du fournisseur d'authentification.

Configurez CookieAuthenticationOptions dans la méthode AddCookie :

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        options.SlidingExpiration = true;
        options.AccessDeniedPath = "/Forbidden/";
    });

builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

L'Cookieintergiciel Azure Policy (source GitHub)UseCookiePolicy active les cookie fonctionnalités Azure Policy. Le middleware est traité dans l'ordre dans lequel il est ajouté :

app.UseCookiePolicy(cookiePolicyOptions);

Utilisation fournie CookiePolicyOptions à l'intergiciel Azure Policy Cookie pour contrôler les caractéristiques globales du traitement cookie et se connecter aux gestionnaires de traitement cookie lorsque des cookies sont ajoutés ou supprimés.

La valeur MinimumSameSitePolicy par défaut SameSiteMode.Lax est d'autoriser l'authentification OAuth2. Pour appliquer strictement une politique de même site de SameSiteMode.Strict, définissez le MinimumSameSitePolicy. Bien que ce paramètre rompe OAuth2 et d'autres schémas d'authentification cross-origin, il élève le niveau de sécurité cookie pour d'autres types d'applications qui ne reposent pas sur le traitement des requêtes cross-origin.

var cookiePolicyOptions = new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Strict,
};

Le paramètre Policy Middleware Cookie pour MinimumSameSitePolicy peut affecter le paramètre de Cookie.SameSite dans les paramètres CookieAuthenticationOptions selon la matrice ci-dessous.

MinimumSameSitePolicy Cookie.SameSite Paramètre Cookie.SameSite résultant
SameSiteMode.None SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict

Pour créer un fichier d'informations sur l'utilisateur cookie, construisez un fichier ClaimsPrincipal. Les informations utilisateur sont sérialisées et stockées dans le fichier cookie.

Créez un ClaimsIdentity avec tous les Claim requis et appelez SignInAsync pour connecter l'utilisateur. Login.cshtml.cs dans l’échantillon d’application contient le code suivant :

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    ReturnUrl = returnUrl;

    if (ModelState.IsValid)
    {
        // Use Input.Email and Input.Password to authenticate the user
        // with your custom authentication logic.
        //
        // For demonstration purposes, the sample validates the user
        // on the email address maria.rodriguez@contoso.com with 
        // any password that passes model validation.

        var user = await AuthenticateUser(Input.Email, Input.Password);

        if (user == null)
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Email),
            new Claim("FullName", user.FullName),
            new Claim(ClaimTypes.Role, "Administrator"),
        };

        var claimsIdentity = new ClaimsIdentity(
            claims, CookieAuthenticationDefaults.AuthenticationScheme);

        var authProperties = new AuthenticationProperties
        {
            //AllowRefresh = <bool>,
            // Refreshing the authentication session should be allowed.

            //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
            // The time at which the authentication ticket expires. A 
            // value set here overrides the ExpireTimeSpan option of 
            // CookieAuthenticationOptions set with AddCookie.

            //IsPersistent = true,
            // Whether the authentication session is persisted across 
            // multiple requests. When used with cookies, controls
            // whether the cookie's lifetime is absolute (matching the
            // lifetime of the authentication ticket) or session-based.

            //IssuedUtc = <DateTimeOffset>,
            // The time at which the authentication ticket was issued.

            //RedirectUri = <string>
            // The full path or absolute URI to be used as an http 
            // redirect response value.
        };

        await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme, 
            new ClaimsPrincipal(claimsIdentity), 
            authProperties);

        _logger.LogInformation("User {Email} logged in at {Time}.", 
            user.Email, DateTime.UtcNow);

        return LocalRedirect(Url.GetLocalUrl(returnUrl));
    }

    // Something failed. Redisplay the form.
    return Page();
}

Si vous souhaitez voir les commentaires de code traduits dans une langue autre que l’anglais, dites-le nous dans cette discussion GitHub.

SignInAsync crée un crypté cookie et l'ajoute à la réponse actuelle. Si AuthenticationScheme n'est pas spécifié, le schéma par défaut est utilisé.

RedirectUri n'est utilisé que sur quelques chemins spécifiques par défaut, par exemple, le chemin de connexion et les chemins de déconnexion. Pour plus d'informations, consultez la source CookieAuthenticationHandler.

Le système de protection des données ASP.NET Core est utilisé pour le chiffrement. Pour une application hébergée sur plusieurs machines, l'équilibrage de charge entre les applications ou l'utilisation d'une batterie de serveurs Web, configurez la protection des données pour utiliser le même trousseau de clés et le même identifiant d'application.

Se déconnecter

Pour déconnecter l'utilisateur actuel et supprimer son cookie, appelez SignOutAsync :

public async Task OnGetAsync(string returnUrl = null)
{
    if (!string.IsNullOrEmpty(ErrorMessage))
    {
        ModelState.AddModelError(string.Empty, ErrorMessage);
    }

    // Clear the existing external cookie
    await HttpContext.SignOutAsync(
        CookieAuthenticationDefaults.AuthenticationScheme);

    ReturnUrl = returnUrl;
}

Si CookieAuthenticationDefaults.AuthenticationScheme ou "Cookies" n'est pas utilisé comme schéma, indiquez le schéma utilisé lors de la configuration du fournisseur d'authentification. Sinon, le schéma par défaut est utilisé. Par exemple, si « Contoso Cookie » est utilisé comme schéma, indiquez le schéma utilisé lors de la configuration du fournisseur d'authentification.

Lorsque le navigateur se ferme, il supprime automatiquement les cookies basés sur la session (cookies non persistants), mais aucun cookies n'est effacé lorsqu'un onglet individuel est fermé. Le serveur n'est pas averti des événements de fermeture d'onglet ou de navigateur.

Réagir aux modifications back-end

Une fois le cookie a créé, le cookie est la seule source d'identité. Si un compte utilisateur est désactivé dans les systèmes back-end :

  • Le système cookie d'authentification de l'application continue de traiter les requêtes en fonction de l'authentification cookie.
  • L'utilisateur reste connecté à l'application tant que l'authentification cookie est valide.

L'événement ValidatePrincipal peut être utilisé pour intercepter et remplacer la validation de l'identité cookie. La validation du cookie à chaque requête réduit le risque que des utilisateurs révoqués accèdent à l'application.

Une approche de la validation cookie est basée sur le suivi du moment où la base de données utilisateur change. Si la base de données n'a pas été modifiée depuis la création de l'utilisateur cookie, il n'est pas nécessaire de ré-authentifier l'utilisateur si celui-ci cookie est toujours valide. Dans l'exemple d'application, la base de données est implémentée dans IUserRepository et stocke une valeur LastChanged. Lorsqu'un utilisateur est mis à jour dans la base de données, la valeur LastChanged est définie sur l'heure actuelle.

Afin d'invalider un cookie lorsque la base de données change en fonction de la valeur LastChanged, créez le cookie avec une revendication LastChanged contenant la valeur actuelle LastChanged de la base de données :

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("LastChanged", {Database Value})
};

var claimsIdentity = new ClaimsIdentity(
    claims,
    CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity));

Pour implémenter un override pour l'événement ValidatePrincipal, écrivez une méthode avec la signature suivante dans une classe qui dérive de CookieAuthenticationEvents :

ValidatePrincipal(CookieValidatePrincipalContext)

Voici un exemple d'implémentation de CookieAuthenticationEvents :

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
    private readonly IUserRepository _userRepository;

    public CustomCookieAuthenticationEvents(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
    {
        var userPrincipal = context.Principal;

        // Look for the LastChanged claim.
        var lastChanged = (from c in userPrincipal.Claims
                           where c.Type == "LastChanged"
                           select c.Value).FirstOrDefault();

        if (string.IsNullOrEmpty(lastChanged) ||
            !_userRepository.ValidateLastChanged(lastChanged))
        {
            context.RejectPrincipal();

            await context.HttpContext.SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

Enregistrez l'instance d'événements lors de l'enregistrement du service cookie. Fournissez une inscription au service étendu pour votre classe CustomCookieAuthenticationEvents :

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

builder.Services.AddScoped<CustomCookieAuthenticationEvents>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Considérez une situation dans laquelle le nom de l'utilisateur est mis à jour—une décision qui n'affecte en rien la sécurité. Si vous souhaitez mettre à jour de manière non destructive le principal de l'utilisateur, appelez context.ReplacePrincipal et définissez la propriété context.ShouldRenew sur true.

Avertissement

L'approche décrite ici est déclenchée à chaque requête. La validation des authentifications cookies pour tous les utilisateurs à chaque requête peut entraîner une baisse importante des performances de l'application.

Persistant cookies

Vous souhaiterez peut-être cookie que le persiste d'une session de navigateur à l'autre. Cette persistance ne doit être activée qu'avec le consentement explicite de l'utilisateur avec une case à cocher "Se souvenir de moi" lors de la connexion ou un mécanisme similaire.

L'extrait de code suivant crée une identité et un correspondant cookie qui survivent aux fermetures de navigateur. Tous les paramètres d'expiration glissants précédemment configurés sont honorés. Si le cookie expire alors que le navigateur est fermé, le navigateur efface le cookie une fois qu'il est redémarré.

Définissez IsPersistent sur true dans AuthenticationProperties :

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true
    });

Un délai d'expiration absolu peut être défini avec ExpiresUtc. Pour créer un persistent cookie, IsPersistent doit également être défini. Sinon, le cookie est créé avec une durée de vie basée sur la session et peut expirer avant ou après le ticket d'authentification qu'il contient. Lorsque ExpiresUtc est défini, il remplace la valeur de l'option ExpireTimeSpan de CookieAuthenticationOptions, si elle est définie.

L'extrait de code suivant crée une identité et un correspondant cookie qui dure 20 minutes. Cela ignore tous les paramètres d'expiration glissants précédemment configurés.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
    });

ASP.NET Core Identity est un fournisseur d'authentification complet et complet pour la création et la maintenance des connexions. Cependant, un cookiefournisseur d'authentification basé sur – sans ASP.NET Core Identity peut être utilisé. Pour plus d’informations, consultez Présentation de Identity sur ASP.NET Core.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

À des fins de démonstration dans l'exemple d'application, le compte d'utilisateur de l'utilisateur hypothétique, Maria Rodriguez, est codé en dur dans l'application. Utilisez l'adresseEmail maria.rodriguez@contoso.com et un mot de passe pour vous connecter à l'utilisateur. L'utilisateur est authentifié dans la méthode AuthenticateUser du fichier Pages/Account/Login.cshtml.cs. Dans un exemple concret, l'utilisateur serait authentifié par rapport à une base de données.

Configuration

Dans la méthode Startup.ConfigureServices, créez les services Authentication Middleware avec les méthodes AddAuthentication et AddCookie :

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie();

AuthenticationScheme passé à AddAuthentication définit le schéma d'authentification par défaut pour l'application. AuthenticationScheme est utile lorsqu'il existe plusieurs instances d'authentification cookie et que vous souhaitez autoriser avec un schéma spécifique. La définition de AuthenticationSchemeCookieAuthenticationDefaults.AuthenticationScheme fournit une valeur de « Cookies » pour le schéma. Vous pouvez fournir n'importe quelle valeur de chaîne qui distingue le schéma.

Le schéma d'authentification de l'application est différent du schéma d'authentification de l'application cookie. Lorsqu'un schéma d'authentification cookie n'est pas fourni à AddCookie, il utilise CookieAuthenticationDefaults.AuthenticationScheme ("Cookies").

La propriété cookie authentification IsEssential est définie sur true par défaut. Les authentifications cookies sont autorisées lorsqu'un visiteur du site n'a pas consenti à la collecte de données. Pour plus d’informations, consultez Prise en charge du règlement général sur la protection des données (RGPD) de l’Union Européenne dans ASP.NET Core.

Dans Startup.Configure, appelez UseAuthentication et UseAuthorization pour définir la propriété HttpContext.User et exécutez le middleware d'autorisation pour les demandes. Appelez les méthodes UseAuthentication et UseAuthorization avant d'appeler UseEndpoints :

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

La classe CookieAuthenticationOptions est utilisée pour configurer les options du fournisseur d'authentification.

Défini CookieAuthenticationOptions dans la configuration du service pour l'authentification dans la méthode Startup.ConfigureServices :

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        ...
    });

Cookie L'intergiciel Azure Policy active les capacités de politiquecookie. L'ajout du middleware au pipeline de traitement des applications est sensible à l'ordre—il n'affecte que les composants en aval enregistrés dans le pipeline.

app.UseCookiePolicy(cookiePolicyOptions);

Utilisation fournie CookiePolicyOptions à l'intergiciel Azure Policy Cookie pour contrôler les caractéristiques globales du traitement cookie et se connecter aux gestionnaires de traitement cookie lorsque des cookies sont ajoutés ou supprimés.

La valeur MinimumSameSitePolicy par défaut SameSiteMode.Lax est d'autoriser l'authentification OAuth2. Pour appliquer strictement une politique de même site de SameSiteMode.Strict, définissez le MinimumSameSitePolicy. Bien que ce paramètre rompe OAuth2 et d'autres schémas d'authentification cross-origin, il élève le niveau de sécurité cookie pour d'autres types d'applications qui ne reposent pas sur le traitement des requêtes cross-origin.

var cookiePolicyOptions = new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Strict,
};

Le paramètre Policy Middleware Cookie pour MinimumSameSitePolicy peut affecter le paramètre de Cookie.SameSite dans les paramètres CookieAuthenticationOptions selon la matrice ci-dessous.

MinimumSameSitePolicy Cookie.SameSite Paramètre Cookie.SameSite résultant
SameSiteMode.None SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict

Pour créer un fichier d'informations sur l'utilisateur cookie, construisez un fichier ClaimsPrincipal. Les informations utilisateur sont sérialisées et stockées dans le fichier cookie.

Créez un ClaimsIdentity avec tous les Claims requis et appelez SignInAsync pour vous connecter à l'utilisateur :

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("FullName", user.FullName),
    new Claim(ClaimTypes.Role, "Administrator"),
};

var claimsIdentity = new ClaimsIdentity(
    claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
    //AllowRefresh = <bool>,
    // Refreshing the authentication session should be allowed.

    //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
    // The time at which the authentication ticket expires. A 
    // value set here overrides the ExpireTimeSpan option of 
    // CookieAuthenticationOptions set with AddCookie.

    //IsPersistent = true,
    // Whether the authentication session is persisted across 
    // multiple requests. When used with cookies, controls
    // whether the cookie's lifetime is absolute (matching the
    // lifetime of the authentication ticket) or session-based.

    //IssuedUtc = <DateTimeOffset>,
    // The time at which the authentication ticket was issued.

    //RedirectUri = <string>
    // The full path or absolute URI to be used as an http 
    // redirect response value.
};

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity), 
    authProperties);

Si vous souhaitez voir les commentaires de code traduits dans une langue autre que l’anglais, dites-le nous dans cette discussion GitHub.

SignInAsync crée un crypté cookie et l'ajoute à la réponse actuelle. Si AuthenticationScheme n'est pas spécifié, le schéma par défaut est utilisé.

RedirectUri n'est utilisé que sur quelques chemins spécifiques par défaut, par exemple, le chemin de connexion et les chemins de déconnexion. Pour plus d'informations, consultez la source CookieAuthenticationHandler.

Le système de protection des données ASP.NET Core est utilisé pour le chiffrement. Pour une application hébergée sur plusieurs machines, l'équilibrage de charge entre les applications ou l'utilisation d'une batterie de serveurs Web, configurez la protection des données pour utiliser le même trousseau de clés et le même identifiant d'application.

Se déconnecter

Pour déconnecter l'utilisateur actuel et supprimer son cookie, appelez SignOutAsync :

await HttpContext.SignOutAsync(
    CookieAuthenticationDefaults.AuthenticationScheme);

Si CookieAuthenticationDefaults.AuthenticationScheme (ou "Cookies") n'est pas utilisé comme schéma (par exemple, "ContosoCookie"), indiquez le schéma utilisé lors de la configuration du fournisseur d'authentification. Sinon, le schéma par défaut est utilisé.

Lorsque le navigateur se ferme, il supprime automatiquement les cookies basés sur la session (cookies non persistants), mais aucun cookies n'est effacé lorsqu'un onglet individuel est fermé. Le serveur n'est pas averti des événements de fermeture d'onglet ou de navigateur.

Réagir aux modifications back-end

Une fois le cookie a créé, le cookie est la seule source d'identité. Si un compte utilisateur est désactivé dans les systèmes back-end :

  • Le système cookie d'authentification de l'application continue de traiter les requêtes en fonction de l'authentification cookie.
  • L'utilisateur reste connecté à l'application tant que l'authentification cookie est valide.

L'événement ValidatePrincipal peut être utilisé pour intercepter et remplacer la validation de l'identité cookie. La validation du cookie à chaque requête réduit le risque que des utilisateurs révoqués accèdent à l'application.

Une approche de la validation cookie est basée sur le suivi du moment où la base de données utilisateur change. Si la base de données n'a pas été modifiée depuis la création de l'utilisateur cookie, il n'est pas nécessaire de ré-authentifier l'utilisateur si celui-ci cookie est toujours valide. Dans l'exemple d'application, la base de données est implémentée dans IUserRepository et stocke une valeur LastChanged. Lorsqu'un utilisateur est mis à jour dans la base de données, la valeur LastChanged est définie sur l'heure actuelle.

Afin d'invalider un cookie lorsque la base de données change en fonction de la valeur LastChanged, créez le cookie avec une revendication LastChanged contenant la valeur actuelle LastChanged de la base de données :

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("LastChanged", {Database Value})
};

var claimsIdentity = new ClaimsIdentity(
    claims, 
    CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity));

Pour implémenter un override pour l'événement ValidatePrincipal, écrivez une méthode avec la signature suivante dans une classe qui dérive de CookieAuthenticationEvents :

ValidatePrincipal(CookieValidatePrincipalContext)

Voici un exemple d'implémentation de CookieAuthenticationEvents :

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
    private readonly IUserRepository _userRepository;

    public CustomCookieAuthenticationEvents(IUserRepository userRepository)
    {
        // Get the database from registered DI services.
        _userRepository = userRepository;
    }

    public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
    {
        var userPrincipal = context.Principal;

        // Look for the LastChanged claim.
        var lastChanged = (from c in userPrincipal.Claims
                           where c.Type == "LastChanged"
                           select c.Value).FirstOrDefault();

        if (string.IsNullOrEmpty(lastChanged) ||
            !_userRepository.ValidateLastChanged(lastChanged))
        {
            context.RejectPrincipal();

            await context.HttpContext.SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

Enregistrez l'instance d'événements lors cookie de l'enregistrement du service Startup.ConfigureServices. Fournissez une inscription au service étendu pour votre classe CustomCookieAuthenticationEvents :

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

services.AddScoped<CustomCookieAuthenticationEvents>();

Considérez une situation dans laquelle le nom de l'utilisateur est mis à jour—une décision qui n'affecte en rien la sécurité. Si vous souhaitez mettre à jour de manière non destructive le principal de l'utilisateur, appelez context.ReplacePrincipal et définissez la propriété context.ShouldRenew sur true.

Avertissement

L'approche décrite ici est déclenchée à chaque requête. La validation des authentifications cookies pour tous les utilisateurs à chaque requête peut entraîner une baisse importante des performances de l'application.

Persistant cookies

Vous souhaiterez peut-être cookie que le persiste d'une session de navigateur à l'autre. Cette persistance ne doit être activée qu'avec le consentement explicite de l'utilisateur avec une case à cocher "Se souvenir de moi" lors de la connexion ou un mécanisme similaire.

L'extrait de code suivant crée une identité et un correspondant cookie qui survivent aux fermetures de navigateur. Tous les paramètres d'expiration glissants précédemment configurés sont honorés. Si le cookie expire alors que le navigateur est fermé, le navigateur efface le cookie une fois qu'il est redémarré.

Définissez IsPersistent sur true dans AuthenticationProperties :

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true
    });

Un délai d'expiration absolu peut être défini avec ExpiresUtc. Pour créer un persistent cookie, IsPersistent doit également être défini. Sinon, le cookie est créé avec une durée de vie basée sur la session et peut expirer avant ou après le ticket d'authentification qu'il contient. Lorsque ExpiresUtc est défini, il remplace la valeur de l'option ExpireTimeSpan de CookieAuthenticationOptions, si elle est définie.

L'extrait de code suivant crée une identité et un correspondant cookie qui dure 20 minutes. Cela ignore tous les paramètres d'expiration glissants précédemment configurés.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
    });