Aller au contenu principal

2-7-2 Créer le contexte d'un utilisateur connecté

Au même titre que pour les tests MVC, il doit être possible de faire des requêtes authentifiées. Pour faire ces requêtes, on procède de façon un peu différente.

Au fond, on doit créer un jeton et associer à l'en-tête HTTP Authorization du client HTTP le jeton JWT. Les requêtes subséquentes à l'API seront faites avec cet en-tête et donc elles seront authentifiées si le jeton est valide.

Une méthode semblable aux tests d'intégration MVC sera utilisée. Une fonction CreerClientUtilisateur sera ajoutée à SnowfallApiApplication.

  1. Ajouter la coquille de fonction suivante à WebApiIntegrationTestBase:

    Snowfall.Tests/Web.Api/TestsIntegration/WebApiIntegrationTestBase.cs
    public async Task<HttpClient> CreerClientUtilisateur(string email)
    {

    }
    info

    On accepte en entrée seulement le courriel de l'utilisateur. Notre but ici n'est pas de valider les informations de l'utilisateur, mais seulement de créer un jeton pour l'utilisateur demandé. Ainsi, seulement le courriel est requis.

  2. Récupérer les dépendances nécessaires en provenance du système d'injection de dépendances

    Snowfall.Tests/Web.Api/TestsIntegration/WebApiIntegrationTestBase.cs
    public async Task<HttpClient> CreerClientUtilisateur(string email)
    {
    var configuration = _scope.ServiceProvider.GetRequiredService<IConfiguration>();
    var userManager = _scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
    }
    info
    • IConfiguration permets d'accéder à la configuration (appsettings)
    • UserManager permets de récupérer l'utilisateur de la base de données
  3. Ensuite, récupérons l'utilisateur de la BD en vérifiant qu'il n'est pas null

    Snowfall.Tests/Web.Api/TestsIntegration/WebApiIntegrationTestBase.cs
    public async Task<HttpClient> CreerClientUtilisateur(string email)
    {
    var configuration = _scope.ServiceProvider.GetRequiredService<IConfiguration>();
    var userManager = _scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    ApplicationUser? utilisateur = await userManager.FindByNameAsync(email);

    Assert.NotNull(utilisateur);
    }
  4. Pour la suite, on réutilise le système de création de jetons que nous avons implémenté dans l'API. On pourrait extraire cette logique dans une classe de service afin de ne pas dupliquer le code, mais pour l'instant réutilisons simplement le code.

    Snowfall.Tests/TestsApi/SnowfallApiApplication.cs
    public async Task<HttpClient> CreerClientUtilisateur(string email)
    {
    var configuration = _scope.ServiceProvider.GetRequiredService<IConfiguration>();
    var userManager = _scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    ApplicationUser? utilisateur = await userManager.FindByNameAsync(email);

    Assert.NotNull(utilisateur);

    // La clé secrète est récupérée de la configuration (appsettings)
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSecurityKey"]!));

    // On crée une clé de signature à partir de la clé secrète
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    // La date d'expiration du jeton est configurée en fonction de la durée en jours du jeton
    DateTime expirationDateTime = DateTime.Now.AddDays(Convert.ToInt32(configuration["JwtExpirationJours"]!));

    // Les attributs de l'utilisateur qu'on veut rendre disponible côté client via le jeton
    var claims = new List<Claim>
    {
    new Claim(ClaimTypes.Name, utilisateur.UserName),
    new Claim(ClaimTypes.GivenName, utilisateur.Prenom),
    new Claim(ClaimTypes.Surname, utilisateur.Nom),
    new Claim(JwtRegisteredClaimNames.Email, utilisateur.Email),
    new Claim(JwtRegisteredClaimNames.Sub, utilisateur.Id!),
    };

    var roles = await userManager.GetRolesAsync(utilisateur);
    foreach (var role in roles)
    {
    claims.Add(new Claim(ClaimTypes.Role, role));
    }

    var token = new JwtSecurityToken(
    configuration["JwtIssuer"],
    configuration["JwtAudience"],
    claims,
    expires: expirationDateTime,
    signingCredentials: credentials
    );

    var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
    }
  5. Pour terminer, créons le client, assignons-lui le jeton dans l'en-tête HTTP Authorization et retournons le client.

    Snowfall.Tests/TestsApi/SnowfallApiApplication.cs
    public async Task<HttpClient> CreerClientUtilisateur(string email)
    {
    var configuration = _scope.ServiceProvider.GetRequiredService<IConfiguration>();
    var userManager = _scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    ApplicationUser? utilisateur = await userManager.FindByNameAsync(email);

    Assert.NotNull(utilisateur);

    // La clé secrète est récupérée de la configuration (appsettings)
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSecurityKey"]!));

    // On crée une clé de signature à partir de la clé secrète
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    // La date d'expiration du jeton est configurée en fonction de la durée en jours du jeton
    DateTime expirationDateTime = DateTime.Now.AddDays(Convert.ToInt32(configuration["JwtExpirationJours"]!));

    // Les attributs de l'utilisateur qu'on veut rendre disponible côté client via le jeton
    var claims = new List<Claim>
    {
    new Claim(ClaimTypes.Name, utilisateur.UserName),
    new Claim(ClaimTypes.GivenName, utilisateur.Prenom),
    new Claim(ClaimTypes.Surname, utilisateur.Nom),
    new Claim(JwtRegisteredClaimNames.Email, utilisateur.Email),
    new Claim(JwtRegisteredClaimNames.Sub, utilisateur.Id!),
    };

    var roles = await userManager.GetRolesAsync(utilisateur);
    foreach (var role in roles)
    {
    claims.Add(new Claim(ClaimTypes.Role, role));
    }

    var token = new JwtSecurityToken(
    configuration["JwtIssuer"],
    configuration["JwtAudience"],
    claims,
    expires: expirationDateTime,
    signingCredentials: credentials
    );

    var tokenString = new JwtSecurityTokenHandler().WriteToken(token);

    HttpClient client = this.Application.CreateClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer",
    new JwtSecurityTokenHandler().WriteToken(token));

    return client;
    }
    info

    Le client HttpClient est un singleton et donc, dès qu'on associe une valeur à l'en-tête d'une instance via DefaultRequestHeaders, comme c'est la même instance qui est réutilisée partout, elle persistera partout où le client est utilisé.