Aws cognito jwt
Utilisez l’authentification JWT par clé privée entre les groupes d’utilisateurs Amazon Cognito et un fournisseur d’identité OIDC
Avec les groupes d’utilisateurs Amazon Cognito, vous pouvez ajouter des fonctionnalités d’inscription et de connexion des utilisateurs et contrôler l’accès à vos applications Web et mobiles. Vous pouvez permettre à vos utilisateurs qui ont déjà des comptes auprès d’autres fournisseurs d’identité (IdP) d’ignorer l’étape d’inscription et de se connecter à votre application à l’aide d’un compte existant via SAML 2.0 ou OpenID Connect (OIDC). Dans ce billet de blog, vous allez apprendre comment étendre l’octroi du code d’autorisation entre Cognito et un fournisseur d’identité OIDC externe avec l’authentification client JSON Web Token (JWT) à clé privée.
Pour OIDC, Cognito utilise le flux d’octroi du code d’autorisation OAuth 2.0 tel que défini par l’IETF dans la RFC 6749 Section 1.3.1. Ce flux peut être décomposé en Deux étapes : l’authentification de l’utilisateur et la demande de jeton. Lorsqu’un utilisateur doit s’authentifier par le biais d’un fournisseur d’identité externe, le groupe d’utilisateurs Cognito le redirige vers le point de terminaison de connexion du fournisseur d’identité. Une fois l’authentification réussie, l’IdP renvoie une réponse qui inclut un code d’autorisation, qui conclut l’étape d’authentification. Le pool d’utilisateurs Cognito utilise désormais ce code, ainsi qu’une clé secrète client pour l’authentification du client, afin de récupérer un JWT à partir de l’IdP. Le JWT se compose d’un jeton d’accès et d’un jeton d’identité. Cognito ingère ce JWT, crée ou met à jour l’utilisateur dans le pool d’utilisateurs et renvoie au client un JWT qu’il a créé pour la session du client. Vous trouverez une description plus détaillée de ce flux dans la documentation Amazon Cognito.
Bien que ce flux sécurise suffisamment les demandes entre Cognito et l’IdP pour la plupart des clients, ceux du secteur public, de la santé et de la finance ont parfois besoin de s’intégrer à Fournisseurs d’identité qui appliquent des mesures de sécurité supplémentaires dans le cadre de leurs exigences de sécurité. Par le passé, cela a été soulevé dans les conversations chez AWS lorsque nos clients avaient besoin d’intégrer Cognito avec, par exemple, les IdP HelseID (secteur de la santé, Norvège), login.gov (secteur public, États-Unis) ou GOV.UK One Login (secteur public, Royaume-Uni). Les clients qui utilisent Okta, PingFederate ou des fournisseurs d’identité similaires et qui souhaitent des mesures de sécurité supplémentaires dans le cadre de leurs exigences de sécurité internes peuvent également trouver souhaitable d’ajouter des exigences de sécurité supplémentaires dans le cadre de leurs propres politiques.
L’exigence supplémentaire la plus courante consiste à remplacer la clé secrète client par une assertion qui consiste en une clé privée JWT comme moyen d’authentification du client lors des demandes de jetons. Cette méthode est définie par une combinaison de la RFC 7521 et de la RFC 7523. Au lieu d’une clé symétrique (la clé secrète client), cette méthode utilise une paire de clés asymétriques pour signer un JWT avec un Clé privée. L’IdP peut ensuite vérifier la demande de jeton en validant la signature de ce JWT à l’aide de la clé publique correspondante. Cela permet d’éliminer l’exposition de la clé secrète client à chaque demande, réduisant ainsi le risque de falsification de la demande, en fonction de la qualité de la clé utilisée et de la manière dont l’accès à la clé privée est sécurisé. De plus, le JWT a une date d’expiration, ce qui limite encore plus le risque d’attaques par rejeu à une fenêtre de temps étroite.
Un groupe d’utilisateurs Cognito ne prend pas en charge en mode natif l’authentification du client JWT à clé privée lors de l’intégration avec un fournisseur d’identité externe. Toutefois, vous pouvez toujours intégrer des groupes d’utilisateurs Cognito avec des fournisseurs d’identité qui prennent en charge ou nécessitent l’authentification JWT par clé privée à l’aide d’Amazon API Gateway et d’AWS Lambda.
Cet article de blog présente une vue d’ensemble de la manière dont vous pouvez mettre en œuvre cette solution. Pour en savoir plus sur le code sous-jacent, comment configurer le et à quoi ressemble le flux de demande détaillé, consultez la section Déployer une démo plus loin dans cet article. Gardez à l’esprit que cette solution ne couvre pas le flux de demandes entre votre propre application et un groupe d’utilisateurs Cognito, mais uniquement la communication entre Cognito et l’IdP.
Présentation de la solution
Suite aux détails de mise en œuvre technique des RFC mentionnés précédemment, le flux de demandes requis entre un pool d’utilisateurs Cognito et l’IdP OIDC externe peut être décomposé en quatre étapes simplifiées, illustrées à la figure 1.
Figure 1 : Diagramme UML simplifié de l’implémentation cible pour l’utilisation d’une clé privée JWT lors de l’octroi du code d’autorisation
Dans cet exemple, nous utilisons l’interface utilisateur hébergée du pool d’utilisateurs Cognito, car elle fournit déjà l’intégration IdP alignée sur OAuth 2.0, et l’étendons avec la clé privée JWT. Graphique 1 illustre les étapes suivantes :
- L’interface utilisateur hébergée transmet le client utilisateur au point de terminaison /authorize du fournisseur d’identité OIDC externe avec une requête HTTP GET.
- Une fois que l’utilisateur s’est connecté avec succès à l’IdP, la réponse de l’IdP inclut un code d’autorisation.
- L’interface utilisateur hébergée envoie ce code dans une requête HTTP POST au point de terminaison /token de l’IdP. Par défaut, l’interface utilisateur hébergée ajoute également une clé secrète client pour l’authentification du client. Pour vous aligner sur la méthode d’authentification JWT de clé privée, vous devez remplacer la clé secrète client par une assertion client et spécifier le type d’assertion client, comme mis en évidence dans le diagramme et décrit plus en détail plus loin.
- L’IdP valide l’assertion du client à l’aide d’une clé publique pré-partagée.
- L’IdP émet le JWT de l’utilisateur, que Cognito ingère pour créer ou mettre à jour l’utilisateur dans le groupe d’utilisateurs.
Comme mentionné précédemment, le jeton les demandes entre un groupe d’utilisateurs Cognito et un fournisseur d’identité externe ne prennent pas en charge en mode natif l’assertion de client requise. Toutefois, vous pouvez rediriger les demandes de jeton vers, par exemple, une passerelle Amazon API Gateway, qui appelle une fonction Lambda pour étendre la demande avec les nouveaux paramètres. Étant donné que vous devez signer l’assertion du client avec une clé privée, vous avez également besoin d’un emplacement sécurisé pour stocker cette clé. Pour cela, vous pouvez utiliser AWS Secrets Manager, qui vous aide à protéger la clé contre toute utilisation non autorisée. En gardant à l’esprit le flux requis et les services supplémentaires, vous pouvez créer l’architecture suivante.
Figure 2 : Schéma d’architecture avec Amazon API Gateway et Lambda pour traiter les demandes de jeton entre Cognito et le fournisseur d’identité OIDC
Examinons de plus près les composants individuels et le flux de demandes illustrés à la figure 2.
Lors de l’ajout d’un IdP OIDC à un utilisateur Cognito , vous configurez les points de terminaison pour Authorization, UserInfo, Jwks_uri et Token. Étant donné que la clé privée n’est requise que pour le flux de demande de jeton, vous pouvez configurer les ressources pour rediriger et traiter les demandes, comme suit (les numéros d’étape correspondent à la numérotation d’étape de la figure 2) :
- Configurez les points de terminaison pour Authorization, UserInfo et Jwks_Uri avec ceux de l’IdP.
- Créez une passerelle API Gateway avec un itinéraire dédié pour les demandes de jeton (par exemple, /token) et ajoutez-la en tant que point de terminaison de jeton dans la configuration IdP dans Cognito.
- Intégrez cet itinéraire à une fonction Lambda : lorsque Cognito appelle le point de terminaison de l’API, il appelle automatiquement la fonction.
Avec les paramètres de demande d’origine, qui incluent le code d’autorisation, cette fonction effectue les opérations suivantes :
- Récupère la clé privée à partir du Gestionnaire de secrets.
- Crée et signe l’assertion du client.
- Envoie la demande de jeton au point de terminaison du jeton IdP.
- Reçoit la réponse de l’IdP.
- Renvoie la réponse au point de terminaison de réponse Cognito IdP.
Les détails de la logique de la fonction peuvent être décomposés comme suit :
- Décodez le corps de la demande d’origine, y compris le code d’autorisation acquis au cours du flux d’autorisation.
- Récupérez la clé privée à partir de Secrets Manager à l’aide de l’API GetSecretValue ou de l’équivalent du kit SDK, ou à l’aide de l’extension AWS Parameters and Secrets Lambda.
- Créez et signez le JWT.
- Modifiez le corps d’origine et effectuez la demande de jeton, y compris les paramètres d’origine pour le grant_type, le code et le client_id, avec les client_assertion_type ajoutés et les client_assertion. (L’exemple suivant : la requête HTTP comporte des sauts de ligne et des espaces réservés entre crochets pour une meilleure lisibilité.)
- Renvoie la réponse de l’IdP.
Notez qu’aucune clé secrète client n’est nécessaire dans cette demande. Au lieu de cela, vous ajoutez un type d’assertion client sous la forme urn :ietf :params :oauth :client-assertion-type :jwt-bearer, et l’assertion client avec le JWT signé.
Si la demande aboutit, la réponse de l’IdP inclut un JWT avec le jeton d’accès et le jeton d’identité. Lors du renvoi de la réponse via la fonction Lambda, Cognito ingère le JWT et crée ou met à jour l’utilisateur dans le groupe d’utilisateurs. Il répond ensuite à la demande d’autorisation d’origine du client utilisateur en envoyant son propre code d’autorisation, qui peut être échangé contre un JWT émis par Cognito dans votre propre application.
Pour
déployer un exemple de cette solution, consultez notre référentiel GitHub. Vous y trouverez les prérequis et les étapes de déploiement, ainsi que Informations supplémentaires détaillées.
Pour
optimiser davantage cette solution, vous devez envisager de vérifier les détails de l’événement dans la fonction Lambda avant de traiter entièrement les demandes. De cette façon, vous pouvez, par exemple, vérifier que tous les paramètres requis sont présents et valides. Pour ce faire, vous pouvez définir une clé secrète client lorsque vous créez l’intégration IdP pour le pool d’utilisateurs. Lorsque Cognito envoie la demande de jeton, il ajoute la clé secrète client dans le corps codé, afin que vous puissiez la récupérer et valider sa valeur. En cas d’échec de la validation, les demandes peuvent être abandonnées plus tôt afin d’améliorer la gestion des exceptions et d’éviter que les demandes non valides n’entraînent des frais de fonction inutiles.
Dans cet exemple, nous avons utilisé Secrets Manager pour stocker la clé privée. Vous pouvez explorer d’autres alternatives, telles que AWS Systems Manager Parameter Store ou AWS Key Management Service (AWS KMS). Pour récupérer la clé à partir de le magasin de paramètres, vous pouvez utiliser le kit SDK ou l’extension Lambda AWS Parameter and Secrets. Avec AWS KMS, vous pouvez à la fois créer et stocker la clé privée, ainsi que dériver une clé publique via les API du service, et vous pouvez également utiliser l’API de signature pour signer le JWT dans la fonction Lambda.
Conclusion
En redirigeant le point de terminaison du jeton IdP dans la configuration IdP OIDC externe du pool d’utilisateurs Cognito vers une route dans une passerelle API, vous pouvez utiliser les fonctions Lambda pour personnaliser le flux de demande entre Cognito et l’IdP. Dans l’exemple de cet article, nous avons montré comment modifier le mécanisme d’authentification du client lors de la demande de jeton d’une clé secrète client en une assertion client avec un JWT signé (JWT de clé privée). Vous pouvez également appliquer la même approche de type proxy pour personnaliser encore plus le flux de demande, par exemple, en ajoutant une clé de preuve pour l’échange de code (PKCE), dont vous trouverez un exemple dans les exemples aws Référentiel GitHub.
Si vous avez des commentaires sur cet article, soumettez-les dans la section Commentaires ci-dessous. Si vous avez des questions sur cet article, lancez un nouveau fil de discussion sur AWS re :Post pour les groupes d’utilisateurs Amazon Cognito ou contactez AWS Support.
Vous souhaitez obtenir plus de contenu pratique, d’actualités et d’annonces de fonctionnalités sur la sécurité AWS ? Suivez-nous sur Twitter.
Martin Pagel
Martin est un architecte de solutions en Norvège spécialisé dans la sécurité et la conformité, en mettant l’accent sur l’identité. Avant de rejoindre AWS, il a évolué dans la peau d’un administrateur système et d’un ingénieur, concevant et mettant en œuvre l’aspect technique d’une grande variété de contrôles de conformité dans des entreprises des secteurs de l’automobile, de la fintech et de la santé. Aujourd’hui, il aide nos clients de la région nordique à atteindre leurs objectifs de sécurité et de conformité.
MOTS CLÉS : Amazon Cognito, Groupes d’utilisateurs Amazon Cognito, Blog sur la sécurité