Présentation de Identity sur ASP.NET Core

Par Rick Anderson

ASP.NET Core Identity :

  • Est une API qui prend en charge la fonctionnalité de connexion par interface utilisateur.
  • Gère les utilisateurs, les mots de passe, les données de profil, les rôles, les revendications, les jetons, la confirmation par e-mail, etc.

Les utilisateurs peuvent créer un compte avec les informations de connexion stockées dans Identity ou utiliser un fournisseur de connexion externe. Les fournisseurs de connexion externes pris en charge incluent Facebook, Google, Compte Microsoft et Twitter.

Pour plus d’informations sur la façon d’exiger l’authentification globale de tous les utilisateurs, consultez la section Exiger des utilisateurs authentifiés.

Le code source Identity est disponible sur GitHub. Générez automatiquement des modèles Identity et affichez les fichiers générés pour passer en revue l’interaction du modèle avec Identity.

Identity est généralement configuré à l’aide d’une base de données SQL Server pour stocker les noms d’utilisateur, les mots de passe et les données de profil. Vous pouvez également utiliser un autre magasin persistant, par exemple Stockage Table Azure.

Dans cette rubrique, vous allez apprendre à utiliser Identity pour inscrire, connecter et déconnecter un utilisateur. Remarque : les modèles traitent le nom d’utilisateur et l’adresse e-mail comme étant identiques pour les utilisateurs. Pour obtenir des instructions plus détaillées sur la création d’applications qui utilisent Identity, consultez la section Étapes suivantes.

ASP.NET Core Identity n’est pas lié à la plateforme d’identités Microsoft. La plateforme d’identités Microsoft est :

  • Une évolution de la plateforme de développement Azure Active Directory (Azure AD).
  • Une solution d’identité alternative pour l’authentification et l’autorisation dans les applications ASP.NET Core.

ASP.NET Core Identity ajoute la fonctionnalité de connexion de l’interface utilisateur aux applications web ASP.NET Core. Pour sécuriser les API web et SPA, utilisez l’une des options suivantes :

Duende Identity Server est un framework OpenID Connect et OAuth 2.0 pour ASP.NET Core. Duende Identity Server active les fonctionnalités de sécurité suivantes :

  • Authentification en tant que service (AaaS)
  • Authentification/déconnexion unique (SSO) sur plusieurs types d’applications
  • Contrôle d’accès pour les API
  • Federation Gateway

Important

Duende Software peut vous demander de payer des frais de licence pour une utilisation en production de Duende Identity Server. Pour plus d’informations, consultez Migrer de ASP.NET Core 5.0 vers 6.0.

Pour plus d’informations, consultez la documentation Duende Identity Server (site web de Duende Software).

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

Créer une application web avec authentification

Créez un projet d’application web ASP.NET Core avec des comptes d’utilisateur individuels.

  • Sélectionnez le modèle Application web ASP.NET Core. Nommez le projet WebApp1 pour que l’espace de noms soit le même que le téléchargement du projet. Cliquez sur OK.
  • Dans l’entrée Type d’authentification, sélectionnez Comptes d’utilisateur individuels.

Le projet généré fournit ASP.NET Core Identity en tant que bibliothèque de classes Razor. La bibliothèque de classes IdentityRazor expose les points de terminaison avec la zone Identity. Par exemple :

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Appliquer des migrations

Appliquez les migrations pour initialiser la base de données.

Exécutez la commande suivante dans la console du Gestionnaire de package (PMC) :

Update-Database

Tester l’inscription et la connexion

Exécutez l’application et inscrivez un utilisateur. Selon la taille de votre écran, vous devrez peut-être sélectionner le bouton bascule de navigation pour afficher les liens d’inscription et de connexion.

Afficher la base de données Identity

  • Dans le menu Affichage, sélectionnez Explorateur d’objets SQL Server (SSOX).
  • Accédez à (localdb)MSSQLLocalDB(SQL Server 13). Cliquez avec le bouton droit sur dbo.AspNetUsers>Afficher les données :

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Configurer les services Identity

Les services sont ajoutés dans Program.cs. Le modèle classique consiste à appeler les méthodes dans l’ordre suivant :

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

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

Le code précédent configure Identity avec les valeurs d’option par défaut. Les services sont accessibles à l’application au moyen de l’injection de dépendances.

