API web protégée : Vérifier les étendues et les rôles d’application

Cet article décrit comment ajouter une autorisation à votre API web. Cette protection garantit que l’API est appelée uniquement par :

  • Des applications pour le compte d’utilisateurs qui ont des étendues et des rôles adaptés.
  • Des applications de démon qui ont les rôles d’application appropriés.

Les extraits de code de cet article proviennent des exemples de code suivants sur GitHub :

Pour protéger une API web ASP.NET ou ASP.NET Core, vous devez ajouter l’attribut [Authorize] à l’un des éléments suivants :

  • Le contrôleur proprement dit, si vous voulez que toutes les actions de contrôleur soient protégées
  • L’action de contrôleur individuelle pour votre API
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

Mais cette protection n’est pas suffisante. Elle garantit uniquement qu’ASP.NET et ASP.NET Core valident le jeton. Votre API doit vérifier que le jeton utilisé pour appeler l’API est demandée avec les revendications attendues. Ces revendications doivent notamment être vérifiées :

  • Les étendues si l’API est appelée pour le compte d’un utilisateur.
  • Les rôles d’application si l’API peut être appelée à partir d’une application démon.

Vérifier les étendues dans les API appelées pour le compte d’utilisateurs

Si une application cliente appelle votre API pour le compte d’un utilisateur, l’API doit demander un jeton du porteur ayant des étendues spécifiques pour elle. Pour plus d’informations, consultez Configuration de code | Jeton du porteur.

Dans ASP.NET Core, vous pouvez utiliser Microsoft.Identity.Web pour vérifier les étendues de chaque action du contrôleur. Vous pouvez également les vérifier au niveau du contrôleur ou de l’ensemble de l’application.

Vérifier les étendues de chaque action de contrôleur

Vous pouvez vérifier les étendues de l’action du contrôleur à l’aide de l'attribut [RequiredScope]. Cet attribut comporte plusieurs remplacements. Un qui prend directement les étendues requises, et une qui prend une clé de la configuration.

Vérifier les étendues d’une action de contrôleur avec des étendues codées en dur

L’extrait de code suivant montre l’utilisation de l'attribut [RequiredScope] avec des étendues codées en dur.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Vérifier les étendues d’une action de contrôleur avec des étendues définies dans la configuration

Vous pouvez également déclarer ces étendues requises dans la configuration et faire référence à la clé de configuration :

Par exemple, si vous avez la configuration suivante dans appsettings.json :

