Bonnes pratiques pour gérer vos clés d’API avec Kuzzle mobile et React Native

Enfin, voici la 3ème partie de notre série d'articles sur Kuzzle Mobile et React Native :

Cette partie est consacrée aux bonnes pratiques de l'utilisation de Kuzzle et en particulier à la gestion des clés API des utilisateurs.

 

Si vous avez suivi les parties 1 et 2 (ce que je vous encourage vivement à faire), vous devriez avoir une application mobile de messagerie en temps réel qui fonctionne parfaitement ! Mais il y a plus... en effet, vous avez peut-être remarqué que lorsque l'application est redémarrée, il est nécessaire de refaire l'authentification à chaque fois. Troublant, n'est-ce pas ?

 

Aujourd'hui, nous allons donc voir comment générer des clés API et comment faire persister les informations de l'utilisateur actuel avec Kuzzle Mobile, Expo et React en natif.

 

Petit rappel

Dans la première partie de cette série, nous avons créé notre nouveau projet pour notre application mobile, configuré notre pile de backend et utilisé le SDK Kuzzle Javascript pour effectuer la connexion des utilisateurs. Dans la deuxième partie, nous avons mis en œuvre toute la logique du chat en temps réel.

 

Commençons la troisième partie, mais avant cela, pour rappel, voici deux commandes utiles dont nous aurons besoin :

 

Pour lancer la stack Kuzzle :

 

docker-compose up

 

Pour relancer l'application mobile avec Expo :

 

npm start

 

Ces 2 commandes doivent être exécutées à la racine de votre projet.

 

Persistez l'utilisateur courant

Pour conserver les informations actuelles sur l'utilisateur et ne pas être obligé de s'authentifier à nouveau chaque fois que nous lançons l'application, nous devrons procéder à une petite refonte de notre code.

 

Dans le fichier App.js se trouvent les deux lignes où nous déclarons les deux états React JWT et username :

 

const [jwt, setJwt] = useState(null);
const [username, setUsername] = useState(null);

 

Nous pouvons les supprimer tous les deux et les remplacer par une nouvelle déclaration "useState" :

 

const [currentUser, setCurrentUser] = useState(null);

 

C'est donc dans cette variable de notre état que nous allons stocker les informations que nous voulons de l'utilisateur fraîchement connecté.

 

Nous devons maintenant modifier la fonction onLoginSuccess. Ce que nous voulons, c'est créer une nouvelle clé API une fois que l'utilisateur est connecté avec succès.

 

Si vous allez sur le site de documentation Kuzzle et en particulier sur la section Clé API. Vous apprendrez que les clés API ne sont que des jetons d'authentification. Vous pouvez utiliser votre clé API de la même manière que vous utilisez votre jeton d'authentification. La principale différence est qu'il n'y a pas de date d'expiration pour ce jeton.

 

Dans le fichier App.js, localisez la méthode onLoginSuccess et modifiez-la pour faire ce que nous voulons maintenant :

 

const onLoginSuccess = async (username) => {
 try {
   const apiKey = await kuzzle.auth.createApiKey(`${username} API key`);
   const currentUser = {
     username,
     jwt: apiKey._source.token,
     tokenId: apiKey._id
   };
 
   await SecureStore.setItemAsync('persistedUser', JSON.stringify(currentUser));
 
   setCurrentUser(currentUser)
 } catch {
   showToast(
     "danger",
     "Sorry an error occurred during authentication"
   );
 }
};

 

Nous devons maintenant appeler la méthode createApiKey du contrôleur d'authentification du SDK Kuzzle. Nous lui donnons comme paramètre une description de la clé API que nous voulons créer. Cela peut être utile si vous devez gérer différentes clés.

 

Ensuite, nous créons un nouvel objet représentant l'utilisateur actuel : username, jwt et tokenId. Ce dernier est l'identifiant unique, renvoyé par le SDK, de la clé API que nous venons de créer. Nous en aurons besoin dans la partie suivante.

 

Si vous vous souvenez du premier article de cette série. Nous avions installé la bibliothèque "Expo-secure-store" et nous ne l'avons jamais utilisée. C'est maintenant le moment de s'en servir.

Cette bibliothèque permet de chiffrer et de stocker en toute sécurité des paires clé-valeur localement sur l'appareil (vous pouvez en savoir plus ici).


C'est parfait pour nos besoins. Nous pouvons utiliser la méthode SecureStore.setItemAsync pour conserver nos informations d'utilisateur. Celle-ci prend 2 paramètres : la clé à associer à la valeur stockée et la valeur à stocker. Notez que nous devons utiliser JSON.stringify pour convertir notre objet JS en une chaîne de caractères.

 

