Création de l'authentification pour l'utilisateur - Créer un blog avec Adonis

barbapapazes

Estéban

Posted on August 21, 2021

Création de l'authentification pour l'utilisateur - Créer un blog avec Adonis

Bonjour,

Bienvenue dans ce tutoriel pour apprendre à utiliser le framework web Adonis ! Si tu souhaites en savoir plus sur Adonis en 1 coup œil, je t'invite à lire cette page.

Dans cette partie, on va voir le système de routing, de controller et un moyen pour authentifier l'utilisateur.

Rappel

Ce tutoriel est la partie 3 d'une série de tutoriels qui ont pour objectif de te faire découvrir Adonis au travers la création d'un blog.

Pour lire la partie précédente, c'est là Création d'un utilisateur - Créer un blog avec Adonis

Tu trouveras aussi sur GiHub l'ensemble du code source du projet !

Sommaire

Ce tutoriel est découpé en différente partie pour t'aider et pour éviter d'avoir des articles trop longs où l'on pourrait se perdre !

Nous allons donc voir ensemble :

Finalement, tu auras un blog fonctionnel !

Création de l'authentification

Nous allons voir ensemble comment créer une route avec Adonis puis l'ajout d'un controller à cette dernière pour faciliter la gestion des fonctionnalités de notre application.

Création d'une route

Pour commencer à créer des routes, rendons-nous dans le fichier start/routes.ts

Dans un premier temps, on va créer la route permettant d'afficher à l'utilisateur le formulaire pour s'authentifier puis dans un second temps, on fera la route qui permet d'authentifier l'utilisateur.

Dans notre fichier, on va commencer par créer la route /login et s'assurer que tout fonctionne.

Route.get('login', () => {
  return 'Bienvenue sur la page de login'
})
Enter fullscreen mode Exit fullscreen mode

Rends-toi à cette adresse et tu vas voir ce message dans ton navigateur ! N'oublie pas de démarrer le serveur !

Super, mais comment on affiche de l'HTML à notre utilisateur ? Pour cela, on va utiliser le template engine edge. Nous allons donc créer une page puis demander au serveur de la générer puis de l'envoyer à l'utilisateur.

Commençons par créer la vue :

node ace make:view login
Enter fullscreen mode Exit fullscreen mode

Un nouveau fichier vient alors de faire son apparition dans ressources/views. Dans ce fichier, on va y créer un simple formulaire permettant à l'utilisateur de renseigner ses identifiants pour se connecter :

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Login</title>
</head>
<body>
  <form action="/login" method="post">
    <div>
      <label for="pseudo">Votre pseudo</label>
      <input type="text" name="pseudo" id="pseudo">
    </div>
    <div>
      <label for="password">Votre password</label>
      <input type="text" name="password" id="password">
    </div>
    <button type="submit">Se connecter</button>
  </form>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

L'action de ce formulaire nous indique que la requête sera sur login et la méthode nous indique POST. C'est donc comme cela que nous allons devoir définir nos routes !

Ensuite, nous allons indiquer à notre route de rendre cette page puis de la renvoyer à l'utilisateur. Changeons un peu notre code dans le fichier start/routes.ts :

Route.get('login', ({ view }) => {
  return view.render('login')
})
Enter fullscreen mode Exit fullscreen mode

Rends-toi à cette adresse pour visualiser notre formulaire de connexion !

Pour en savoir plus : Routing, Controllers, Edge

Authentifier un utilisateur

Installation du module

Pour commencer à authentifier notre utilisateur, on va devoir installer et configurer un module :

npm i @adonisjs/auth
Enter fullscreen mode Exit fullscreen mode

Puis

node ace configure @adonisjs/auth
Enter fullscreen mode Exit fullscreen mode

On indique que l'on souhaite utiliser Lucid pour trouver les utilisateurs. Ensuite, on va utiliser le web guard pour gérer l'authentification et enfin, on indique User comme modèle pour l'authentification. Puis on indique ne pas vouloir créer une migration comme cela a déjà été fait dans la partie précédente.

Dans le fichier config/auth.ts, il faut changer l'uids qui est à email par pseudo. En effet, dans la migration de notre utilisateur, nous avons indiqué que le pseudo devait être unique. Cela va permettre de le récupérer en base de données lors de l'authentification.

Authentification

Dans nos routes, nous allons créer une nouvelle route qui correspond à ce que l'on a mis dans le formulaire d'authentification.

