Authentication and Authorization in ASP.NET Web API (Authentification et autorisation dans l’API Web ASP.NET)

par Rick Anderson

Vous avez créé une API web, mais vous souhaitez maintenant contrôler l’accès à celle-ci. Dans cette série d’articles, nous allons examiner certaines options permettant de sécuriser une API web auprès d’utilisateurs non autorisés. Cette série couvre à la fois l’authentification et l’autorisation.

  • L’authentification consiste à connaître l’identité de l’utilisateur. Par exemple, Alice se connecte avec son nom d’utilisateur et son mot de passe, et le serveur utilise le mot de passe pour authentifier Alice.
  • L’autorisation détermine si un utilisateur est autorisé à effectuer une action. Par exemple, Alice a l’autorisation d’obtenir une ressource, mais pas de créer une ressource.

Le premier article de la série donne une vue d’ensemble de l’authentification et de l’autorisation dans API Web ASP.NET. D’autres rubriques décrivent les scénarios d’authentification courants pour l’API web.

Notes

Merci aux personnes qui ont passé en revue cette série et fourni des commentaires précieux : Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.

Authentification

L’API web suppose que l’authentification se produit dans l’hôte. Pour l’hébergement web, l’hôte est IIS, qui utilise des modules HTTP pour l’authentification. Vous pouvez configurer votre projet pour utiliser l’un des modules d’authentification intégrés à IIS ou ASP.NET, ou écrire votre propre module HTTP pour effectuer une authentification personnalisée.

Lorsque l’hôte authentifie l’utilisateur, il crée un principal, qui est un objet IPrincipal qui représente le contexte de sécurité sous lequel le code s’exécute. L’hôte attache le principal au thread actuel en définissant Thread.CurrentPrincipal. Le principal contient un objet Identity associé qui contient des informations sur l’utilisateur. Si l’utilisateur est authentifié, la propriété Identity.IsAuthenticated retourne true. Pour les requêtes anonymes, IsAuthenticated retourne false. Pour plus d’informations sur les principaux, consultez Sécurité basée sur le rôle.

Gestionnaires de messages HTTP pour l’authentification

Au lieu d’utiliser l’hôte pour l’authentification, vous pouvez placer une logique d’authentification dans un gestionnaire de messages HTTP. Dans ce cas, le gestionnaire de messages examine la requête HTTP et définit le principal.

Quand devez-vous utiliser des gestionnaires de messages pour l’authentification ? Voici quelques compromis :

  • Un module HTTP voit toutes les requêtes qui passent par le pipeline ASP.NET. Un gestionnaire de messages voit uniquement les requêtes qui sont routées vers l’API web.
  • Vous pouvez définir des gestionnaires de messages par itinéraire, ce qui vous permet d’appliquer un schéma d’authentification à un itinéraire spécifique.
  • Les modules HTTP sont spécifiques à IIS. Les gestionnaires de messages étant indépendants de l’hôte, ils peuvent être utilisés à la fois avec l’hébergement web et l’auto-hébergement.
  • Les modules HTTP participent à la journalisation IIS, à l’audit, etc.
  • Les modules HTTP s’exécutent plus tôt dans le pipeline. Si vous gérez l’authentification dans un gestionnaire de messages, le principal n’est pas défini tant que le gestionnaire n’est pas exécuté. En outre, le principal revient au principal précédent lorsque la réponse quitte le gestionnaire de messages.

En règle générale, si vous n’avez pas besoin de prendre en charge l’auto-hébergement, un module HTTP est une meilleure option. Si vous devez prendre en charge l’auto-hébergement, envisagez un gestionnaire de messages.

Définition du principal

Si votre application effectue une logique d’authentification personnalisée, vous devez définir le principal à deux emplacements :

  • Thread.CurrentPrincipal. Cette propriété est la méthode standard pour définir le principal du thread dans .NET.
  • HttpContext.Current.User. Cette propriété est spécifique à ASP.NET.

Le code suivant montre comment définir le principal :

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Pour l’hébergement web, vous devez définir le principal aux deux emplacements ; sinon, le contexte de sécurité peut devenir incohérent. Toutefois, pour l’auto-hébergement, HttpContext.Current a la valeur Null. Pour vous assurer que votre code est indépendant de l’hôte, case activée pour null avant d’affecter à HttpContext.Current, comme indiqué.

Autorisation

