Sécurisez vos ressources d'API pour la communication entre machines
Découvrez comment exploiter OAuth 2.0 et JWT pour sécuriser vos ressources d'API pour la communication entre machines.
Lors de la création d'un projet impliquant plusieurs services, la sécurité des ressources de l'API est une préoccupation essentielle. Dans cet article, je vous montrerai comment tirer parti d'OAuth 2.0 et JWT pour sécuriser la communication entre services (machine à machine), et comment appliquer le contrôle d'accès basé sur les rôles (RBAC) pour suivre le principe de privilège minimum.
Commencer
Pour suivre, je suppose que vous avez les prérequis suivants :
- Un compte Logto Cloud, ou une instance Logto auto-hébergée
- Au moins deux services qui doivent communiquer l'un avec l'autre
À des fins de démonstration, supposons que nous ayons les services suivants :
- Un service de panier d'achat qui fournit des APIs pour gérer les paniers d'achat
- Point de terminaison :
https://cart.example.com/api
- Point de terminaison :
- Un service de paiement qui fournit des APIs pour traiter les paiements
- Point de terminaison :
https://payment.example.com/api
- Point de terminaison :
Flux d'authentification
Maintenant, notre service de panier doit appeler le service de paiement pour traiter les paiements. Le flux d'authentification est le suivant :
Quelques concepts clés dans le diagramme ci-dessus :
- JWT (RFC 7519) : JSON Web Token. Consultez notre article précédent pour une introduction à JWT.
- JWK (RFC 7517) : JSON Web Key qui est utilisé pour vérifier la signature d'un JWT. Un ensemble de JWK est un ensemble de JWK.
- "client_credentials" grant (RFC 6749) : Un type de concession dans OAuth 2.0. Il utilise les informations d'identification du client pour obtenir un token d'accès. Nous démontrerons les détails dans les sections à venir.
Chaque participant dans le diagramme ci-dessus a un rôle à jouer dans le flux d'authentification :
- Service de panier : Le client qui doit appeler le service de paiement. Bien que ce soit un service, c'est toujours un client dans le contexte OAuth 2.0, et nous appelons ces clients des "applications machine à machine" dans Logto.
- Logto : Le serveur d'autorisation OAuth 2.0 qui délivre les tokens d'accès.
- Service de paiement : La ressource d'API qui fournit des APIs pour traiter les paiements.
Passons en revue le flux d'authentification étape par étape.
Configuration initiale
Pour effectuer le flux d'authentification, nous devons créer une application machine à machine (service de panier) et une ressource d'API (service de paiement) dans Logto.
Créer une ressource d'API
Étant donné que notre service de panier doit être conscient de l'API du service de paiement lors de l'authentification, nous devons d'abord créer une ressource d'API. Allez dans Logto Console, cliquez sur API resources dans la barre latérale gauche et cliquez sur Create API resource. Dans la boîte de dialogue ouverte, nous offrons quelques tutoriels pour vous aider à démarrer. Vous pouvez également cliquer sur Continue without tutorial pour le sauter.
Entrez le nom et l'identifiant de l'API, par exemple, Service de paiement
et https://payment.example.com/api
, puis cliquez sur Create API resource.
Après avoir créé la ressource d'API, vous serez redirigé vers la page de détails. Nous pouvons la laisser telle quelle pour le moment.
Créer une application machine à machine
Cliquez sur Applications dans la barre latérale gauche et cliquez sur Create application. Dans la boîte de dialogue ouverte, trouvez la carte Machine-to-machine et cliquez sur Start building.
Entrez le nom de l'application, par exemple, Service de panier
, et cliquez sur Create application. Un guide interactif vous sera présenté pour vous aider à configurer l'application. Vous pouvez suivre le guide pour comprendre l'utilisation de base, ou cliquer sur Finish and done pour le sauter.
Demander un token d'accès
Étant donné que les applications machine à machine sont supposées être sécurisées (par exemple, elles sont déployées dans un réseau privé), nous pouvons utiliser l'autorisation "client_credentials" d'OAuth 2.0 pour obtenir un token d'accès. Elle utilise l'authentification de base pour authentifier le client :
- L'URL de la requête est le point de terminaison du token de votre instance Logto. Vous pouvez la trouver et la copier dans l'onglet Advanced settings de la page des détails de l'application machine à machine.
- La méthode de la requête est
POST
. - L'en-tête
Content-Type
de la requête estapplication/x-www-form-urlencoded
. - Pour l'en-tête
Authorization
, la valeur estBasic <base64(app_id:app_secret)>
, oùapp_id
etapp_secret
sont l'ID et le secret de l'application machine à machine respectivement. Vous pouvez les trouver sur la page des détails de l'application. - Le corps de la requête doit spécifier le type de concession et l'identifiant de l'API. Par exemple,
grant_type=client_credentials&resource=https://payment.example.com/api
.grant_type=client_credentials
: La valeur constante pour la concession "client_credentials".resource=https://payment.example.com/api
: L'identifiant de l'API de la ressource d'API que le client souhaite accéder.- Si l'application doit être autorisée avec des portées (permissions), vous pouvez également spécifier les portées dans le corps de la requête. Par exemple,
scope=read:payment write:payment
. Nous couvrirons les portées plus tard.
Voici un exemple de la requête utilisant curl
:
Un corps de réponse réussi serait comme :
Envoyer une demande avec l'en-tête d'autorisation
Nous avons maintenant le token d'accès, et nous pouvons l'ajouter à l'en-tête Authorization
de la requête à la ressource d'API. Par exemple, si nous voulons appeler l'API POST /payments
du service de paiement, nous pouvons envoyer la demande suivante :
Valider JWT
Vous remarquerez peut-être que le service de paiement doit valider le JWT en utilisant le jeu de JWK, et peut avoir un cache de jeu de JWK local pour éviter de récupérer le jeu de JWK de Logto à chaque fois. Heureusement, en raison de la popularité de JWT, il existe de nombreuses bibliothèques qui peuvent vous aider à atteindre cet objectif avec quelques lignes de code.
Ces bibliothèques sont généralement appelées "jose" (JavaScript Object Signing and Encryption) ou "jsonwebtoken". Par exemple, dans Node.js, nous pouvons utiliser jose pour valider le JWT :
Si la validation réussit, la variable payload
sera la charge utile JWT décodée. Sinon, une erreur sera générée.
Appliquer le contrôle d'accès basé sur les rôles
Nous avons maintenant réussi à sécuriser la communication entre le service de panier et le service de paiement. Cependant, le flux d'authentification ne garantit que le client est le véritable service de panier, mais ne garantit pas que le service de panier a quelconque permission pour effectuer des actions sur le service de paiement.
Disons que nous voulons permettre au service de panier de créer des paiements, mais pas de lire les paiements.
Définir les permissions
Dans Logto, les "portées" et les "permissions" sont interchangeables. Allez à la page des détails de la ressource d'API du service de paiement, et naviguez vers l'onglet Permissions. Il devrait être vide maintenant. Cliquez sur Create permission, entrez read:payment
comme nom de la permission, et entrez Lire les paiements
comme description de la permission. Puis cliquez sur Create permission.
Répétez les étapes ci-dessus pour créer une autre permission avec le nom write:payment
et la description Créer des paiements
.
Créer un rôle machine à machine
Un rôle est un groupe de permissions. Dans Logto, les applications machine à machine peuvent se voir attribuer des rôles pour accorder des permissions. Cliquez sur "Roles" dans la barre latérale gauche, et cliquez sur Create role.
- Entrez
checkout
comme nom du rôle, et entrezService de paiement
comme description du rôle. - Cliquez sur Show more options. Choisissez "Machine-to-machine app role" comme type de rôle.
- Dans la section "Assigned permissions", cliquez sur l'icône de flèche sur la gauche du nom de la ressource d'API (Service de paiement), et sélectionnez la permission
write:payment
. - Cliquez sur Create role.
- Étant donné que nous avons déjà une application machine à machine (Service de panier), nous pouvons directement attribuer le rôle à cela dans l'étape suivante. Cochez la case à gauche du nom de l'application (Service de panier), et cliquez sur Assign applications.
Demander un token d'accès avec des portées
En plus des paramètres du corps de la requête que nous avons mentionnés dans Demander un token d'accès, nous pouvons également spécifier les portées dans le corps de la requête. Par exemple, si nous voulons demander la permission write:payment
, nous pouvons envoyer la requête suivante :
Pour demander plusieurs portées, vous pouvez les séparer par des espaces. Par exemple, scope=write:payment read:payment
.
Valider les portées
Si une action nécessite la permission write:payment
dans le service de paiement, nous pouvons valider les portées en vérifiant que la revendication scope
de la charge utile JWT :
Conclusion
Si vous souhaitez protéger l'accès au service de panier, vous pouvez également appliquer le même flux d'authentification. Cette fois, le service de panier est la ressource d'API, et le client est un autre service qui doit accéder.
Avec Logto, vos ressources d'API sont sécurisées avec OAuth 2.0 et JWT, et vous pouvez suivre le principe de privilège minimum en appliquant le contrôle d'accès basé sur les rôles. De plus, vous pouvez également utiliser Logto pour gérer vos utilisateurs et leurs permissions, et même vous intégrer à des fournisseurs d'identité tiers.