Utiliser les champs d'application OAuth2

Vous consultez la documentation d'Apigee Edge.
Consultez la documentation Apigee X.
en savoir plus

Cet article explique comment utiliser les champs d'application OAuth 2.0 sur Apigee Edge.

Qu'est-ce qu'un champ d'application OAuth 2.0 ?

Les champs d'application OAuth 2.0 permettent de limiter la quantité d'accès accordée à un jeton d'accès. Par exemple, un jeton d'accès attribué à une application cliente peut se voir accorder un accès en lecture et en écriture aux ressources protégées, ou simplement un accès en lecture. Vous pouvez mettre en œuvre vos API pour appliquer le champ d'application ou la combinaison de champs d'application de votre choix. Ainsi, si un client reçoit un jeton avec champ d'application en lecture et tente d'appeler un point de terminaison d'API nécessitant un accès en écriture, l'appel échoue.

Dans cet article, nous verrons comment les champs d'application sont attribués aux jetons d'accès et comment Apigee Edge applique les champs OAuth 2.0. Après avoir lu cet article, vous pourrez utiliser des champs d'application en toute confiance.

Comment les champs d'application sont-ils associés aux jetons d'accès ?

Lorsqu'Edge génère un jeton d'accès, il peut attribuer un champ d'application à ce jeton. Pour comprendre comment cela se passe, vous devez d'abord connaître les entités Apigee Edge suivantes : les produits d'API, les développeurs et les applications de développeur. Pour obtenir une présentation, consultez la page Présentation du processus de publication. Nous vous recommandons de consulter ce document avant de continuer si vous en avez besoin.

