Le guide complet pour intégrer un serveur OIDC dans votre projet
Apprenez les meilleures pratiques pour intégrer un serveur OIDC (OpenID Connect) dans votre projet et comprenez comment les composants interagissent les uns avec les autres sur la scène.
Vous pouvez rencontrer une situation où vous avez besoin d'un système centralisé d'authentification et d'autorisation, également connu sous le nom de gestion des accès à l'identité (IAM) ou fournisseur d'identité (IdP). Parfois, les gens ajoutent un mot pour désigner l'entreprise, comme IAM Client et IAM Main-d'œuvre.
Mettons de côté ces noms fantaisistes pour un moment. Le besoin d'un IAM peut surgir parce que votre application se développe, ou vous prévoyez de déléguer le travail difficile à un fournisseur dès le début. Néanmoins, vous arrivez à un point où un système d'identité devra être introduit dans votre projet.
Compte tenu de la popularité d'OAuth 2.0, OpenID Connect (OIDC) est un choix naturel pour de nombreux développeurs. Étant donné qu'OIDC est une couche d'authentification construite sur OAuth 2.0, vous pouvez vous sentir familier lorsque vous commencez à travailler avec OIDC. Commençons !
Qu'est-ce qu'un serveur OIDC, et pourquoi devrais-je intégrer un serveur OIDC ?
Un serveur OIDC, ou fournisseur d'identité, est un système centralisé qui gère l'authentification et l'autorisation des utilisateurs. Comme nous en avons discuté dans Pourquoi vous avez besoin d'un système d'identité centralisé pour une entreprise multi-apps, un système d'identité centralisé présente de nombreux avantages.
Disons que votre projet commence avec une application web simple, et qu'elle a l'authentification intégrée :
À mesure que votre projet grandit, vous devez introduire une version mobile :
Ce sera une mauvaise expérience pour les utilisateurs s'ils doivent créer un compte pour chaque application. Puisque vous avez commencé avec une application web, vous laissez l'application mobile communiquer avec l'application web pour l'authentification :
Maintenant, un nouveau service API est introduit. Puisque c'est un service pour des utilisateurs payants, vous devez vous assurer que l'utilisateur est authentifié et autorisé à accéder au service. Pour y parvenir, vous pouvez faire passer le service par l'application web :
Ou, utiliser une technique de jeton pour authentifier l'utilisateur, et valider le jeton en parlant à l'application web dans le service. Ainsi, l'application mobile peut utiliser le service directement :
Les choses deviennent compliquées. Alors vous décidez de séparer la logique d'authentification et d'autorisation dans un service distinct :
Le processus de refactoring peut être pénible. Vous pouvez remarquer que sa complexité augmentera de façon exponentielle à mesure que vous ajoutez plus d'applications et de services au projet. Encore pire, vous pourriez avoir besoin de maintenir plusieurs méthodes d'authentification telles que la connexion sans mot de passe, la connexion sociale, SAML, etc.
C'est pourquoi nous devrions de préférence introduire un fournisseur d'identité dès le début lorsque vous avez un plan pour faire évoluer votre projet.
Meilleures pratiques pour intégrer un serveur OIDC
Trouver un fournisseur OIDC
Il existe de nombreux fournisseurs OIDC disponibles sur le marché. Vous pouvez en choisir un selon vos exigences et préférences. Tant que le fournisseur est conforme à OIDC, il jouera le même rôle dans votre projet.
Que signifient "sujet","client", et "audience" dans OIDC ?
Pour simplifier le concept, nous pouvons penser que le sujet est l'entité qui demande l'accès à une audience via un client.
Voyons quelques scénarios typiques :
1. Un utilisateur clique sur le bouton de connexion sur une application web
Dans une application web traditionnelle et à rendu côté serveur, le frontend et le backend sont couplés. Disons que l'application web sert à la fois le frontend et le backend :
- Sujet : L'utilisateur
- Audience : Le serveur OIDC
- Client : L'application web
Cela peut paraître contre-intuitif que l'audience soit le serveur OIDC. En fait, c'est la clé pour réaliser l'expérience SSO (authentification unique) pour les utilisateurs finaux. Voyons un diagramme de séquence simplifié pour le flux de code d'autorisation :
code
est un code à usage unique pouvant être échangé contre divers jetons, tels que le jeton d'accès, le jeton d'identité, et le jeton d'actualisation. C'est correct si vous ne comprenez pas tous ces jetons en ce moment. À mesure que nous avançons, vous aurez une meilleure compréhension d'eux.
Dans le cas ci-dessus, l'utilisateur n'a pas besoin de se connecter à nouveau lorsqu'il passe à une autre application car l'utilisateur (sujet) est déjà authentifié avec le serveur OIDC (audience).
2. Un utilisateur utilise une application à page unique
Dans une application à page unique (ou une application mobile), le frontend et le backend sont séparés. Disons que le backend est un service API :
- Sujet : L'utilisateur
- Audience : Le service API
- Client : L'application à page unique (SPA)
Un diagramme de séquence simplifié avec flux de code d'autorisation :
Puisque le service API est non-interactif, le SPA doit utiliser le jeton d'accès avec le service API comme audience (le aud
dans le jeton).
Pourquoi le serveur OIDC est-il toujours une audience ?
Techniquement, vous pouvez retirer le serveur OIDC de la liste des audiences. Étant donné que dans la plupart des cas, vous aurez besoin des informations de l'utilisateur du serveur OIDC (ce qui nécessite que le serveur OIDC soit l'audience), il est préférable de toujours inclure le serveur OIDC dans la liste des audiences lorsqu'il s'agit d'interaction utilisateur.
Attendez, vous dites que nous pouvons avoir plusieurs audiences dans une demande d'autorisation ?
Exactement ! Rappelez-vous qu'OIDC est construit sur OAuth 2.0, il est possible de tirer parti de RFC 8707 : Resource Indicators for OAuth 2.0 dans la demande d'autorisation pour spécifier plusieurs audiences. Cela nécessite le soutien à la fois de la source et du serveur OIDC. Logto prend en charge cette fonctionnalité nativement.
3. Une communication de machine à machine
Disons que vous avez un service A qui doit appeler le service B :
- Sujet : Service A
- Audience : Service B
- Client : Service A
Un diagramme de séquence simplifié avec client credentials grant :
Quand le service B doit appeler le service A, les rôles sont simplement échangés.
Récapitulatif
- Sujet : Il peut s'agir d'un utilisateur, d'un service, ou de toute entité qui doit accéder à l'audience.
- Client : Il peut s'agir d'une application web, d'une application mobile, ou de toute entité qui initie la demande ou agit pour le compte du sujet.
- Audience : Il peut s'agir d'un service, d'une API, ou de toute entité qui fournit l'accès au sujet.
Quels sont les jetons d'accès, les jetons d'identité et les jetons d'actualisation ?
Il existe trois types de jetons que vous pourriez rencontrer en travaillant avec OIDC :
- Jeton d'accès : Il est utilisé pour accéder à l'audience. Il peut s'agir d'un JWT (JSON Web Token) ou d'un jeton opaque (généralement une chaîne aléatoire).
- Jeton d'identité : Un jeton spécifique à OIDC qui contient des informations utilisateur. C'est toujours un JWT. Le client peut décoder le jeton pour obtenir des informations utilisateur.
- Jeton d'actualisation : Il est utilisé pour obtenir un nouvel ensemble de jetons lorsque le jeton d'accès ou le jeton d'identité expire.
Pour une explication détaillée de ces jetons, vous pouvez vous référer à Comprendre les jetons d'actualisation, les jetons d'accès et les jetons d'identité dans le protocole OIDC.
Dans le scénario 1 et 2 ci-dessus, le terme demande d'autorisation fait référence à une demande pour obtenir un ensemble de jetons via une source spécifique.
Lorsque tout se passe bien, un ensemble de jetons sera renvoyé à l'étape "Échanger les jetons en utilisant code
". Les jetons disponibles dans l'ensemble dépendent de plusieurs facteurs, notamment du paramètre scope
dans la demande d'autorisation. Pour simplifier, nous supposerons que tous les jetons sont renvoyés dans l'ensemble. Une fois que le jeton d'accès expire, le client peut utiliser le jeton d'actualisation pour obtenir un nouvel ensemble de jetons sans interaction utilisateur.
Pour le scénario 3, c'est plus simple car la source des informations d'identification du client ne retourne qu'un jeton d'accès.
Comment gérer plusieurs audiences dans OIDC ?
Vous pouvez remarquer qu'un seul jeton d'accès est renvoyé à la fois. Comment pourrions-nous gérer le cas où le client a besoin d'accéder à plusieurs audiences ?
Il y a deux solutions courantes :
Spécifiez resource
dans la demande d'échange de code
Lorsque le client échange le code contre des jetons, il peut spécifier un paramètre resource
dans la demande. Le serveur OIDC retournera un jeton d'accès pour l'audience spécifiée si applicable.
Voici un exemple non normatif :
Ensuite, le serveur OIDC retournera un jeton d'accès pour l'audience API_SERVICE
, si applicable.
Utilisez un jeton d'actualisation pour obtenir un nouveau jeton d'accès
Avec RFC 8707, le client peut même spécifier plusieurs audiences en utilisant plusieurs fois le paramètre resource
. Maintenant, si le jeton d'actualisation est disponible dans le client, celui-ci peut spécifier l'audience dans le paramètre resource
lors de l'actualisation du jeton.
Voici un exemple non normatif :
Cela a le même effet que la solution précédente. Pendant ce temps, d'autres audiences accordées sont toujours disponibles dans les futures demandes de jetons.
Source des informations d'identification du client
Vous pouvez également utiliser le paramètre resource
dans la source des informations d'identification du client pour spécifier l'audience. Il n'y a pas de problème avec plusieurs audiences dans cette source car vous pouvez toujours demander un nouveau jeton d'accès pour une audience différente en envoyant simplement une autre demande de jeton.
Protégez votre service API
Le "service API" dans le scénario 2 et le "Service B" dans le scénario 3 ont une chose en commun : ils doivent valider le jeton d'accès pour déterminer si la demande est autorisée. En fonction du format du jeton d'accès, le processus de validation peut varier.
- Jeton opaque : Le service API doit appeler le serveur OIDC pour valider le jeton. Un point d'extrémité d'introspection est généralement fourni par le serveur OIDC à cet effet.
- JWT : Le service API peut valider le jeton localement en vérifiant la signature et les revendications dans le jeton. Le serveur OIDC fournit généralement un point d'accès JSON Web Key Set (JWKS) (
jwks_uri
) pour que le service API obtienne la clé publique pour vérifier la signature.
Si vous êtes nouveau dans JWT, vous pouvez vous référer à Qu'est-ce qu'un JSON Web Token (JWT) ?. En fait, il n'est généralement pas nécessaire de valider la signature et d'évaluer les revendications manuellement car il existe de nombreuses bibliothèques qui peuvent le faire pour vous, comme jose pour Node.js et les navigateurs web.
Revendications d'analyse
En plus de valider la signature JWT, le service API doit toujours vérifier les revendications dans le jeton :
iss
: L'émetteur du jeton. Il doit correspondre à l'URL de l'émetteur du serveur OIDC.aud
: L'audience du jeton. Elle doit correspondre à la valeur d'audience du service API (généralement un URI valide).exp
: La date d'expiration du jeton. Le service API doit rejeter le jeton s'il est expiré.scope
: Les portées (permissions) du jeton. Le service API doit vérifier si la portée requise est présente dans le jeton.
D'autres revendications, telles que sub
(sujet) et iat
(émis à), sont également importantes dans certains cas. Si vous avez des mesures de sécurité supplémentaires, vérifiez les revendications en conséquence.
Autorisation
Une question sans réponse subsiste : Comment déterminons-nous si un scope (c'est-à-dire une permission) peut être accordé à un sujet ?
La seule question conduit à un tout nouveau monde de l'autorisation, qui est hors de portée de cet article. En bref, il existe des approches courantes comme la RBAC (contrôle d'accès basé sur les rôles) et l'ABAC (contrôle d'accès basé sur les attributs). Voici quelques ressources pour vous aider à démarrer :
- RBAC et ABAC : Les modèles de contrôle d'accès que vous devriez connaître
- Maîtriser la RBAC dans Logto : Un exemple concret complet
Remarques finales
L'introduction d'un serveur OIDC dans votre projet est une étape importante. Cela peut considérablement améliorer la sécurité et l'évolutivité de votre projet. Parallèlement, cela peut prendre un certain temps pour comprendre les concepts et les interactions entre les composants.
Choisir un bon fournisseur OIDC qui correspond à vos besoins et préférences peut notablement réduire la complexité du processus d'intégration, car le fournisseur offre généralement le package complet, y compris le serveur OIDC, les mécanismes d'autorisation, les SDK, et les fonctions d'entreprise dont vous pourriez avoir besoin à l'avenir.
J'espère que ce guide vous aide à comprendre les bases de l'intégration d'un serveur OIDC. Si vous en cherchez un pour commencer, je recommanderais égoïstement Logto, notre infrastructure d'identité pour les développeurs.