Route.post('login',async ({ request, auth, response }) => {
    return 'Post sur login'
})
Enter fullscreen mode Exit fullscreen mode

Dans cette fonction, nous allons devoir récupérer de la requête le pseudo et le mot de passe provenant du formulaire. Puis, nous allons devoir authentifier l'utilisateur à l'aide de ses identifiants. Si les identifiants sont bons, alors on redirige l'utilisateur vers la page principale. Cependant, si les identifiants ne sont pas bons, alors on l'indique à l'utilisateur.

On récupère les entrées de l'utilisateur :

const pseudo = request.input('pseudo')
const password = request.input('password')
Enter fullscreen mode Exit fullscreen mode

Ensuite, nous essayons d'authentifier l'utilisateur. Dépendant du résultat, on redirige l'utilisateur sur / ou il est renvoyé sur la page login :

try {
    await auth.attempt(pseudo, password)
    response.redirect('/')
} catch (error) {
    response.redirect().back()
}
Enter fullscreen mode Exit fullscreen mode

Rends-toi à cette adresse pour tester ! Pour rappel, notre unique utilisateur a pour pseudo demo et pour mot de passe azerty ! Tout se passe bien et tu es redirigé sur la page d'accueil. Cependant, si tu commets une erreur délibérée sur le mot de passe, alors rien ne te signale ce qui ne vas pas. Pas pratique !

Aussi, rien ne nous assure que les données entrées par l'utilisateur soient correctes. Ainsi, nous allons vérifier en amont ces données pour nous assurer de leur forme, type, format et intégrité.

Aussi, nous aimerions prévenir l'utilisateur des raisons qui peuvent faire qu'il n'arrive pas à s'authentifier ! Et pour cela, on va utiliser les sessions.

Et pour cela, nous allons utiliser un Validator.

Validation des entrées

Commençons par créer le validator :

node ace make:validator login
Enter fullscreen mode Exit fullscreen mode

Ensuite, nous allons définir dans ce dernier la structure et le format des données que l'on souhaite avoir :

public schema = schema.create({
  pseudo: schema.string({ trim: true }, [
    rules.exists({ table: 'users', column: 'pseudo' }),
  ]),
  password: schema.string(),
})
Enter fullscreen mode Exit fullscreen mode

On indique que l'on souhaite une chaine de caractères nommée pseudo dont la valeur doit correspondre à une des entrées de la table users de la colonne pseudo. On indique que l'on souhaite une seconde chaine de caractères nommée password.

Parfait, essayons à nouveau de nous connecter mais en omettant le pseudo ! On n'est pas authentifié mais rien n'a vraiment changé sur la page pour l'utilisateur ! Et c'est normal ! Il faut maintenant indiquer à notre controller d'utiliser le validator :

- const pseudo = request.input('pseudo')
- const password = request.input('password')
---
+ import LoginValidator from 'App/Validators/LoginValidator'
//...
+ const { pseudo, password } = await request.validate(LoginValidator)
Enter fullscreen mode Exit fullscreen mode

Ensuite, nous allons devoir donner à l'utilisateur des indications en fonction des erreurs qu'il commet !

Flash messages

Ce qui est pratique avec le validator, c'est qu'il gère pour nous les messages d'erreurs. Cependant, nous allons devoir les afficher dans la vue. Pour cela, ouvrons le fichier login.edge :

 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>Login</title>
 </head>
 <body>
   <form action="/login" method="post">
     <div>
       <label for="pseudo">Votre pseudo</label>
+      <input type="text" name="pseudo" id="pseudo" value="{{ flashMessages.get('pseudo') ?? '' }}">
+      @if(flashMessages.has('errors.pseudo'))
+      <div>{{ flashMessages.get('errors.pseudo') }}</div>
+      @endif
     </div>
     <div>
       <label for="password">Votre password</label>
+      <input type="text" name="password" id="password" value="{{ flashMessages.get('password') ?? '' }}">
+      @if(flashMessages.has('errors.password'))
+      <div>{{ flashMessages.get('errors.password') }}</div>
+      @endif
     </div>
     <button type="submit">Se connecter</button>
   </form>
 </body>
 </html>
Enter fullscreen mode Exit fullscreen mode

Comme on peut le voir, on ajoute à notre template une div qui contient le message contenu dans la session via flashMessages. Cependant, on ne souhaite faire cela que s'il y a un message à afficher, c'est pour cela qu'on entoure cette div de la condition de présence d'une erreur.