Identity est activé en appelant UseAuthentication. UseAuthentication ajoute un intergiciel d’authentification au pipeline de requêtes.

L’application générée par un modèle n’utilise pas l’autorisation. app.UseAuthorization est inclus pour s’assurer qu’il est ajouté dans l’ordre correct si l’application ajoute une autorisation. UseRouting, UseAuthentication et UseAuthorization doivent être appelés dans l’ordre indiqué dans le code précédent.

Pour plus d’informations sur IdentityOptions, consultez les articles IdentityOptions et Démarrage d’une application.

Générer automatiquement les modèles Register, Login, LogOut et RegisterConfirmation

Ajoutez les fichiers Register, Login, LogOut et RegisterConfirmation. Suivez les instructions de la section Générer un modèle d’Identity dans un projet Razor avec autorisation pour générer le code présenté dans cette section.

Examiner l’inscription

Quand un utilisateur clique sur le bouton d’inscription sur la page Register, l’action RegisterModel.OnPostAsync est appelée. L’utilisateur est créé par CreateAsync(TUser) sur l’objet _userManager :

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Désactiver la vérification du compte par défaut

Avec les modèles par défaut, l’utilisateur est redirigé vers Account.RegisterConfirmation où il peut sélectionner un lien pour que le compte soit confirmé. La valeur par défaut Account.RegisterConfirmation est utilisée uniquement à des fins de test. La vérification automatique du compte doit être désactivée dans une application de production.

Pour exiger un compte confirmé et empêcher la connexion immédiate lors de l’inscription, définissez DisplayConfirmAccountLink = false dans /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs :

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

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

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

Se connecter

Le formulaire de connexion s’affiche dans les cas suivants :

  • Le lien de connexion est sélectionné.
  • Un utilisateur tente d’accéder à une page restreinte à laquelle il n’est pas autorisé à accéder ou quand il n’a pas été authentifié par le système.

Quand le formulaire de la page de connexion est envoyé, l’action OnPostAsync est appelée. PasswordSignInAsync est appelé sur l’objet _signInManager.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Pour plus d’informations sur la prise de décisions en ce qui concerne l’autorisation, consultez l’article Présentation de l’autorisation dans ASP.NET Core.

Se déconnecter

Le lien de déconnexion appelle l’action LogoutModel.OnPost.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Dans le code précédent, le code return RedirectToPage(); doit être une redirection afin que le navigateur effectue une nouvelle demande et que l’identité de l’utilisateur soit mise à jour.

SignOutAsync efface les revendications de l’utilisateur stockées dans un cookie.

Post est spécifié dans Pages/Shared/_LoginPartial.cshtml :

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Tester Identity

Les modèles de projet web par défaut autorisent l’accès anonyme aux pages d’accueil. Pour tester Identity, ajoutez [Authorize] :

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Si vous êtes connecté, déconnectez-vous. Exécutez l'application et sélectionnez le lien Privacy. Vous êtes redirigé vers la page de connexion.

Explorer Identity

Pour explorer Identity en détail :

Identity Components

Tous les packages NuGet dépendants d’Identity sont inclus dans l’infrastructure partagée ASP.NET Core.

Le package principal pour Identity est Microsoft.AspNetCore.Identity. Ce package contient l’ensemble d’interfaces de base pour ASP.NET Core Identity et est inclus par Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migration vers ASP.NET Core Identity

Pour plus d’informations et pour obtenir de l’aide sur la migration de votre magasin Identity existant, consultez l’article Migrer l’authentification et Identity.

Définition de la force du mot de passe

Consultez la section sur la configuration pour obtenir un exemple qui définit les exigences minimales relatives aux mots de passe.

AddDefaultIdentity et AddIdentity

AddDefaultIdentity a été introduit dans ASP.NET Core 2.1. L’appel de AddDefaultIdentity est similaire à l’appel de ce qui suit :

Pour plus d’informations, consultez la source AddDefaultIdentity.

Empêcher la publication de ressources Identity statiques

Pour empêcher la publication de ressources Identity statiques (feuilles de style et fichiers JavaScript pour l’interface utilisateur Identity) sur la racine web, ajoutez la propriété et la cible ResolveStaticWebAssetsInputsDependsOn suivantes RemoveIdentityAssets au fichier projet de l’application :

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Étapes suivantes

Par Rick Anderson

