Implémenter un SDK OIDC simple côté client
Logto propose une variété de SDK pour différentes plateformes. Outre nos SDK officiels, nous encourageons les développeurs de la communauté à créer leurs propres SDK conviviaux. Cet article vous guidera dans la création d'un SDK basique côté client pour OIDC.
Introduction
Logto offre à nos développeurs et groupes commerciaux une solution complète de gestion des identités et des accès des clients (CIAM). Nous proposons une large gamme de SDK prêts à l'emploi pour différentes plateformes et cadres d'application. Combiné à notre service cloud Logto, vous pouvez établir sans effort un flux d'autorisation d'utilisateur hautement sécurisé pour votre application en quelques minutes. En tant qu'entreprise née de la communauté des développeurs, Logto embrasse et valorise notre engagement communautaire. En plus de ces SDK officiellement développés par Logto, nous encourageons continuellement et accueillons chaleureusement les développeurs de la communauté à apporter leur expertise en créant des SDK plus diversifiés et conviviaux, répondant aux besoins uniques de diverses plateformes et cadres. Dans cet article, nous allons brièvement montrer comment implémenter un SDK d'authentification standard OIDC étape par étape.
Contexte
Le flux OpenID Connect (OIDC) est un protocole d'authentification qui s'appuie sur le cadre OAuth 2.0 pour fournir une vérification d'identité et des capacités de connexion unique. Il permet aux utilisateurs de s'authentifier eux-mêmes auprès d'une application et d'obtenir une autorisation pour un accès ultérieur à toute ressource privée de manière sécurisée. Veuillez vous référer aux spécifications OIDC pour plus de détails.
Workflow
Un flux de code d'autorisation standard comprend les étapes suivantes :
Flow d'authentification
- L'utilisateur déclenche la demande de connexion : Un utilisateur anonyme arrive sur votre application depuis une entrée publique. Il tente de s'authentifier et peut-être de demander l'accès à une ressource protégée sur une application ou un service tiers.
- Authentification de l'utilisateur : L'application cliente génère un URI d'authentification et envoie une demande au serveur d'autorisation, qui redirige l'utilisateur vers sa page de connexion. L'utilisateur interagit avec la page de connexion en utilisant un large éventail de méthodes de connexion et est authentifié par le serveur d'autorisation.
- Gérer la redirection de connexion : Suite à une authentification réussie, l'utilisateur sera redirigé vers votre application avec un
code d'autorisation
accordé. Cecode d'autorisation
contient toutes les permissions pertinentes liées au statut d'authentification et aux données d'autorisation demandées. - Echange de jetons : Demande d'échange de jetons en utilisant le
code d'autorisation
extrait de l'adresse de redirection ci-dessus. En retour :id_token
: Un JWT signé numériquement qui contient des informations d'identité sur l'utilisateur authentifié.access_token
: Unaccess_token
opaque qui peut être utilisé pour accéder au point de terminaison des informations de base de l'utilisateur.refresh_token
: Jeton d'identification permettant à l'utilisateur de maintenir un échange continu pour unaccess_token
Flux d'autorisation
- Accès aux informations de l'utilisateur : Pour accéder à plus d'informations sur l'utilisateur, l'application peut faire des requêtes supplémentaires au point de terminaison UserInfo, en utilisant l'
access_token
opaque obtenu à partir du flux initial d'échange de jetons. Cela permet de récupérer des détails supplémentaires sur l'utilisateur, tels que son adresse électronique ou sa photo de profil. - Octroi d'accès à la ressource protégée : Si nécessaire, l'application peut faire des requêtes supplémentaires au point de terminaison d'échange de jetons, en utilisant le
refresh_token
combiné avec les paramètresresource
etscope
, pour obtenir unaccess_token
dédié pour que l'utilisateur accède à la ressource cible. Ce processus aboutit à l'émission d'unaccess_token
au format JWT contenant toutes les informations d'autorisation nécessaires pour accéder à la ressource protégée.
Implémentation
Nous allons suivre certaines stratégies de conception au sein de notre SDK JavaScript @logto/client pour démontrer le processus d'implémentation d'un SDK simple pour votre propre application client. Notez que la structure de code détaillée peut varier en fonction du cadre client avec lequel vous travaillez. N'hésitez pas à choisir l'un des SDK officiels de Logto comme exemple pour votre propre projet de SDK.
Aperçu
Constructeur
Le constructeur doit prendre un logtoConfig en entrée. Cela fournit toutes les configurations nécessaires dont vous aurez besoin pour établir une connexion d'authentification à travers ce SDK.
En fonction de la plateforme ou du cadre que vous utilisez pour le SDK, vous pouvez passer une instance de stockage local persistante au constructeur. Cette instance de stockage sera utilisée pour stocker tous les jetons et secrets d'autorisation.
Init de l'authentification utilisateur
Avant de générer un URL de demande d'authentification, il est essentiel de compléter plusieurs étapes de préparation pour garantir un processus sécurisé.
Obtention des configurations OIDC du serveur d'autorisation
Définissez une méthode privée getOidcConfigs
pour récupérer les configurations OIDC du point de terminaison de la découverte du serveur d'autorisation. La réponse aux configurations OIDC contient toutes les informations de métadonnées que le client peut utiliser pour interagir avec le serveur d'autorisation, y compris les emplacements de ses points de terminaison et les capacités du serveur. (Veuillez vous référer à OAuth OAuth Authorization Server Metadata Specs pour plus de détails.)
Générateur PKCE
Un PKCE(Proof Key for Code Exchange) flux de validation est essentiel pour tous les flux publics d'échange de code d'autorisation. Il atténue le risque d'attaque d'interception du code d'autorisation. Ainsi, un code_challenge
et code_verifier
est nécessaire pour toutes les demandes d'autorisation des applications client publics (par exemple, application native et SPA).
Les méthodes d'implémentation peuvent varier en fonction des langages et des cadres que vous utilisez. Veuillez vous référer à la spécification code_challenge et code_verifier pour les définitions détaillées.
Générer le paramètre d'état
Dans le flux d'autorisation, le paramètre d'état est une valeur générée aléatoirement qui est incluse dans la demande d'autorisation envoyée par le client. Il agit comme une mesure de sécurité pour prévenir les attaques par requête forgée intersites (CSRF).
Stocker les infos de session intermédiaire
Il y a plusieurs paramètres qui doivent être préservés dans le stockage pour la validation après que l'utilisateur s'est authentifié et a été redirigé vers le côté client. Nous allons implémenter une méthode pour mettre ces paramètres intermédiaires dans le stockage.
Sign-in
Enfin, prenons tout ce que nous avons implémenté ci-dessus, définissons une méthode générant une URL de connexion de l'utilisateur et redirigeons l'utilisateur vers le serveur d'autorisation pour qu'il s'authentifie.
Gérer le callback de connexion de l'utilisateur
Dans la section précédente, nous avons créé une méthode de connexion qui génère une URL pour l'authentification de l'utilisateur. Cette URL contient tous les paramètres nécessaires pour initier un flux d'authentification à partir d'une application client. La méthode redirige l'utilisateur vers la page de connexion du serveur d'autorisation pour l'authentification. Après une connexion réussie, l'utilisateur final sera redirigé vers l'emplacement redirect_uri fourni ci-dessus. Tous les paramètres nécessaires seront transportés dans l'URI de redirection pour compléter les flux d'échange de jetons suivants.
Extraire et vérifier l'URL du callback
Cette étape de validation est extrêmement importante pour prévenir toute forme d'attaques de faux callbacks d'autorisation. L'URL de callback DOIT être soigneusement vérifiée avant d'envoyer une demande d'échange de code supplémentaire au serveur d'autorisation. Tout d'abord, nous devons récupérer les données de la session de connexion que nous avons stockées dans le stockage de l'application.
Ensuite, vérifiez les paramètres de l'URL du callback avant d'envoyer la demande d'échange de jeton.
- Utilisez le
redirectUri
précédemment stocké pour vérifier si lecallbackUri
est le même que celui que nous avons envoyé au serveur d'autorisation. - Utilisez le
state
précédemment stocké pour vérifier si l'état renvoyé est le même que celui que nous avons envoyé au serveur d'autorisation. - Vérifiez si une erreur est renvoyée par le serveur d'autorisation
- Vérifiez l'existence du
code d'autorisation
renvoyé
Envoyer la demande d'échange de code
En tant que dernière étape du flux d'authentification de l'utilisateur, nous utiliserons le code d'autorisation
pour envoyer une demande d'échange de jetons et obtenir les jetons d'autorisation nécessaires. Pour plus de détails sur les définitions des paramètres de la demande, veuillez vous référer à la spécification d'échange de jetons token exchange specification.
- code : le
code d'autorisation
que nous avons reçu de l'URI de callback - clientId : l'ID de l'application
- redirectUri : La même valeur est utilisée lors de la génération de l'URL de connexion pour l'utilisateur.
- codeVerifier : vérificateur de code PKCE. De la même manière que le redirectUri, le serveur d'autorisation comparera cette valeur à celle que nous avions précédemment envoyée, assurant la validation de la demande d'échange de jetons entrante.
Gérer le callback de connexion
Faisons le point sur tout ce que nous avons. Construisons la méthode de gestion du callback de connexion :
En conséquence, la demande d'échange de jetons renverra les jetons suivants :
id_token
: idToken OIDC, un Jeton Web JSON (JWT) qui contient des informations d'identité sur l'utilisateur authentifié. L'id_token peut également être utilisé comme Source Unique de Vérité(SSOT) du statut d'authentification d'un utilisateur.access_token
: Le code d'autorisation par défaut renvoyé par le serveur d'autorisation. Peut être utilisé pour appeler le point de terminaison des informations de l'utilisateur et récupérer les informations de l'utilisateur authentifié.refresh_token
: (si la portée offline_access est présente dans la demande d'autorisation) : Ce jeton de rafraîchissement permet à l'application client d'obtenir un nouvel access_token sans que l'utilisateur ait besoin de se ré-authentifier, accordant un accès à plus long terme aux ressources.expires_in
: La durée en secondes pendant laquelle l'access_token est valide avant son expiration.
Vérification du jeton d'identification
La vérification et l'extraction des claims du id_token
sont une étape cruciale du processus d'authentification pour garantir l'authenticité et l'intégrité du jeton. Voici les étapes clés impliquées dans la vérification d'un idToken
.
- Vérification de la signature : Le
id_token
est numériquement signé par le serveur d'autorisation en utilisant sa clé privée. L'application client doit valider la signature en utilisant la clé publique du serveur d'autorisation. Ceci assure que le jeton n'a pas été altéré et a bien été émis par le serveur d'autorisation légitime. - Vérification de l'émetteur**:** Vérifiez que la claim "iss" (issuer) dans le
id_token
correspond à la valeur attendue, indiquant que le jeton a été émis par le bon serveur d'autorisation. - Vérification de l'audience : Assurez-vous que la claim "aud" (audience) dans le
id_token
correspond à l'ID du client de l'application client, assurant que le jeton est destiné au client. - Vérification de l'expiration : Vérifiez que la claim "iat" (emis à) dans le
id_token
n'a pas dépassé l'heure actuelle, assurant que le jeton est toujours valide. Comme il y a des coûts de transaction de réseau, nous devons définir une tolérance de temps émis lors de la validation de la claim iat du jeton reçu.
Le id_token
renvoyé est un Jeton Web JSON (JWT) standard. Selon le cadre que vous utilisez, vous pouvez trouver divers plugins de validation de jetons JWT pratiques pour vous aider à déchiffrer et valider le jeton. Pour cet exemple, nous utiliserons jose dans notre SDK JavaScript pour faciliter la validation et le décodage du jeton.
Obtenir des informations utilisateur
Après une authentification réussie, des informations utilisateur de base peuvent être récupérées à partir de l'id_token
OIDC délivré par le serveur d'autorisation. Cependant, en raison de considérations de performance, le contenu du jeton JWT est limité. Pour obtenir des informations de profil utilisateur plus détaillées, les serveurs d'autorisation conformes à OIDC offrent un point de terminaison d'information d'utilisateur clé en main. Ce point de terminaison vous permet de récupérer des données de profil d'utilisateur supplémentaires en demandant des scopes de profil d'utilisateur spécifiques.
Lors de l'appel à un point de terminaison d'échange de jetons sans indiquer une ressource API spécifique, le serveur d'autorisation délivrera par défaut un access_token
de type opaque. Cet access_token
ne peut être utilisé que pour accéder au point de terminaison des informations utilisateur, permettant la récupération des données de profil utilisateur de base.
Obtenir un access_token pour l'autorisation de la ressource protégée
Dans la plupart des cas, une application client nécessite non seulement l'authentification de l'utilisateur, mais aussi son autorisation pour accéder à certaines ressources protégées ou points de terminaison API. Ici, nous allons utiliser le refresh_token
obtenu lors de la connexion pour obtenir des access_tokens
spécifiquement accordés pour gérer des ressources particulières. Cela nous permet d'obtenir l'accès à ces APIs protégées.
Résumé
Nous avons fourni des implémentations de méthodes essentielles pour le processus d'authentification et d'autorisation des utilisateurs de l'application côté client. Selon votre scénario spécifique, vous pouvez organiser et optimiser la logique du SDK en conséquence. Notez que des variations peuvent exister en raison des différentes plateformes et cadres.
Pour plus de détails, explorez les packages de SDK offerts par Logto. Nous encourageons plus d'utilisateurs à se joindre au développement et à participer aux discussions avec nous. Vos commentaires et contributions sont très appréciés alors que nous continuons à améliorer et à étendre les capacités du SDK.
Annexe
Portées réservées
Portées réservées de Logto dont vous aurez besoin pour passer lors de la première demande d'autorisation. Ces portées sont soit des portées fondamentales réservées à OIDC, soit réservées à Logto pour compléter un flux d'autorisation réussi.
Nom de la portée | Description |
---|---|
openid | Requis pour accorder id_token après une authentification réussie. |
offline-access | Requis pour accorder un refresh_token qui permet à votre application cliente d'échanger et de renouveler un access_token hors écran. |
profile | Requis pour obtenir un accès aux informations de base de l'utilisateur |
Logto Config
Nom de la propriété | Type | Requis | Description | Valeur par défaut |
---|---|---|---|---|
appId | string | true | L'identifiant unique de l'application. Généré par le serveur d'autorisation pour identifier l'application cliente. | |
appSecret | string | Le secret de l'application est utilisé, en conjonction avec l'ID de l'application, pour vérifier l'identité du demandeur. Il est requis pour les clients confidentiels comme les applications web Go ou Next.js, et facultatif pour les clients publics comme les applications natives or single-page applications (SPAs) | ||
endpoint | string | true | Votre point de terminaison racine du serveur d'autorisation. Cette propriété sera largement utilisée pour générer des demandes d'autorisation. | |
scopes | liste de string | Indique toutes les portées de ressources nécessaires que l'utilisateur peut devoir être accordées pour accéder à toute ressource protégée donnée. | [reservedScopes] | |
resources | liste de string | Tous les indicateurs de ressource protégée auxquels l'utilisateur peut demander l'accès |