IdentityServer4 : Création et configuration du Client pour accéder à une Web API ASP.NET Core sécurisée

Le , par Hinault Romaric, Responsable .NET
IdentityServer est une solution open source .NET de gestion d’identité et de contrôle d’accès. Il repose sur les protocoles OpenID Connect et OAuth 2.0.

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.


Dans le premier billet de blog que j'ai rédigé sur le sujet, j'ai présenté comment mettre en place un serveur de gestion sécurisée de jetons (STS – Secure Token Service) en utilisant IdentityServer4. Dans le deuxième billet, nous avons vu comment sécuriser l’accès à une Web API ASP.NET Core en utilisant notre serveur Identityserver4.

Dans ce billet, nous allons créer le client qui sera une application Console .NET Core. Nous allons écrire le code nécessaire pour permettre à ce denier de demander un jeton de sécurité à l’application IdentityServer, ensuite utiliser ce dernier pour s’authentifier auprès de l’API et accéder à ses fonctionnalités.

1. Création du client

Pour commencer, nous allons créer une nouvelle application console .NET Core 2.x :





Ensuite, nous devons ajouter le package Newtonsoft.Json à cette dernière en utilisant le gestionnaire de packages NuGet.



Une fois cela fait, nous allons éditer le fichier Program.cs et ajouter le code nécessaire pour appeler notre API. Le code complet de la classe Program 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
class Program 
    { 
        static void Main(string[] args) => CallWebApi().GetAwaiter().GetResult(); 
  
        static async Task CallWebApi() 
  
        { 
            var client = new HttpClient(); 
  
            var response = await client.GetAsync("https://localhost:5003/api/secure"); 
            if (!response.IsSuccessStatusCode) 
            { 
                Console.WriteLine(response.StatusCode); 
            } 
            else 
            { 
                var content = await response.Content.ReadAsStringAsync(); 
                Console.WriteLine(JArray.Parse(content)); 
            } 
        } 
    }

Enregistrez et exécutez votre application.

Vous aurez le message suivant à l’écran :



L’accès à la ressource pour notre application a été refusé. Nous allons maintenant configurer cette dernière pour qu’elle demande un jeton d’authentification à IdentityServer et qu’elle utilise ce dernier pour accéder à l’API.

2. Configuration du client

Pour accéder facilement à IdentityServer dans notre client, nous allons utiliser la librairie IdentityModel. La première chose à faire sera donc l’ajout du package correspondant à notre application en utilisant NuGet :



Nous allons utiliser la méthode GetAsync() de la classe DiscoveryClient pour récupérer les métadonnées exposées par le EndPoint de l’application IdentityServer. Cette méthode prend en paramètre l’URL de notre application IdentityServer. Nous devons nous assurer que le EndPoint est accessible avant de continuer :

Code c# : Sélectionner tout
1
2
3
4
5
6
var disco = await DiscoveryClient.GetAsync("https://localhost:5001"); 
            if (disco.IsError) 
            { 
                Console.WriteLine(disco.Error); 
                return; 
            }

Ensuite, nous devons initialiser un nouvel objet TokenClient, en lui passant en paramètre le TokenEndpoint, l’ID du client et le secret.
Nous allons utiliser la méthode RequestClientCredentialsAsync pour demander un jeton d’authentification pour accéder à l’API. Cette méthode prend en paramètre le nom de l’API auquel on veut accéder, tel qu’il est répertorié auprès de IdentityServer. Nous devons nous assurer que le jeton a été obtenu avant de continuer :

Code c# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
// request token 
            var tokenClient = new TokenClient(disco.TokenEndpoint, "consoleappclient", "secret"); 
            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("testapi"); 
  
            if (tokenResponse.IsError) 
            { 
                Console.WriteLine(tokenResponse.Error); 
                return; 
            } 
  
            Console.WriteLine(tokenResponse.Json);

Nous allons pour finir utiliser la méthode SetBearerToken de HttpClient() pour inscrire le jeton dans l’entête HTTP de notre requête :

Code c# : Sélectionner tout
1
2
  var client = new HttpClient(); 
            client.SetBearerToken(tokenResponse.AccessToken);

Le code complet de la méthode CallWebApi devient ceci :

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
static async Task CallWebApi() 
  
        { 
            // discover endpoints from metadata 
            var disco = await DiscoveryClient.GetAsync("https://localhost:5001"); 
            if (disco.IsError) 
            { 
                Console.WriteLine(disco.Error); 
                return; 
            } 
  
            // request token 
            var tokenClient = new TokenClient(disco.TokenEndpoint, "consoleappclient", "secret"); 
            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("testapi"); 
  
            if (tokenResponse.IsError) 
            { 
                Console.WriteLine(tokenResponse.Error); 
                return; 
            } 
  
            Console.WriteLine(tokenResponse.Json); 
  
            // call api 
            var client = new HttpClient(); 
            client.SetBearerToken(tokenResponse.AccessToken); 
  
            var response = await client.GetAsync("https://localhost:5003/api/secure"); 
            if (!response.IsSuccessStatusCode) 
            { 
                Console.WriteLine(response.StatusCode); 
            } 
            else 
            { 
                var content = await response.Content.ReadAsStringAsync(); 
                Console.WriteLine(JArray.Parse(content)); 
            } 
        }

3. Mise à jour de IdentityServer pour reconnaître le client

Toute la configuration nécessaire pour accéder à l’API a été effectuée côté client. Toutefois, si ce dernier essaye d’accéder à la ressource, il n’aura toujours pas le droit. Cela est dû au fait qu’il n’est pas encore connu par IdentityServer. Nous devons donc enregistrer ce dernier et définir à quoi il a accès.

Pour cela, nous devons éditer le fichier Config.cs et ajouter un nouveau client à la liste des clients. Nous devons lui donner le même nom et le même secret que nous avons passé en paramètre en initialisant le tokenclient dans l’application console :

Code c# : Sélectionner tout
var tokenClient = new TokenClient(disco.TokenEndpoint, "consoleappclient", "secret");

Le code de la méthode GetClients de la classe Config devrait donc ressembler à ceci :

Code c# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  public static IEnumerable<Client> GetClients() 
        { 
            return new List<Client> 
            { 
  
                new Client 
                { 
                    ClientId = "consoleappclient", 
                    AllowedGrantTypes = GrantTypes.ClientCredentials, 
  
                    ClientSecrets = 
                    { 
                        new Secret("secret".Sha256()) 
                    }, 
                    AllowedScopes = { "testapi" } 
                } 
  
           }; 
        }

Vous remarquerez que nous avons également défini la ressource à laquelle le client doit accéder.

Enregistrez les modifications. Exécutez l’application IdentityServer, l’API et enfin l’application console.

Vous aurez le résultat suivant :



Le jeton qui est généré par IdentityServer et utilisé par le client pour accéder à la ressource est au format JWT. Il s’agit d’un jeton sécurisé qui contient toutes les informations nécessaires pour confirmer l’identité du client et lui donner accès à la ressource demandée. Si vous décodez le jeton avec https://jwt.io, vous obtiendrez ce qui suit :



Nous venons d’accéder à notre ressource sécurisée en utilisant un jeton de sécurité provenant d’IdentityServer.

Dans la prochain billet sur IdentityServer, nous verrons comment mettre en place l’authentification pour une application ASP.NET Core MVC en utilisant OpenID Connect.

Restez connecté !


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :
Responsables bénévoles de la rubrique Microsoft DotNET : Hinault Romaric - François DORIN -