L’autorisation se produit plus tard dans le pipeline, plus près du contrôleur. Cela vous permet de faire des choix plus précis lorsque vous accordez l’accès aux ressources.

  • Les filtres d’autorisation s’exécutent avant l’action du contrôleur. Si la demande n’est pas autorisée, le filtre retourne une réponse d’erreur et l’action n’est pas appelée.
  • Dans une action de contrôleur, vous pouvez obtenir le principal actuel à partir de la propriété ApiController.User . Par exemple, vous pouvez filtrer une liste de ressources en fonction du nom d’utilisateur, renvoyant uniquement les ressources qui appartiennent à cet utilisateur.

Diagramme du pipeline d’authentification et d’autorisation.

Utilisation de l’attribut [Autoriser]

L’API web fournit un filtre d’autorisation intégré, AuthorizeAttribute. Ce filtre vérifie si l’utilisateur est authentifié. Si ce n’est pas le cas, il retourne le code HTTP status 401 (Non autorisé), sans appeler l’action.

Vous pouvez appliquer le filtre globalement, au niveau du contrôleur ou au niveau des actions individuelles.

Globalement : pour restreindre l’accès pour chaque contrôleur d’API web, ajoutez le filtre AuthorizeAttribute à la liste de filtres globale :

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

Contrôleur : pour restreindre l’accès pour un contrôleur spécifique, ajoutez le filtre en tant qu’attribut au contrôleur :

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

Action : pour restreindre l’accès à des actions spécifiques, ajoutez l’attribut à la méthode d’action :

public class ValuesController : ApiController
{
    public HttpResponseMessage Get() { ... }

    // Require authorization for a specific action.
    [Authorize]
    public HttpResponseMessage Post() { ... }
}

Vous pouvez également restreindre le contrôleur, puis autoriser l’accès anonyme à des actions spécifiques à l’aide de l’attribut [AllowAnonymous] . Dans l’exemple suivant, la Post méthode est restreinte, mais la méthode autorise l’accès Get anonyme.

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

Dans les exemples précédents, le filtre permet à tout utilisateur authentifié d’accéder aux méthodes restreintes ; seuls les utilisateurs anonymes sont tenus à l’extérieur. Vous pouvez également limiter l’accès à des utilisateurs spécifiques ou à des utilisateurs dans des rôles spécifiques :

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
   
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

Notes

Le filtre AuthorizeAttribute pour les contrôleurs d’API web se trouve dans l’espace de noms System.Web.Http . Il existe un filtre similaire pour les contrôleurs MVC dans l’espace de noms System.Web.Mvc , qui n’est pas compatible avec les contrôleurs d’API web.

Filtres d’autorisation personnalisés

Pour écrire un filtre d’autorisation personnalisé, dérivez de l’un des types suivants :

  • AuthorizeAttribute. Étendez cette classe pour effectuer une logique d’autorisation en fonction de l’utilisateur actuel et des rôles de l’utilisateur.
  • AuthorizationFilterAttribute. Étendez cette classe pour effectuer une logique d’autorisation synchrone qui n’est pas nécessairement basée sur l’utilisateur ou le rôle actuel.
  • IAuthorizationFilter. Implémentez cette interface pour effectuer une logique d’autorisation asynchrone ; par exemple, si votre logique d’autorisation effectue des appels d’E/S ou réseau asynchrones. (Si votre logique d’autorisation est liée au processeur, il est plus simple de dériver d’AuthorizationFilterAttribute, car vous n’avez pas besoin d’écrire une méthode asynchrone.)

Le diagramme suivant montre la hiérarchie de classes pour la classe AuthorizeAttribute .

Diagramme de la hiérarchie de classes pour la classe Authorize Attribute.

Diagramme de la hiérarchie de classes pour la classe Authorize Attribute. L’attribut d’autorisation se trouve en bas, avec une flèche pointant vers le haut vers l’attribut de filtre d’autorisation et une flèche pointant vers le haut vers le filtre d’autorisation I en haut.

Autorisation à l’intérieur d’une action de contrôleur

Dans certains cas, vous pouvez autoriser une requête à continuer, mais modifier le comportement en fonction du principal. Par exemple, les informations que vous retournez peuvent changer en fonction du rôle de l’utilisateur. Dans une méthode de contrôleur, vous pouvez obtenir le principal actuel à partir de la propriété ApiController.User .

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}