Si tout se passe bien, il suffit d'appeler la fonction setCurrentUser pour le sauvegarder.

 

Ensuite, localisez la déclaration de hook useEffect qui s'est déclenchée sur JWT et la modification du nom d'utilisateur :


 

useEffect(() => {
 if (jwt && username) {
   setIsLoggedIn(true);
 }
}, [jwt, username]);

 

Et le modifier pour l'activer sur la mise à jour de l'état de l'utilisateur actuel :

 

useEffect(() => {
 if (currentUser) {
   setIsLoggedIn(true);
 }
}, [currentUser]);

 

Et pour finir sur ce fichier, sur la fonction renderApp, changez la condition basée sur l'état comme ceci :

 

if (!isLoadingComplete && isRessourcesLoaded) {
 pageContent = <Spinner />;
} else if (!isLoggedIn) {
 pageContent = <LoginForm onLoginSuccess={onLoginSuccess} />;
} else if (currentUser) {
 pageContent = <Chat currentUsername={currentUser.username} />;
}

 

Ouvrez maintenant le fichier LoginForm.js et trouvez la méthode performLogin. En regardant les modifications précédentes, vous aurez remarqué que nous ne devons plus donner qu'un seul paramètre à la fonction onLoginSuccess. Donc, mettez à jour l'appel de cette fonction dans la méthode performLogin comme ceci :

 

onLoginSuccess(username);

 

C'est tout pour cette première partie de refonte. Maintenant, chaque fois qu'un utilisateur remplit le formulaire de connexion et que l'authentification est réussie, une clé API est alors créée par Kuzzle. Ainsi, nous pouvons conserver dans le store local ainsi que dans l'état de notre application toutes les informations nécessaires de l'utilisateur concerné.

 

Récuperer l'utilisateur courant

Pour le chapitre suivant, nous voulons modifier notre application pour pouvoir récuperer l'utilisateur courant store local du téléphone, s'il y en a un, et aussi, vérifier que la clé API de l'utilisateur est valide avant de continuer avec cet utilisateur.

 

Une fois de plus, allez dans App.js et localisez le hook useState utilisant la variable d'état connected

 

useEffect(() => {
 if (connected) {
   setIsLoadingComplete(true);
 }
}, [connected]);

 

Nous devons mettre à jour la logique de ce hook pour récupérer les informations de l'utilisateur précédemment connecté, puis vérifier si la clé API est valide et enfin si tout va bien, montrer à l'utilisateur le composant de chat, sinon nous devons lui demander de se connecter.

 

Remplacez le code du hook par celui-ci :

 

useEffect(() => {
 if (connected) {
   SecureStore.getItemAsync('persistedUser').then(async (persistedUser) => {
     persistedUser = JSON.parse(persistedUser);
 
     if (await checkTokenValidity(persistedUser.jwt)) {
       kuzzle.jwt = persistedUser.jwt; // set the jwt on the kuzzle SDK property to automatically use it
       setCurrentUser(persistedUser)
     }
 
     setIsLoadingComplete(true);
   }).catch(() => setIsLoadingComplete(true));
 }
}, [connected]);

 

Ce que nous faisons ici est d'appeler la méthode SecureStore.getItemAsync fournie par Expo-secure-store et s'il y a un résultat, nous pouvons essayer de vérifier si le jeton est valide puis si c'est le cas, nous pouvons mettre l'utilisateur actuel sur l'état de l'application.

 

Notez que nous utilisons une promise pour appeler getItemAsync parce que nous ne pouvons pas déclarer un hook asynchrone. S'il n'y a pas de résultat, nous passons dans le cas du catch et nous n'avons pas besoin de montrer une erreur. En fait, nous devons juste continuer l'exécution sans définir les informations actuelles de l'utilisateur.

 

Il faut aussi ajouter une nouvelle fonction dans le composant pour implémenter la vérification de la clé API, la voici :

 

const checkTokenValidity = async (jwt) => {
 try {
   const verifiedToken = await kuzzle.auth.checkToken(jwt);
   return verifiedToken.valid;
 } catch {
   showToast(La première chose à faire est d'ajouter un bouton de déconnexion. Pour ce faire, nous devons importer de nouveaux composants de la bibliothèque Native-base.
     "danger",
     "Sorry an error occurred during user verification"
   );
 }
}

 

Celle-ci appelle la méthode checkToken du contrôleur d'authentification, fournie par le SDK Kuzzle (plus d'info sur cette méthode ici).

 

À ce stade, vous pouvez lancer l'application avec Expo et essayer de vous authentifier. Si vous fermez l'application et la redémarrez, vous serez automatiquement connecté et vous verrez directement l'écran de discussion.

 

Déconnexion