{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

Ensuite, référencez-la dans l’attribut [RequiredScope] :

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Vérifier les étendues de manière conditionnelle

Dans certains cas, vous souhaiterez vérifier les étendues de manière conditionnelle. Pour ce faire, utilisez la méthode d'extension VerifyUserHasAnyAcceptedScope sur le HttpContext.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

Vérifier les étendues au niveau du contrôleur

Vous pouvez également vérifier les étendues pour l’ensemble du contrôleur.

Vérifier les étendues sur un contrôleur avec des étendues codées en dur

L’extrait de code suivant montre l’utilisation de l'attribut [RequiredScope] avec des étendues codées en dur sur le contrôleur. Pour utiliser RequiredScopeAttribute, vous devez :

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Vérifier les étendues sur le contrôleur avec des étendues définies dans la configuration

Comme pour une action, vous pouvez également déclarer ces étendues requises dans la configuration et faire référence à la clé de configuration :

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

Vérifier les étendues plus globalement

La définition d’étendues précises pour votre API web et la vérification des étendues dans chaque action de contrôleur est l’approche recommandée. Toutefois, il est également possible de vérifier les étendues au niveau de l’application ou d’un contrôleur. Pour plus d’informations, consultez Autorisation basée sur les revendications dans la documentation d’ASP.NET Core.

Qu’est-ce qui est vérifié ?

L’attribut [RequiredScope] et la méthode VerifyUserHasAnyAcceptedScope effectuent des étapes semblables aux suivantes :

  • Vérifier qu’il existe une revendication nommée http://schemas.microsoft.com/identity/claims/scope ou scp.
  • Vérifier que la revendication a une valeur qui contient l’étendue attendue par l’API.

Vérifier les rôles d’application dans les API appelées par des applications démon

Si votre API web est appelée par une application de démon, cette application doit exiger une autorisation d’application à votre API web. Comme indiqué dans Exposition des autorisations d’application (rôles d’application), votre API expose de telles autorisations. Le rôle d’application access_as_application en est un exemple.

Vous devez maintenant demander à votre API de vérifier que le jeton qu’elle reçoit contient la revendication roles et que cette revendication a la valeur attendue. Le code de vérification est similaire au code qui vérifie les autorisations déléguées, excepté que votre action de contrôleur teste les rôles au lieu des étendues :

L’extrait de code suivant montre comment vérifier le rôle d’application ;

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

À la place, vous pouvez utiliser les attributs [Authorize(Roles = "access_as_application")] sur le contrôleur ou une action (ou une page razor).

[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

L’autorisation basée sur les rôles dans ASP.NET Core liste plusieurs approches pour implémenter l’autorisation basée sur les rôles. Les développeurs peuvent en choisir une parmi celles qui conviennent à leurs scénarios respectifs.

Pour des exemples qui fonctionnent, consultez le tutoriel incrémentiel d’application web sur l’autorisation par rôles et groupes.

Vérifier les rôles d’application dans les API appelées pour les utilisateurs

Les utilisateurs peuvent également utiliser des revendications de rôles dans des modèles d’affectation d’utilisateurs, comme indiqué dans Guide pratique pour ajouter des rôles d’application dans votre application et les recevoir dans le jeton. Si les rôles peuvent être attribués aux deux, vérifier les rôles permet aux applications de se connecter en tant qu’utilisateurs et aux utilisateurs de se connecter en tant qu’applications. Nous vous recommandons de déclarer des rôles différents pour les utilisateurs et les applications afin d’éviter cette confusion.

Si vous avez défini des rôles d’application avec un utilisateur ou un groupe, la revendication de rôles peut également être vérifiée dans l’API avec des étendues. La logique de vérification des rôles d’application dans ce scénario reste la même que si l’API est appelée par les applications démon, car il n’existe aucune différenciation dans la revendication de rôle pour l’utilisateur/groupe et l’application.

Acceptation de jetons d’application uniquement si l’API web doit être appelée uniquement par des applications de démon

Si vous voulez que seules les applications démon appellent votre API web, ajoutez la condition indiquant que le jeton est un jeton d’application uniquement quand vous validez le rôle d’application.

string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

La vérification de la condition inverse autorise uniquement les applications qui connectent un utilisateur à appeler votre API.

Utilisation de l’autorisation basée sur les listes de contrôle d’accès (ACL)

En guise d’alternative à l’autorisation basée sur les rôles d’application, vous pouvez protéger votre API web avec un modèle d’autorisation basé sur les listes de contrôle d’accès (ACL) afin de contrôler les jetons sans la revendication roles.

Si vous utilisez Microsoft.Identity.Web sur ASP.NET Core, vous devez déclarer que vous utilisez une autorisation basée sur les listes de contrôle d’accès (ACL). Dans le cas contraire, Microsoft Identity Web lèvera une exception lorsque ni les rôles ni les étendues ne se trouvent dans les revendications fournies :

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

Pour éviter cette exception, définissez la propriété de configuration AllowWebApiToBeAuthorizedByACL à true dans appsettings.json ou par programmation.

{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

Si vous définissez AllowWebApiToBeAuthorizedByACL à true, il est de votre responsabilité de vérifier le mécanisme d’ACL.

Étapes suivantes

  • Découvrez comment générer une application web ASP.NET Core qui permet de connecter des utilisateurs, en consultant une série de tutoriels en plusieurs parties

  • Explorer les exemples d’API web de la plateforme d’identités Microsoft