Authentification .NET avec EF Core, Identity et DB2 for i (IBM i)

Introduction

ASP.NET Core Identity est le système d'authentification intégré à .NET. Il gère l'inscription, la connexion, le hachage des mots de passe, les rôles et la protection des routes, le tout sans avoir à développer quoi que ce soit à la main. Pages de login, d'inscription, de gestion du compte, réinitialisation de mot de passe... tout est généré automatiquement et prêt à l'emploi.

Par défaut, Identity nécessite un connecteur compatible EF Core pour stocker ses données, comme SQL Server, PostgreSQL, SQLite... Avec l’extension Entity Framework Core de NTi, vous pouvez utiliser DB2 for i comme base de stockage.

En effet, Microsoft Identity s'appuie sur EF Core pour persister ces données en base, et c'est ce point d'entrée que NTi exploite pour rediriger le stockage vers votre IBM i.

Architecture .NET avec Identity, EF Core et NTi vers IBM i

Ce tutoriel vous montre comment mettre en place Identity dans une application Blazor Server, avec les tables utilisateurs créées automatiquement dans une bibliothèque DB2 for i via les migrations EF Core.


Prérequis

  • .NET SDK 8
  • Visual Studio 2022 ou équivalent
  • Microsoft.EntityFrameworkCore.Design en version 8.0.X

Étape 1 - Créer le projet

Créez un nouveau projet Blazor Web App vide en .NET 8 :

dotnet new blazor -n NtiIdentityDemo -f net8.0 --empty
cd NtiIdentityDemo

💡 Si vous utilisez Visual Studio, créez un projet Blazor Web App, sélectionnez .NET 8 comme framework, Server comme render mode.


Étape 2 - Installer les packages NuGet

Quatre packages sont nécessaires, chacun avec un rôle bien précis:

dotnet add package Aumerial.Data.Nti
dotnet add package Aumerial.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 8.0.*
dotnet add package Microsoft.EntityFrameworkCore.Design --version 8.0.*
Package Rôle
Aumerial.Data.Nti Connexion TCP/IP vers l'IBM i
Aumerial.EntityFrameworkCore Extension EF Core pour DB2 for i
Microsoft.AspNetCore.Identity.EntityFrameworkCore Lien entre Identity et EF Core
Microsoft.EntityFrameworkCore.Design Outils de migration (dotnet ef)

💡 Les packages Identity.EntityFrameworkCore et EntityFrameworkCore.Design doivent impérativement être en version 8.0.X pour assurer la compatibilité avec Aumerial.EntityFrameworkCore et .NET 8.


Étape 3 - Créer le dossier Data

Créez un dossier Data/ à la racine du projet. Il contiendra les deux classes nécessaires au fonctionnement d'Identity avec EF Core.

Data/ApplicationUser.cs

ApplicationUser est la classe qui représente un utilisateur dans votre application. Elle hérite d'IdentityUser, qui contient déjà tous les champs nécessaires : identifiant, nom d'utilisateur, email, hash du mot de passe, etc.

using Microsoft.AspNetCore.Identity;

namespace NtiIdentityDemo.Data
{
    public class ApplicationUser : IdentityUser
    {
    }
}

Pour l'instant elle est vide, et c'est normal. Si vous souhaitez stocker des informations supplémentaires sur vos utilisateurs (nom, prénom, département...), c'est ici que vous les ajouterez :

public class ApplicationUser : IdentityUser
{
    public string? Nom { get; set; }
    public string? Prenom { get; set; }
}

💡 Chaque propriété ajoutée ici deviendra une colonne dans la table ASPNETUSERS de DB2 for i. Une nouvelle migration sera nécessaire pour appliquer le changement en base.

Data/AppDbContext.cs

DbContext est la classe qui fait le pont entre votre code C# et la base de données. C'est lui qui sait quelles tables existent, comment convertir vos objets C# en données exploitables par DB2 for i, et comment exécuter les requêtes via NTi.

Au lieu d'hériter du DbContext classique, on hérite d'IdentityDbContext<ApplicationUser>. Ce contexte spécialisé embarque automatiquement tous les DbSet dont Identity a besoin, inutile de les déclarer manuellement :

using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

namespace NtiIdentityDemo.Data
{
    public class AppDbContext : IdentityDbContext
    {
        public AppDbContext(DbContextOptions options) : base(options)
        {
        }
    }
}
DbSet Table créée dans DB2 for i
DbSet<ApplicationUser> ASPNETUSERS
DbSet<IdentityRole> ASPNETROLES
DbSet<IdentityUserRole> ASPNETUSERROLES
DbSet<IdentityUserClaim> ASPNETUSERCLAIMS
DbSet<IdentityUserLogin> ASPNETUSERLOGINS
DbSet<IdentityUserToken> ASPNETUSERTOKENS
DbSet<IdentityRoleClaim> ASPNETROLECLAIMS

Étape 4 - Configurer la chaîne de connexion

Dans appsettings.json, ajoutez votre chaîne de connexion NTi :

{
  "ConnectionStrings": {
    "DefaultConnection": "server=SERVER;user=USER;password=PWD;schema=IDENTITYDB;"
  }
}

Le paramètre schema désigne la bibliothèque DB2 for i dans laquelle les tables Identity seront créées. Si la bibliothèque n'existe pas, EF Core la créera automatiquement lors de la migration. Pour la liste complète des paramètres disponibles, consultez la classe NTiConnection.

💡 Les noms de bibliothèques IBM i sont limités à 10 caractères. Veillez à respecter cette contrainte dans le paramètre schema.


Étape 5 - Générer les composants Identity