Un jeton d'accès est une longue chaîne de caractères d'apparence aléatoire qui permet à Edge de vérifier les requêtes API entrantes (un peu comme les identifiants nom d'utilisateur/mot de passe classiques). Techniquement, le jeton est une clé qui renvoie à une collection de métadonnées de ce type :

{
  "issued_at" : "1416962591727",
  "application_name" : "0d3e1d41-a59f-4d74-957e-d4e3275d4781",
  "scope" : "A",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-bs0cSuqS9y]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-AdBmANhsag@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "eTtB7w5lvk3DnOZNGReBlvGvIAeAywun",
  "access_token" : "ODm47ris5AlEty8TDc1itwYPe5MW",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

Les métadonnées du jeton comprennent la chaîne du jeton d'accès, les informations d'expiration, l'identification de l'application de développeur, du développeur et des produits associés au jeton. Vous remarquerez également que les métadonnées incluent aussi le "champ d'application".

Comment le jeton obtient-t-il son champ d'application ?

La première clé pour comprendre le champ d'application est de garder à l'esprit que chaque produit d'une application de développeur peut avoir zéro, un ou plusieurs champs d'application qui lui sont attribués. Ces champs d'application peuvent être attribués lors de la création du produit, ou peuvent être ajoutés ultérieurement. Ils existent sous la forme d'une liste de noms et sont inclus dans les "métadonnées" associées à chaque produit.

Lorsque vous créez une application de développement et que vous y ajoutez des produits, Edge examine tous les produits de l'application et crée une liste de tous les champs d'application de ces produits (liste principale ou globale de l'application, c'est-à-dire une combinaison de tous les champs d'application reconnus).

Lorsqu'une application cliente demande un jeton d'accès à Apigee Edge, elle peut éventuellement spécifier les champs d'application auxquels elle aimerait que le jeton soit associé. Par exemple, la requête suivante demande le champ d'application "A". En d'autres termes, le client demande que le serveur d'autorisation (Edge) génère un jeton d'accès ayant le champ d'application "A" (accordant ainsi à l'application l'autorisation d'appeler les API associées au champ "A"). L'application envoie une requête POST comme suit :

curl -i -X POST -H Authorization: Basic Mg12YTk2UkEIyIBCrtro1QpIG -H content-type:application/x-www-form-urlencoded http://myorg-test.apigee.net/oauth/token?grant_type=client_credentials&scope=A

Cela arrive.

Lorsque Edge reçoit cette requête, il sait quelle application l'envoie, et il identifie l'application de développeur enregistrée par le client (l'ID client et les clés secrètes du client sont encodées dans l'en-tête d'authentification de base). Étant donné que le paramètre de requête scope est inclus, Edge doit décider si l'un des produits d'API associés à l'application de développeur dispose du champ d'application "A". Si c'est le cas, un jeton d'accès est généré avec le champ d'application "A". Vous pouvez imaginer que le paramètre de requête de champ d'application est une sorte de filtre. Si l'application de développeur reconnaît les champs d'application "A, B, X" et que le paramètre de requête spécifie "scope=X Y Z", seul le champ d'application "X" sera attribué au jeton.

Que se passe-t-il si le client n'associe pas de paramètre d'application ? Dans ce cas, Edge génère un jeton qui inclut tous les champs d'application reconnus par l'application du développeur. Il est important de comprendre que le comportement par défaut consiste à renvoyer un jeton d'accès contenant l'union de tous les champs d'application pour tous les produits inclus dans l'application du développeur.

Si aucun des produits associés à une application de développeur ne spécifie de champ d'application et qu'un jeton a un champ d'application, les appels effectués avec ce jeton échoueront.

Imaginons qu'une application de développeur reconnaisse les champs d'application suivants : A, B, C et D. Il s'agit de la liste principale des champs d'application de l'application. Un produit de l'application peut disposer des champs A et B, et un second, des champs C et D, ou d'une combinaison quelconque. Si le client ne spécifie pas de paramètre scope (ou s'il spécifie un paramètre de champ d'application sans valeur), le jeton recevra les quatre champs d'application : A, B, C et D. Là encore, le jeton reçoit un ensemble de champs d'application qui correspond à l'union de tous les champs d'application reconnus par l'application de développeur.

Il existe d'autre cas où le comportement par défaut consiste à renvoyer un jeton d'accès avec tous les champs d'application reconnus, par exemple lorsque la règle GenerateAccessToken (la règle Apigee Edge qui génère des jetons d'accès) ne spécifie pas d'élément <Scope>. Par exemple, voici une règle GenerateAccessToken où <Scope> est spécifié. Si cet élément <Scope> est manquant (ou s'il est présent, mais vide), le comportement par défaut est exécuté.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-GenerateAccessToken">
    <DisplayName>OAuthV2 - Generate Access Token</DisplayName>
    <Attributes>
      <Attribute name='hello' ref='system.time' display='false'>value1</Attribute>
    </Attributes>
    <Scope>request.queryparam.scope</Scope> 
    <GrantType>request.formparam.grant_type</GrantType>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>GenerateAccessToken</Operation>
    <SupportedGrantTypes>
      <GrantType>client_credentials</GrantType>
    </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
</OAuthV2>

Comment les champs d'application sont-ils appliqués ?

Tout d'abord, n'oubliez pas qu'avec Apigee Edge, les jetons d'accès sont validés avec la règle OAuthV2 (généralement placée au tout début d'un flux de proxy). L'opération VerifyAccessToken doit être spécifiée pour la règle. Étudions la règle suivante :

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope> <!-- Optional: space-separated list of scope names. -->
    <GenerateResponse enabled="true"/>
</OAuthV2>

Notez l'élément <Scope>. Il permet de spécifier les champs d'application qui seront acceptés par la règle.

Dans cet exemple, la règle n'aboutit que si le jeton d'accès inclut le champ d'application "A". Si cet élément "<Scope>" est omis ou s'il n'a pas de valeur, la règle ignore le champ d'application du jeton d'accès.

Maintenant, avec la possibilité de valider des jetons d'accès en fonction du champ d'application, vous pouvez concevoir vos API de façon à appliquer des champs d'application spécifiques. Pour ce faire, vous devez concevoir des flux personnalisés auxquels sont associées des règles VerifyAccessToken qui reconnaissent les champ d'application.

Imaginons que votre API dispose d'un flux défini pour le point de terminaison /resourceA :

<Flow name="resourceA">
            <Condition>(proxy.pathsuffix MatchesPath "/resourceA") and (request.verb = "GET")</Condition>
            <Description>Get a resource A</Description>
            <Request>
                <Step>
                    <Name>OAuthV2-VerifyAccessTokenA</Name>
                </Step>
            </Request>
            <Response>
                <Step>
                    <Name>AssignMessage-CreateResponse</Name>
                </Step>
            </Response>
        </Flow>

Lorsque ce flux est déclenché (une requête est reçue avec /resourceA dans le suffixe de chemin d'accès), la règle OAuthV2-VerifyAccessTokenA est appelée immédiatement. Cette règle vérifie que le jeton d'accès est valide et recherche les champs d'application compatibles avec le jeton. Si la règle est configurée comme dans l'exemple ci-dessous, avec "<Scope>A</Scope>", elle n'aboutit que si le jeton d'accès a le champ d'application "A". Sinon, elle renvoie une erreur.

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

En résumé, les développeurs d'API sont chargés de concevoir l'application des champs d'application dans leurs API. Pour ce faire, ils créent des flux personnalisés pour gérer des champs d'application spécifiques et incluent des règles VerifyAccessToken pour appliquer ces champs d'application.

Exemples de code

Enfin, examinons quelques exemples d'appels d'API pour illustrer comment les jetons reçoivent les champs d'application et comment ceux-ci sont appliqués.

Cas par défaut

Imaginons que vous disposiez d'une application de développeur contenant des produits, et que l'union des champs d'application de ces produits soit : "A", "B" et "C". Cet appel d'API demande un jeton d'accès, mais ne spécifie pas de paramètre de requête de champ d'application.

curl -X POST -H content-type:application/x-www-form-urlencoded http://wwitman-test.apigee.net/scopecheck1/token?grant_type=client_credentials

Dans ce cas, le jeton généré se voit attribuer les champs d'application "A", "B" et "C" (comportement par défaut). Les métadonnées du jeton ressembleraient à ceci :

{
  "issued_at" : "1417016208588",
  "application_name" : "eb1a0333-5775-4116-9eb2-c36075ddc360",
  "scope" : "A B C",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-yEgQbQqjRR]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-yxiuHuZcDW@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "atGFvl3jgA0pJd05rXKHeNAC69naDmpW",
  "access_token" : "MveXpj4UYXol38thNoJYIa8fBGlI",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

Supposons maintenant que vous disposiez d'un point de terminaison d'API ayant le champ d'application "A" (c'est-à-dire que sa règle VerifyAccessToken nécessite le champ d'application "A"). Voici la règle VerifyAccessToken correspondante :

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

Voici un exemple d'appel vers un point de terminaison qui applique le champ d'application "A" :

curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI http://wwitman-test.apigee.net/scopecheck1/resourceA 

Cet appel GET aboutit :

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }

L'appel aboutit, car la règle VerifyAccessToken déclenchée lorsque le point de terminaison est appelé requiert un champ d'application "A", et les champs d'application "A", "B" et "C" ont été attribués au jeton d'accès (comportement par défaut).

Cas de filtrage

Imaginons que vous ayez une application de développeur avec des produits dotés des champs d'application "A", "B", "C" et "X". Vous demandez un jeton d'accès et incluez le paramètre de requête scope, comme suit :

curl -i -X POST -H content-type:application/x-www-form-urlencoded 'http://myorg-test.apigee.net/oauth/token?grant_type=client_credentials&scope=A X'

Dans ce cas, le jeton généré se voit attribuer les champs d'application "A" et "X", car ils constituent tous deux des champs d'application valides. N'oubliez pas que l'application de développeur reconnaît les champs d'application "A", "B", "C" et "X". Dans ce cas, vous filtrez la liste des produits d'API en fonction de ces champs d'application. Si un produit est associé au champ d'application "A" ou "X", vous pouvez configurer des points de terminaison d'API qui appliqueront ces champs d'application. Si un produit ne possède pas le champ d'application "A" ou "X" (par exemple, s'il possède "B", "C" et "Z"), les API qui appliquent les champs d'application "A" ou "X" ne peuvent pas être appelées avec le jeton.

Lorsque vous appelez l'API avec le nouveau jeton :

curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG http://wwitman-test.apigee.net/scopecheck1/resourceX

Le jeton d'accès est validé par le proxy d'API. Exemple :

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenX">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A X</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

L'appel GET se déclenche, aboutit et renvoie une réponse. Exemple :

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }
 

L'appel aboutit, car la règle VerifyAccessToken nécessite le champ d'application "A" ou "X", et le jeton d'accès inclut les champs d'application "A" et "X". Bien entendu, si l'élément <Scope> était défini sur "B", cet appel échouerait.

Résumé

Il est important de comprendre comment Apigee Edge gère les champs d'application OAuth 2.0. Voici les points clés à retenir :

  • Une application de développeur "reconnaît" l'union de tous les champs d'application définis pour tous ses produits.
  • Lorsqu'une application demande un jeton d'accès, elle a la possibilité de spécifier les champs d'application souhaités. Il appartient à Apigee Edge (le serveur d'autorisation) de déterminer quels champs d'application vont être réellement attribués au jeton d'accès en fonction premièrement des champs d'application demandés et deuxièmement de ceux qui sont reconnus par l'application de développeur.
  • Si Apigee Edge n'est pas configuré pour vérifier le champ d'application (l'élément <Scope> ne figure pas dans la règle VerifyAccessToken ou il est vide), l'appel d'API réussit tant que le champ d'application intégré dans le jeton d'accès correspond à l'un des champs d'application reconnus par l'application de développeur enregistrée (l'un des champs de la liste "principale" des champs d'application de l'application).
  • Si aucun jeton d'accès n'est associé à un champ d'application, il ne fonctionnera que si Edge ne tient pas compte du champ d'application (l'élément <Scope> est manquant dans la règle VerifyAccessToken ou il est vide).