IdentityServer peut être utilisé par les entreprises pour mettre en place une solution pour :
- la protection de leurs ressources ;
- l’authentification des utilisateurs via une base de données ou des fournisseurs externes d’identité (Microsoft, Google, Facebook, etc.) ;
- la gestion des sessions et la fédération (single sign-on) ;
- la génération des jetons pour les clients ;
- la validation des jetons et bien plus.
Ce billet est le cinquième que j’écris sur le sujet. Les billets précédents ont porté sur les points suivants :
Mise en place d’un STS avec IdentityServer4 pour sécuriser ses applications .NET
Sécurisation d’une Web API ASP.NET Core avec le STS IdentityServer4
IdentityServer4 : création et configuration du Client pour accéder à une Web API ASP.NET Core sécurisée
IdentityServer4 : Authentification d’un utilisateur avec OpenID Connect
Dans le dernier billet de blog, nous avons configuré IdentityServer4 pour offrir l'authentification avec OpenID Connect. Nous allons maintenant créer une nouvelle application ASP.NET Core MVC. L’utilisateur pour accéder à une page sécurisée de cette application, devra au préalable s’authentifier. Et l’authentification sera gérée par notre solution d’authentification centralisée.
Vous allez donc ajouter une nouvelle application ASP.NET Core MVC à votre solution :
Vous devez modifier l’application pour utiliser les ports 5004 et 5005 respectivement en HTTP et en HTTPS :
Configuration de l’authentification OpenID Connect
Nous allons configurer notre application pour utiliser l’authentification OpenID Connect, car c’est ce qui est supporté par notre serveur d’authentification IdentityServer.
Pour cela, nous allons éditer le fichier Startup.cs et ajouter les lignes de code suivantes dans la méthode ConfigureServices :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "https://localhost:5001"; options.RequireHttpsMetadata = false; options.ClientId = "mvcappclient"; options.SaveTokens = true; }); |
La méthode AddAuthentication() va permettre d’enregistrer auprès du conteneur d’IoC le service d’authentification en utilisant « Cookies » par défaut. Nous avons besoin de définir DefaultChallengeScheme à « oidc » parce que nous utilisons OpenID Connect.
L’extension AddCookie va permettra d’ajouter le handler pour la gestion des cookies.
L’extension AddOpenIdConnect, quant à elle, va permettre de configurer le handler pour utiliser le protocole OpenID Connect. Nous devons spécifier l’URL de l’application IdentityServer (Authority) et l’ID du client (ClientID). Nous devons renseigner le même ID Client dans IdentityServer. Par ailleurs, SaveTokens est utilisé pour assurer la persistance du token retourné par IdentityServer dans le cookie et SignInScheme permet d’utiliser le handler de gestion de cookies pour générer un cookie lorsque le processus OpenID Connect est complété.
Le code complet de cette méthode ConfigureServices est le suivant :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "https://localhost:5001"; options.RequireHttpsMetadata = false; options.ClientId = "mvcappclient"; options.SaveTokens = true; }); } |
Pour nous assurer que les services de gestion d’authentification sont exécutés à chaque requête, nous allons ajouter le middleware pour l’authentification dans le pipeline HTTP de ASP.NET Core.
Cela se fait en ajoutant UseAuthentication dans la méthode Configure du fichier Startup :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } |
Le middleware d’authentification doit être ajouté avant le middleware MVC (UseMVC) et de gestion des fichiers statiques (UseStaticFiles).
Ajout d’une page sécurisée
Maintenant, nous allons ajouter une page dont l’accès est sécurisé. Dans le HomeController, ajoutez une nouvelle méthode d’action Secure, avec le code suivant :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 | [Authorize] public IActionResult Secure() { ViewData["Message"] = "Secure page."; return View(); } |
Elle doit être décorée avec l’attribut [Autorize]. C’est cet attribut qui permet au système d’authentification de ASP.NET de savoir que l’accès à cette ressource est sécurisé et que l’utilisateur doit obtenir au préalable les droits nécessaires.
Faites un clic droit sur cette méthode, puis sélectionnez ajouter une vue. Remplacez le code de cette vue par ce qui suit :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | @{ ViewData["Title"] = "Secure"; } <h2>@ViewData["Title"]</h2> <h3>User claims</h3> <dl> @foreach (var claim in User.Claims) { <dt>@claim.Type</dt> <dd>@claim.Value</dd> } </dl> |
Enregistrez et exécutez votre application, ainsi que l’application IdentityServer en utilisant l’invite de commande et la commande Dotnet Run. Si vous entrez l’URL suivante https://localhost:5005/Home/Secure dans votre navigateur, vous serez redirigé vers la page suivante :
IdentityServer ne reconnait pas notre client. Celui-ci doit faire partie de sa liste des clients.
Enregistrement du client MVC dans IdentityServer
Vous allez éditer le fichier Config.cs et ajouter notre application dans la liste des clients :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "consoleappclient", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "testapi" } }, // OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvcappclient", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "https://localhost:5005/signin-oidc" }, PostLogoutRedirectUris = { "https://localhost:5005/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } } ; } |
L’ID doit être le même que celui spécifié lors de la configuration de l’authentification côté client. Le « Grant Type » doit être à Implicit. Il s’agit du mode le plus optimisé pour les applications Web. Ce mode permet le transfert de tous les jetons via le navigateur.
AllowedScopes permet de définir les informations qui doivent être partagées avec le client. Nous voulons que les données de profil de l’utilisateur puissent être partagées et que la transaction se fasse via le protocole OpenID.
Une fois cela fait, exécutez de nouveau vos applications et accédez à la page sécurisée (https://localhost:5005/Home/Secure). Vous serez maintenant redirigé vers la page de connexion :
Une fois connecté, une page de consentement s’affichera afin d’obtenir l’approbation de l’utilisateur avant de partager ses données de profil avec l’application MVC :
Par défaut le consentement est à « True ». Si vous ne voulez pas que le consentement de l’utilisateur soit demandé au préalable, vous devez mettre la propriété RequireConsent à False lors de l’enregistrement du client :
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | new Client { ClientId = "mvcappclient", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, RedirectUris = { "https://localhost:5005/signin-oidc" }, PostLogoutRedirectUris = { "https://localhost:5005/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } |
Lorsque vous cliquez sur « Yes, Allow », vous êtes redirigé vers la page à laquelle vous vouliez accéder :
5-3 Déconnexion
La déconnexion auprès de IdentityServer est très simple. Il suffit de supprimer les cookies d’authentification. Pour mettre en place la déconnexion, vous devez ajouter la méthode d’action suivante côté client :
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
Si vous exécutez votre application et essayez de vous déconnecter, vous allez vous rendre compte que vous allez être automatiquement redirigé vers la page de confirmation de déconnexion de l’application IdentityServer (cette page est déjà mise en place dans le modèle Quickstart que nous avons intégré dans notre application).
En cliquant sur le lien affiché, vous êtes redirigé vers la page d’accueil du client MVC. Vous devez avoir au préalable défini l’URL de retour. Nous l’avons fait lors de l’enregistrement du client.
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvcappclient", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, RedirectUris = { "https://localhost:5005/signin-oidc" }, PostLogoutRedirectUris = { "https://localhost:5005/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } |
Pour que la redirection soit automatique, vous devez simplement éditer le fichier AccountOptions.cs qui se trouve dans le dossier Quickstart/Account et mettre à true le champ
AutomaticRedirectAfterSignOut.
Code c# : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = true; // specify the Windows authentication scheme being used public static readonly string WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme; // if user uses windows auth, should we load the groups from windows public static bool IncludeWindowsGroups = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } |
Désormais, nous avons une application cliente MVC, capable d'utiliser IdentityServer pour authentifier ses utilisateurs avant que ces derniers puissent accéder à des pages sécurisées. Dans le prochain billet, nous verrons comment permettre au client MVC de consommer la WebAPI ASP.NET sécurisée.