Mise en place d'une authentification par Bearer Token dans une application ASP.NET Core

Dans le cadre du développement d'une API, il est toujours bon de sécuriser celle-ci pour éviter que n'importe qui, ne devant pas y accéder, puisse visualiser ou manipuler les données.

Il existe plusieurs mécanismes possibles pour cela et celle que je voudrais vous présenter aujourd'hui est celle de l'authentification par "Bearer Token".

Un "Bearer Token" est un JSON Web Token dont le rôle est d'indiquer que l'utilisateur qui accède aux ressources est bien authentifié. Il doit donc commencer par récupérer le token puis l'envoyer au serveur, à chaque requête, pour que celui-ci le valide (en fonction de différentes règles: validation de la clé, validation de l'audience, etc.)

 La mise en place d'une telle sécurité se trouve être extrêmement simple avec ASP.NET Core. En effet, lors de la connexion d'un utilisateur, il faut commencer par créer et renvoyer le token qui aura été généré:

[HttpPost]
[Route("login")]
public ActionResult LoginAsync([FromBody] LoginInfo model)
{
    var result = model.Email == "test@test.fr" && model.Password == "password";
    if (result)
    {
        return new OkObjectResult(GenerateJwtToken(model.Email));
    }

    return NotFound();
}
        
private string GenerateJwtToken(string email)
{
    var claims = new List<Claim>
    {
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),
        new Claim(ClaimTypes.Email, email)
    };

    var keyByteArray = Encoding.ASCII.GetBytes(Constants.SecretKey);
    var signinKey = new SymmetricSecurityKey(keyByteArray);

    var expires = DateTime.Now.AddDays(1);

    var token = new JwtSecurityToken(
        issuer: Constants.Issuer,
        audience: Constants.Audience,
        claims: claims,
        expires: expires,
        signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256)
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

Bien sûr, dans une application de production, on préfèrera utiliser ASP.NET Core Identity (ou un autre mécanisme) pour la création/validation des comptes utilisateurs.

Comme vous pouvez le voir, le code précédent se contente de générer un token (à partir d'un issuer, d'une audience) et de le rajouter des claims et une date d'expiration.

Il reste à présent à rendre nos APIs sécurisées en utilisant l'attribut [Authorize]:

[Authorize]
[Route("api/[controller]")]
public class ValuesController : Controller

Cet attribut permet d'indiquer que l'accès à ce controller (et donc les méthodes qui le composent) ne peut se faire que si l'utilisateur est authentifié. Oui mais voilà, nous n'avons, pour le moment, pas encore indiqué comment cette authentification doit se faire ! 

Et bien tout se passe dans le Startup.cs:

services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    var keyByteArray = Encoding.ASCII.GetBytes(Constants.SecretKey);
    var signinKey = new SymmetricSecurityKey(keyByteArray);

#if DEBUG
    options.RequireHttpsMetadata = false;
#else
    options.RequireHttpsMetadata = true;
#endif

    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ClockSkew = TimeSpan.Zero,

        ValidateAudience = true,
        ValidAudience = Constants.Audience,

        ValidateIssuer = true,
        ValidIssuer = Constants.Issuer,

        ValidateLifetime = true,

        ValidateIssuerSigningKey = true,
        IssuerSigningKey = signinKey
    };
});

C'est ici que toute la "magie" opère! En effet, on indique que l'on souhaite ajouter l'authentification par les Bearer Token et l'on configure l'ensemble des éléments / règles de gestions utilisées pour valider les tokens. A noter que la clé de signature doit être la même que la clé de génération des tokens sinon, il vous sera impossible de valider la signature du jeton.

Enfin, on indique que l'on va utiliser l'authentification:

app.UseAuthentication();

Et le tour est joué! A présent, je peux faire une première requête pour obtenir mon token:

GetToken.png

Il ne me reste plus qu'à passer ce token dans les headers des autres requêtes que je fait à l'API pour récupérer les données:

Results.png

Le token ayant une durée de vie (que vous spécifiez), il reste bien sûr à gerer le cas d'un renouvellement de jeton (cela peut se faire via un refresh_token par exemple) mais cela vous donne un bon aperçu de ce qu'il est possible de faire ;)

 

Happy coding! :)

 

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus