Il s'agit d'une version statique de l'intranet d'A. Jonquet, certaines fonctionnalités sont donc potentiellement non fonctionnelles.
Rejoindre la version dynamique 🔒
React
Navigation

JSON Web Token (JWT)

Ce TP sera l'occasion de comprendre le fonctionnement de l'authentification à l'aide de JWT et sa mise en place dans une application Web. Vous en profiterez pour approfondir votre connaissance de Redux Toolkit et plus particulièrement découvrir son module Query.

L'API Tasks servira de support pour ce TP.

Remarque importante

Les notes des TP seront obtenues à partir de vos dépôts Git, vous devrez donc prendre grand soin de la qualité de ces dépôts et de leurs « commits ».

De manière similaire, les descriptions de vos « commits » devront être claires et informatives, afin de permettre l'évaluation de la progression de votre travail.

Objectifs de la séance

  • Mise en place d'une authentification par JWT
  • Utilisation d'un jeton de rafraîchissement pour prolonger la durée de vie du JWT
  • Approfondissement de Redux et Redux Toolkit
  • Utilisation de Redux Toolkit Query pour gérer les appels à l'API

Ajout des données de l'authentification

Afin de pouvoir gérer l'authentification de l'utilisateur, vous ajouterez une nouvelle tranche de données permettant de gérer l'authentification.

Cette tranche de données ne contiendra que :

  • le JWT de l'utilisateur, initialisé à null.
  • le jeton de rafraîchissement, initialisé à null.
  • l'utilisateur authentifié, initialisé à null.
  • un booléen mémorisant si l'utilisateur souhaite rester connecté lorsque l'application est fermée.

Pour l'instant aucune action ne sera ajoutée au « reducer » de la tranche de données.

Travail à réaliser
  • Ajout d'une nouvelle tranche de données pour l'authentification.

Ajout des composants d'authentification

Vous poursuivrez la mise en place de l'authentification en ajoutant deux composants :

  • Gate, le composant permettant de sécuriser l'accessibilité de l'application.
  • et LoginForm, le composant permettant à l'utilisateur de se connecter.

Le composant Gate se contentera de récupérer le JWT de la tranche de données d'authentification, et il affichera le composant LoginForm s'il n'est pas défini et sa propriété children sinon.

Le composant LoginForm affichera un formulaire de connexion, avec deux champs : le login et le mot de passe. Ainsi qu'une case à cochée permettant à l'utilisateur de préciser s'il souhaite rester connecté. Et enfin deux liens permettant à l'utilisateur de s'inscrire ou de réinitialiser son mot de passe.

Vous ferez en sorte d'obtenir le couple login/mot de passe saisie par l'utilisateur dans une fonction invoquée lors du clic sur le bouton de validation du formulaire. Une solution simple de gestion des composants non-contrôlée est d'utiliser l'événement de soumission pour accéder au formulaire et à ses champs.

Travail à réaliser
  • Mise en place des composants de sécurisation et d'authentification de l'application.

Utilisation de RTK Query et requête d'authentification

Vous commencerez par découvrir le module RTK Query de Redux Toolkit.

Puis vous mettrez en place une nouvelle tranche de données pour l'API de gestion de tâches, que vous pourrez ajouter à votre store global.

Vous définirez ensuite un nouvel « enpoint » permettant d'authentifier un utilisateur auprès de l'API et d'obtenir un JWT (https://iut-rcc-infoapi.univ-reims.fr/tasks/api/auth).

Vous devriez maintenant être capable d'utiliser le hook créé par RTK Query pour invoquer l'endpoint d'authentification et obtenir un JWT depuis le composant LoginForm.

Pour l'instant, vous vérifierez dans la console de développement du navigateur que le résultat de la requête comporte bien un jeton d'authentification et un jeton de rafraîchissement.

Travail à réaliser
  • Mise en place d'une tranche de données pour l'API Tasks.
  • Ajout d'un endpoint d'authentification.

Initialisation des données d'authentification de l'application

Vous allez maintenant mettre à jour les données d'authentification de l'application. Il aurait été possible d'ajouter une action au « reducer » d'authentification pour initialiser les jetons, que vous auriez « dispatcher » lors du résultat de la requête.

Mais vous allez plutôt définir une relation entre le magasin de données et la requête afin que les données soient automatiquement mises à jour lors de la requête.

Il s'agit d'ajouter un nouveau « matcher » associé à la bonne réalisation du endpoint de connection. La charge utile de l'action sera alors le résultat de la requête.

Une fois authentifier, votre utilisateur devrait voir l'application protéger par le composant Gate.

Travail à réaliser
  • Mise à jour des données d'authentification à l'aide d'un extraReducers.

Injection des en-têtes d'authentification