Pour cette étape, nous mettrons en œuvre la logique de déconnexion. Pour permettre aux utilisateurs de se déconnecter de l'application et de supprimer leur clé API.

 

La première chose à faire est d'ajouter un bouton de déconnexion. Pour ce faire, nous devons importer de nouveaux composants de la bibliothèque Native-base.

 

Dans App.js, en haut du fichier, trouvez cette déclaration d'importation :

 

import {
 Root,
 Header,
 Body,
 Title,
 Container,
 Toast,
 Text,
 Spinner,
} from "native-base";

 

Et ajouter les nouveaux composants dont nous avons besoin, pour être précis : Right, Icon et Button

 

import {
 Root,
 Header,
 Body,
 Right,
 Icon,
 Button,
 Title,
 Container,
 Toast,
 Text,
 Spinner,
} from "native-base";

 

Ces trois éléments nous permettront d'ajouter un bouton à l'élément d'en-tête de l'application.

Allez à la fonction renderApp et mettez à jour la déclaration de retour comme ceci :

 

return (
 <Root>
   <Container>
     <Header>
       <Body>
         <Title>Kuzzle Chat</Title>
       </Body>
       {isLoggedIn && <Right>
         <Button transparent onPress={logout}>
           <Icon name='log-out' />
         </Button>
       </Right>}
     </Header>
     <Container padder>{pageContent}</Container>
   </Container>
 </Root>
);

 

Nous avons ajouté un bouton transparent placé à droite avec une icône de déconnexion et, bien sûr, nous ne l'afficherons que si l'utilisateur est connecté.

 

Ensuite, nous devons ajouter une nouvelle fonction, celle qui est appelée lorsque l'utilisateur appuie sur le bouton.

 

const logout = async () => {
 try {
   await SecureStore.deleteItemAsync('persistedUser');
   await kuzzle.auth.deleteApiKey(currentUser.tokenId);
 
   setCurrentUser(null);
   setIsLoggedIn(false);
 } catch {
   showToast(
     "danger",
     "Sorry an error occurred during log-out"
   );
 }
}

 

Ici, nous supprimons l'élément PersistedUser du store Expo, puis nous supprimons la clé API de l'utilisateur avec Kuzzle-SDK et nous réinitialisons également l'état CurrentUser et isLoggedIn de notre application.

 

Lorsque l'utilisateur appuie sur le bouton, il est automatiquement redirigé vers le formulaire de connexion.

 

Une fois de plus, vous pouvez lancer l'application et tester le bouton de déconnexion par vous-même.


 

 

Aller plus loin

Nous disposons maintenant d'une application mobile pleinement fonctionnelle ! Les utilisateurs peuvent s'authentifier, recevoir et envoyer des messages via l'écran de chat et déconnecter leur session. Nous en avons fini avec la mise en œuvre du code et de la logique !

Pour le dernier chapitre, je veux vous donner quelques conseils et outils pour aller plus loin avec Kuzzle Mobile et tout son écosystème.

 

Kourou:

Le CLI qui vous aide à gérer vos instances Kuzzle ! C'est un outil formidable pour administrer votre pile Kuzzle et vous aider tout au long du développement de votre projet. De nombreux membres de l'équipe Kuzzle l'utilisent tous les jours. Nous essayons d'ajouter de nombreuses commandes utiles et vous pouvez voir la liste détaillée et la documentation ici : Kourou sur Github.

 

Plugins Kuzzle:

Nous avons développé une application mobile complète sans coder une seule ligne pour la partie backend. C'est l'une des plus grandes forces de Kuzzle, mais parfois, pour certains projets, vous serez obligé de mettre en œuvre une logique metier du côté du backend. C'est là que le système de plugin entre en jeu !

 


Nous fournissons également des plugins officiels, vous pouvez en consulter la liste complète ici.

 

Conclusion

Ceci complète notre série d'articles sur React Native et Kuzzle Mobile.

Si nécessaire, vous pouvez trouver l'intégralité du code du projet sur ce  dépôt github.

 

Dans ces trois articles, nous avons abordé de nombreuses notions importantes pour le développement d'une application de messagerie mobile en temps réel, comme la configuration et l'installation d'une instance de Kuzzle Mobile via docker et docker-compose, l'utilisation de la console d'administration Kuzzle, la gestion des titres, des utilisateurs et de leurs clés API, la compréhension des mappings Elasticsearch, la création d'un index, d'une collection et de quelques documents et bien sûr comment ajouter des fonctionnalités en temps réel à notre application.

 

J'espère que cela vous a aidé à vous familiariser avec Kuzzle. Une fois de plus, n'hésitez pas à venir sur notre serveur Discord si vous avez des questions ou des suggestions.

 

Nicolas Juelle

Postes associés