Aussi, on affecte à l'input sa précédente valeur. Cela permet de ne pas vider le formulaire en cas d'erreur de l'utilisateur lorsque nous le redirigeons via la méthode back.

Mais que faire en cas d'erreur dans l'authentification ? Pour cela, nous allons nous même enregistrer un message flash dans la fonction store du controller login.

 try {
   await auth.attempt(pseudo, password)
   response.redirect('/')
 } catch (error) {
+  session.flash('auth', 'Authentication impossible')
   response.redirect().back()
 }
Enter fullscreen mode Exit fullscreen mode

On crée un nouveau message nommé "auth" qui contient "Authentification impossible".

Pour que l'utilisateur soit en mesure de le voir, il nous faut faire comme dans le fichier login.edge :

 <button type="submit">Se connecter</button>
+@if(flashMessages.has('auth'))
+<div>{{ flashMessages.get('auth') }}</div>
+@endif
Enter fullscreen mode Exit fullscreen mode

Rends-toi à cette adresse pour essayer cela ! N'oublie pas de te tromper pour voir apparaitre les messages !

On remarque cependant que les messages du validator ne sont pas en français ! Pour les personnaliser, on peut se rendre dans le fichier LoginValidator.ts :

public messages = {
  'pseudo.string': 'Le pseudo doit être une chaîne de caractères',
  'pseudo.required': 'Le pseudo est requis',
  'pseudo.exists': "Le pseudo n'existe pas",
  'password.string': 'Le mot de passe doit être une chaîne de caractères',
  'password.required': 'Le mot de passe est requis',
}
Enter fullscreen mode Exit fullscreen mode

Les clés sont une correspondance avec la règle qui est appliquée et la vérification qui est effectuée. Retentons notre formulaire et voilà, les messages sont en français !

Pour en savoir plus : Authentication, Web guard, Validator, Flash, Conditionals

Création des controllers

Les fonctions qui se trouvent après le nom de nos routes dans le fichier start/routes.ts est en réalité des controllers. C'est à dire que ces fonctions vont gérer les requêtes des clients. Mais pour simplifier et faire que chaque fichier est son utilité, on préfère les extraire et les mettre dans un fichier à part.

Commençons par créer un controller :

node ace make:controller login
Enter fullscreen mode Exit fullscreen mode

Ensuite, nous allons devoir migrer nos 2 fonctions dans ce controller de la ressource login.

Pour ce faire, commençons par éditer le fichier LoginController.ts.

Dans une fonction nommée create, nous allons y mettre la gestion de la vue et dans une fonction nommée store, nous allons y mettre l'authentification de l'utilisateur. Le choix du nom de ces fonctions n'est pas au hasard et fait partie des conventions utilisées par Adonis, plus de détails ici.

// Gestion de la vue pour l'utilisateur
public async create({ view }: HttpContextContract) {
  return view.render('login')
}

// Gestion de l'authentification
public async store({ request, auth, response, session }: HttpContextContract) {
  const { pseudo, password } = await request.validate(LoginValidator)

  try {
    await auth.use('web').attempt(pseudo, password)
    response.redirect('/articles')
  } catch (error) {
    session.flash('auth', 'Authentication impossible')
    response.redirect().back()
  }
}
Enter fullscreen mode Exit fullscreen mode

Ensuite, nous allons indiquer simplement dans notre fichier de gestion des routes, quel est le controller qui gère la route. Pour cela, nous allons remplacer nos fonctions par une simple chaine de caractères.

Route.get('login', 'LoginController.create')
Route.post('login', 'LoginController.store')
Enter fullscreen mode Exit fullscreen mode

Cette chaine de caractères indique le nom du controller à utiliser LoginController et la fonction à exécuter, create dans le cas d'une requête de type GET et store dans le cas d'une requête POST.

Pour en savoir plus : Controllers

Conclusion

Et voilà pour cette troisième partie. On a vu la création d'une route, de son controller associé. On a aussi abordé les notions de vue et d'authentification de l'utilisateur.

Dans la suite, on va commencer à modeler nos articles pour les visionner sur notre blog !

N'hésite pas à commenter si tu as des questions, si ça t'a plus ou même pour me faire des retours !

Et tu peux aussi me retrouver sur Twitter ou sur LinkedIn !

On se donne rendez-vous ici, Création et visualisation des articles - Créer un blog avec Adonis pour la suite du tutoriel et visualiser nos premier articles.

💖 💪 🙅 🚩
barbapapazes
Estéban

Posted on August 21, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related