Pour illustrer l'accès aux données protégées de l'API, vous allez ajouter un nouvel « enpoint » permettant d'obtenir les informations de l'utilisateur identifié par le JWT (https://iut-rcc-infoapi.univ-reims.fr/tasks/api/me).

Vous ajouterez dans le composant principal de l'application, l'affichage des informations de l'utilisateur (prénom, nom ou login) ainsi qu'un bouton permettant de mettre à jour ces données (refetch).

Pour que la requête soit autorisé par l'API, vous devrez ajouter le JWT dans les en-têtes de la requête. Plutôt que de le faire pour chaque requête, vous allez configurer RTK Query afin qu'il ajoute automatiquement l'en-tête d'authentification à chaque requête à l'aide de prepareHeaders comme dans l'exemple de gestion d'authentification de RTK Query.

Vous devriez maintenant être capable d'afficher les informations de l'utilisateur dans le composant principal de l'application.

Travail à réaliser
  • Ajout d'un endpoint d'information sur l'utilisateur courant.
  • Injection automatique des en-têtes d'authentification.

Récupération automatique de l'utilisateur connecté

La requête permettant d'obtenir les informations de l'utilisateur courant est un cas d'utilisation d'une requête protégée. Mais il serait intéressant de disposé de cette information automatiquement lors de l'authentification de l'utilisateur.

En vous inspirant de l'exemple sur les multiples requêtes, vous ferez en sorte de modifier le « enpoint » d'authentification afin qu'il récupère automatiquement les informations de l'utilisateur courant et qu'il retourne un résultat contenant les deux jetons et les informations de l'utilisateur.

Vous modifierez le « reducer » d'authentification afin qu'il mette à jour les données de l'utilisateur courant lors de la résolution de la requête d'authentification.

Travail à réaliser
  • Récupération automatique des données de l'utilisateur lors de l'authentification.

Ajout d'un bouton de déconnexion

Vous ajouterez à l'en-tête de l'application un bouton permettant à l'utilisateur de se déconnecter. Ce bouton pourra afficher l'avatar de l'utilisateur (/api/users/{id}/avatar).

Pour réaliser la déconnexion, vous ajouterez une nouvelle action à la tranche de données d'authentification permettant de réinitialiser les données d'authentification.

Travail à réaliser
  • Ajout d'un bouton de déconnexion.
  • Ajout d'une action de déconnexion.

Rafraîchissement du jeton d'authentification

Vous avez dû constater que le jeton d'authentification à une durée de vie très courte. La durée est courte pour des besoins pédagogiques, dans un cas réel, la durée pourrait être plus longue.

Pour améliorer l'expérience utilisateur, il est nécessaire de mettre en place un mécanisme de rafraîchissement du jeton d'authentification. Pour cela, vous ajouterez une nouvelle action à la tranche de données d'authentification permettant de rafraîchir le jeton d'authentification.

Puis vous automatiserez le rafraîchissement du jeton d'authentification en utilisant la fonctionnalité de ré-authorization automatique de RTK Query. Pour cela, vous utiliserez la route de l'API permettant de rafraîchir le jeton d'authentification (https://iut-rcc-infoapi.univ-reims.fr/tasks/api/refresh) à partir du jeton de rafraîchissement.

Vous devriez constater dans la console de développement que le jeton d'authentification est automatiquement rafraîchi lorsqu'une requête n'est plus autorisée, avant de rejouer la requête.

Vous pourrez ensuite empêcher les multiples requêtes d'authentification en suivant la documentation de RTK Query.

Travail à réaliser
  • Ajout d'une action de rafraîchissement des jetons d'authentification et de rafraîchissement.
  • Rafraîchissement automatique des jetons lorsque le jeton d'authentification est périmé.

Enregistrement du jeton de rafraîchissement

Vous avez dû constater que le jeton de rafraîchissement n'est pas enregistré dans le navigateur. En effet, lors du rafraîchissement de la page, vous n'avez pas moyen pour l'instant de le récupérer. Pour améliorer l'expérience utilisateur, vous ajouterez une nouvelle action à la tranche de données d'authentification permettant d'enregistrer le jeton de rafraîchissement dans le navigateur.

Dans le « reducer » d'authentification, vous ajouterez une nouvelle action permettant d'initialiser la valeur du booléen mémorisant si l'utilisateur souhaite rester connecté. Vous pourrez ensuite connecter cette action à la case à cocher du formulaire d'authentification afin de connaitre le choix de l'utilisateur.

Ensuite, vous pourrez sauvegarder le choix de l'utilisateur et le jeton de rafraîchissement dans le navigateur, en prenant bien soin de mettre à jour leur valeur lorsque celle-ci change.

Et finalement, vous pourrez initialiser les données du « reducer » d'authentification à partir des données enregistrées dans le navigateur, si celles-ci sont présentes.

Vous modifierez le composant Gate afin qu'il dépende du jeton de rafraîchissement au lieu du JWT devriez maintenant être en mesure de rester connecté à l'application, lors du rechargement de la page ou de la fermeture du navigateur, si vous avez coché la case « se souvenir de moi » à la connection.

Cependant, vous devriez constater que l'utilisateur n'est pas initialisé dans ce cas. Plusieurs solutions sont possibles, l'une d'elles, relativement simple consistera à invoquer l'endpoint de l'utilisateur courant dans le composant Gate. Et d'ajouter un nouveau « matcher » associé à ce endpoint permettant d'initialiser les données de l'utilisateur courant lors de la résolution de la requête.

Travail à réaliser
  • Gérer la case à cocher « se souvenir de moi ».
  • Sauver les données d'authentification dans le navigateur.
  • Initialiser les données d'authentification à partir des données du navigateur si besoin.
  • Gérer l'utilisateur courant, dans le cas d'un rafraîchissement du JWT.
A. Jonquet DUT-INFO/REIMS