ASP.NET Core Identity :

  • Est une API qui prend en charge la fonctionnalité de connexion par interface utilisateur.
  • Gère les utilisateurs, les mots de passe, les données de profil, les rôles, les revendications, les jetons, la confirmation par e-mail, etc.

Les utilisateurs peuvent créer un compte avec les informations de connexion stockées dans Identity ou utiliser un fournisseur de connexion externe. Les fournisseurs de connexion externes pris en charge incluent Facebook, Google, Compte Microsoft et Twitter.

Pour plus d’informations sur la façon d’exiger l’authentification globale de tous les utilisateurs, consultez la section Exiger des utilisateurs authentifiés.

Le code source Identity est disponible sur GitHub. Générez automatiquement des modèles Identity et affichez les fichiers générés pour passer en revue l’interaction du modèle avec Identity.

Identity est généralement configuré à l’aide d’une base de données SQL Server pour stocker les noms d’utilisateur, les mots de passe et les données de profil. Vous pouvez également utiliser un autre magasin persistant, par exemple Stockage Table Azure.

Dans cette rubrique, vous allez apprendre à utiliser Identity pour inscrire, connecter et déconnecter un utilisateur. Remarque : les modèles traitent le nom d’utilisateur et l’adresse e-mail comme étant identiques pour les utilisateurs. Pour obtenir des instructions plus détaillées sur la création d’applications qui utilisent Identity, consultez la section Étapes suivantes.

La plateforme d’identités Microsoft est :

  • Une évolution de la plateforme de développement Azure Active Directory (Azure AD).
  • Une solution d’identité alternative pour l’authentification et l’autorisation dans les applications ASP.NET Core.
  • Non liée à ASP.NET Core Identity.

ASP.NET Core Identity ajoute la fonctionnalité de connexion de l’interface utilisateur aux applications web ASP.NET Core. Pour sécuriser les API web et SPA, utilisez l’une des options suivantes :

Duende Identity Server est un framework OpenID Connect et OAuth 2.0 pour ASP.NET Core. Duende Identity Server active les fonctionnalités de sécurité suivantes :

  • Authentification en tant que service (AaaS)
  • Authentification/déconnexion unique (SSO) sur plusieurs types d’applications
  • Contrôle d’accès pour les API
  • Federation Gateway

Pour plus d’informations, consultez Vue d’ensemble de Duende IdentityServer.

Pour plus d’informations sur les autres fournisseurs d’authentification, consultez Options d’authentification de la communauté Open Source pour ASP.NET Core

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

Créer une application web avec authentification

Créez un projet d’application web ASP.NET Core avec des comptes d’utilisateur individuels.

  • Sélectionnez Fichier>Nouveau>Projet.
  • Sélectionnez Application web ASP.NET Core. Nommez le projet WebApp1 pour que l’espace de noms soit le même que le téléchargement du projet. Cliquez sur OK.
  • Sélectionnez une application web ASP.NET Core, puis sélectionnez Modifier l’authentification.
  • Sélectionnez Comptes d’utilisateur individuels, puis cliquez sur OK.

Le projet généré fournit ASP.NET Core Identity en tant que bibliothèque de classes Razor. La bibliothèque de classes IdentityRazor expose les points de terminaison avec la zone Identity. Par exemple :

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Appliquer des migrations

Appliquez les migrations pour initialiser la base de données.

Exécutez la commande suivante dans la console du Gestionnaire de package (PMC) :

PM> Update-Database

Tester l’inscription et la connexion

Exécutez l’application et inscrivez un utilisateur. Selon la taille de votre écran, vous devrez peut-être sélectionner le bouton bascule de navigation pour afficher les liens d’inscription et de connexion.

Afficher la base de données Identity

  • Dans le menu Affichage, sélectionnez Explorateur d’objets SQL Server (SSOX).
  • Accédez à (localdb)MSSQLLocalDB(SQL Server 13). Cliquez avec le bouton droit sur dbo.AspNetUsers>Afficher les données :

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Configurer les services Identity

Les services sont ajoutés dans ConfigureServices. Le modèle par défaut consiste à appeler toutes les méthodes Add{Service}, puis toutes les méthodes services.Configure{Service}.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Le code mise en surbrillance précédent configure Identity avec les valeurs d’option par défaut. Les services sont accessibles à l’application au moyen de l’injection de dépendances.