Les composants Razor d'Identity (pages de connexion, d'inscription, de gestion du compte...) peuvent être générés automatiquement via le scaffolding Visual Studio.

  1. Faites un clic droit sur le dossier Components de votre projet
  2. Sélectionnez Ajouter > Nouvel élément généré automatiquement > Blazor Identity
  3. Configurez les options :
    • Classe DbContext : sélectionnez AppDbContext
    • Classe utilisateur : sélectionnez ApplicationUser
  4. Cliquez sur Ajouter

Visual Studio génère automatiquement les composants Razor Identity dans le dossier Components/Account/Pages/ :

  • Login.razor - formulaire de connexion
  • Register.razor - formulaire d'inscription
  • Logout.razor - déconnexion
  • Manage/ - gestion du profil, mot de passe, 2FA...

Il génère également plusieurs fichiers utilitaires dans Components/Account/ nécessaires au bon fonctionnement des composants Identity, ainsi qu'un Program.cs mis à jour automatiquement.

💡 Le projet doit compiler sans erreur après le scaffolding. Si ApplicationUser n'est pas reconnu dans certains fichiers générés, vérifiez que le namespace NtiIdentityDemo.Data est bien référencé dans les using concernés.


Étape 6 - Configurer Program.cs

Le scaffolding a généré un Program.cs configuré pour SQLite. Une seule modification est nécessaire : remplacer UseSqlite par UseNTi pour se connecter à la base DB2 for i via NTi Data Provider.

Remplacer la ligne:

builder.Services.AddDbContext(options =>
    options.UseSqlite(connectionString));

Par:

builder.Services.AddDbContext(options =>
    options.UseNTi(connectionString));

Et ajoutez using Aumerial.EntityFrameworkCore; en haut du fichier. Le Program.cs final doit ressembler à :

using Aumerial.EntityFrameworkCore;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using NtiIdentityDemo.Components;
using NtiIdentityDemo.Components.Account;
using NtiIdentityDemo.Data;

var builder = WebApplication.CreateBuilder(args);

var conn = builder.Configuration.GetConnectionString("DefaultConnection");

// Enregistrement du DbContext avec NTi
builder.Services.AddDbContext(options =>
    options.UseNTi(conn));

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();

builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
    .AddIdentityCookies();

builder.Services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores()
    .AddSignInManager()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton, IdentityNoOpEmailSender>();

var app = builder.Build();

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

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

app.MapRazorComponents()
    .AddInteractiveServerRenderMode();
app.MapAdditionalIdentityEndpoints();

app.Run();

AddDbContext avec UseNTi est le seul endroit où NTi intervient.
Tout le reste du Program.cs, c’est du .NET standard généré par le scaffolding. UseNTi remplace simplement UseSqlite pour rediriger le stockage vers DB2 for i.


Étape 7 - Créer et appliquer la migration

C'est ici que les tables Identity vont être créées dans la base de données DB2 for i.

Générez la migration

EF Core analyse AppDbContext et génère les instructions SQL nécessaires :

dotnet ef migrations add InitialIdentity

Un dossier Migrations/ apparaît dans votre projet. Il contient le fichier de migration qui décrit les 7 tables Identity à créer.

Appliquez la migration sur l'IBM i

NTi se connecte à votre IBM i et exécute les instructions :

dotnet ef database update

Les 7 tables Identity apparaissent dedans, ainsi qu'une table EFMIGRATIONSHISTORY qu'EF Core utilise pour suivre les migrations déjà appliquées.

Vous pouvez vérifier le résultat dans ACS (IBM Access Client Solutions) en ouvrant la bibliothèque IDENTITYDB.

Tables ASP.NET Identity dans DB2 for i

💡 Les migrations sont incrémentales. La première migration crée toutes les tables. Les suivantes ajoutent uniquement ce qui a changé. Si vous ajoutez des propriétés à ApplicationUser, une nouvelle migration créera les colonnes correspondantes dans ASPNETUSERS sans toucher aux données existantes.


Étape 8 - Tester l'authentification Identity

Pour valider que tout fonctionne, ajouter @attribute [Authorize] dans Components/Pages/Home.razor pour protéger la page d’accueil:

@page "/"
@attribute [Authorize]

<PageTitle>Home</PageTitle>

<h1>Welcome</h1>
<p>Vous êtes connecté !</p>

💡[Authorize] indique à Blazor que cette page est accessible uniquement aux utilisateurs connectés. Tout utilisateur non authentifié est automatiquement redirigé vers la page de login.

Si [Authorize] n’est pas reconnu, ajoutez ce using dans Components/_Imports.razor:

@using Microsoft.AspNetCore.Authorization

Lancez l'application et suivez les étapes suivantes pour valider le service d'authentification :

  1. Vous êtes automatiquement redirigé vers /Account/Login.
  2. Cliquez sur "Register as a new user" et créez un compte.
  3. Après l'inscription, cliquez sur le lien de confirmation dans l'URL pour valider le compte
  4. Retournez sur /Account/Login et connectez-vous : vous êtes redirigé vers la page d'accueil, l'authentification a fonctionné.

Ouvrez ACS, et consultez la table ASPNETUSERS dans la bibliothèque IDENTITYDB, la ligne correspondant à votre compte est bien présente dans DB2 for i.

Données utilisateur dans AspNetUsers sur DB2 for i

💡 Tous les composants Identity générés par le scaffolding sont entièrement personnalisables. Vous pouvez modifier Login.razor, Register.razor … , et adapter les règles de mot de passe, désactiver la confirmation d'email, ajouter des champs au formulaire d'inscription... Les composants se trouvent dans Components/Account/Pages/ et sont du Razor standard.


Et maintenant ?