Identity est activé en appelant UseAuthentication. UseAuthentication ajoute un intergiciel d’authentification au pipeline de requêtes.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Le code précédent configure Identity avec les valeurs d’option par défaut. Les services sont accessibles à l’application au moyen de l’injection de dépendances.

Identity est activé en appelant UseAuthentication. UseAuthentication ajoute un intergiciel d’authentification au pipeline de requêtes.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

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

L’application générée par un modèle n’utilise pas l’autorisation. app.UseAuthorization est inclus pour s’assurer qu’il est ajouté dans l’ordre correct si l’application ajoute une autorisation. UseRouting, UseAuthentication, UseAuthorization et UseEndpoints doivent être appelés dans l’ordre indiqué dans le code précédent.

Pour plus d’informations sur IdentityOptions et Startup, consultez les articles IdentityOptions et Démarrage d’une application.

Générer automatiquement les modèles Register, Login, LogOut et RegisterConfirmation

Ajoutez les fichiers Register, Login, LogOut et RegisterConfirmation. Suivez les instructions de la section Générer un modèle d’Identity dans un projet Razor avec autorisation pour générer le code présenté dans cette section.

Examiner l’inscription

Quand un utilisateur clique sur le bouton d’inscription sur la page Register, l’action RegisterModel.OnPostAsync est appelée. L’utilisateur est créé par CreateAsync(TUser) sur l’objet _userManager :

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Désactiver la vérification du compte par défaut

Avec les modèles par défaut, l’utilisateur est redirigé vers Account.RegisterConfirmation où il peut sélectionner un lien pour que le compte soit confirmé. La valeur par défaut Account.RegisterConfirmation est utilisée uniquement à des fins de test. La vérification automatique du compte doit être désactivée dans une application de production.

Pour exiger un compte confirmé et empêcher la connexion immédiate lors de l’inscription, définissez DisplayConfirmAccountLink = false dans /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs :

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

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

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

Se connecter

Le formulaire de connexion s’affiche dans les cas suivants :

  • Le lien de connexion est sélectionné.
  • Un utilisateur tente d’accéder à une page restreinte à laquelle il n’est pas autorisé à accéder ou quand il n’a pas été authentifié par le système.

Quand le formulaire de la page de connexion est envoyé, l’action OnPostAsync est appelée. PasswordSignInAsync est appelé sur l’objet _signInManager.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Pour plus d’informations sur la prise de décisions en ce qui concerne l’autorisation, consultez l’article Présentation de l’autorisation dans ASP.NET Core.

Se déconnecter

Le lien de déconnexion appelle l’action LogoutModel.OnPost.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Dans le code précédent, le code return RedirectToPage(); doit être une redirection afin que le navigateur effectue une nouvelle demande et que l’identité de l’utilisateur soit mise à jour.

SignOutAsync efface les revendications de l’utilisateur stockées dans un cookie.

Post est spécifié dans Pages/Shared/_LoginPartial.cshtml :

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Tester Identity

Les modèles de projet web par défaut autorisent l’accès anonyme aux pages d’accueil. Pour tester Identity, ajoutez [Authorize] :

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Si vous êtes connecté, déconnectez-vous. Exécutez l'application et sélectionnez le lien Privacy. Vous êtes redirigé vers la page de connexion.

Explorer Identity

Pour explorer Identity en détail :

Identity Components

Tous les packages NuGet dépendants d’Identity sont inclus dans l’infrastructure partagée ASP.NET Core.

Le package principal pour Identity est Microsoft.AspNetCore.Identity. Ce package contient l’ensemble d’interfaces de base pour ASP.NET Core Identity et est inclus par Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migration vers ASP.NET Core Identity

Pour plus d’informations et pour obtenir de l’aide sur la migration de votre magasin Identity existant, consultez l’article Migrer l’authentification et Identity.

Définition de la force du mot de passe

Consultez la section sur la configuration pour obtenir un exemple qui définit les exigences minimales relatives aux mots de passe.

Empêcher la publication de ressources Identity statiques

Pour empêcher la publication de ressources Identity statiques (feuilles de style et fichiers JavaScript pour l’interface utilisateur Identity) sur la racine web, ajoutez la propriété et la cible ResolveStaticWebAssetsInputsDependsOn suivantes RemoveIdentityAssets au fichier projet de l’application :

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Étapes suivantes