Catégories
JavaScript Vue.js

Nuxt.js + Adonis.js / Chapitre 06 : Authentification avec adonis.js et nuxt.js

(Article mis à jour le 10 novembre 2019)

Dans ce nouveau chapitre nous allons aborder la partie authentification de notre application. Nous y verrons comment s’enregistrer sur notre site Meet The Coders, comment se connecter, comment se déconnecter et nous mettrons également en place un début de page profil protégé par l’authentification. Nous verrons donc deux aspects, l’authentification avec adonis.js et l’authentification avec nuxt.jseron.

Sommaire du tutoriel

Mise en conformité Nuxt.js

Avant de commencer ce nouvel article, nous allons améliorer le système de navigation en le rendant beaucoup plus fluide.

En effet, Nuxt.js, lorsque on lit la documentation, préconise d’utiliser le composant nuxt-link pour gérer les liens dans nos applications.

De ce fait, nous allons tout de suite corriger notre fichier main.vue qui gère le menu de notre application afin d’utiliser ce composant nuxt-link.

En conséquence, on remplace le code suivant :

      <nav>
        <ul>
          <li><a href="/">Coders</a></li>
          <li><a href="/languages">Languages</a></li>
          <li><a href="/auth/login">Login</a></li>
          <li><a href="/auth/signup">Signup</a></li>
        </ul>
      </nav>

par le suivant :

      <nav>
        <ul>
          <li>
            <nuxt-link to="/">
              Coders
            </nuxt-link>
          </li>
          <li>
            <nuxt-link to="/languages">
              Languages
            </nuxt-link>
          </li>
          <li>
            <nuxt-link to="/auth/login">
              Login
            </nuxt-link>
          </li>
          <li>
            <nuxt-link to="/auth/signup">
              Signup
            </nuxt-link>
          </li>
        </ul>
      </nav>

C’est un petit peu plus verbeux qu’avant (euphémisme 😉 ) mais c’est à cause de l’es-lint qui m’a obligé (si, si je vous jure) à mettre des sauts de lignes un peu partout.

Il faut savoir qu’à partir de la version 2.4 de Nuxt.js, le composant nuxt-link permet d’aller précharger les contenus des liens visible dans votre navigateur lorsque ce dernier est au repos ou qu’en tout cas il ne consomme pas beaucoup de ressources. Et c’est bluffant !!

Vous pouvez faire le test en chargeant vos différentes pages avec vos balises a href classique. Ensuite, faites le test avec le composant nuxt-link. La différence de chargement doit être au moins équivalente à celle qu’il doit y avoir entre le démarrage d’une Tesla modèle P100D et la Cobra ! Pour la Cobra ça pique, on dirait une deuche !!!!

Mettre en place l’authentification avec adonis.js et nuxt.js

La mise en place de l’authentification reprends des éléments du tutoriel « Implementing authentication in a Nuxt.js App« .

La philosophie de cette authentification à double tête (adonis.js et nuxt.js)

Comme vous le savez, notre application est portée par deux composants principaux.

  • le frontend via nuxt.js
  • le backend via adonis.js

Dans la mise en place de cette authentification, le frontend aura la responsabilité de l’affichage des formulaires (login, register).

Le backend, quant à lui, recevra les demandes en provenance du frontend d’authentification et renverra, toujours au frontend, les données nécessaires.

Chacun son rôle !

Le backend aura deux points d’entrées principaux dans son api pour l’authentification : login et register.

Mise en place de la partie authentification avec adonis.js côté backend

Sur votre serveur Adonis.js (côté backend donc, vous me suivez ?), voici les fichiers que vous allez devoir mettre en place pour initier l’authentification avec Adonis.js.

Authentification avec adonis.js : Auth/AuthenticationController.js

Tout d’abord, l’authenticationController :

adonis make:controller Auth/AuthenticationController

N’oubliez pas de choisir ‘http‘ lorsque Adonis vous le demandera.

Dans ce fichier nouvellement créé, veuillez remplacer son contenu par celui-ci :

'use strict'

const User = use('App/Models/User')

class AuthenticationController {
  async register ({ request, auth, response }) {
    const userData = request.only(['username', 'email', 'password'])

    try {
      const user = await User.create(userData)

      const token = await auth.generate(user)

      return response.json({
        status: 'success',
        data: token
      })
    } catch (error) {
      return response.status(400).json({
        status: 'error',
        message: 'There was a problem creating the user, please try again later.'
      })
    }
  }

  async login ({ request, auth, response }) {
    const { email, password } = request.only(['email', 'password'])

    try {
      const token = await auth.attempt(email, password)

      return response.json({
        status: 'success',
        data: token
      })
    } catch (error) {
      response.status(400).json({
        status: 'error',
        message: 'Invalid email/password.'
      })
    }
  }

  async me ({ auth, response }) {
    return response.json({
      status: 'success',
      data: auth.user
    })
  }
}

module.exports = AuthenticationController

Authentification avec adonis.js : start/routes.js

Vous allez ensuite modifier le fichier start/routes.js en y ajoutant les routes liées à l’authentification :

Route.group(() => {
  Route.post('login', 'Auth/AuthenticationController.login')
  Route.post('register', 'Auth/AuthenticationController.register')
  Route.get('me', 'Auth/AuthenticationController.me').middleware(['auth'])
}).prefix('api')

Lors du remplissage des formulaires login et register sur la partie frontend, les données d’authentification saisies seront envoyés via POST à notre serveur backend sous Adonis.js.

Ce dernier pourra alors rediriger ces appels vers notre controller Auth/AuthenticationController.js qui utilisera les fonctions dédiées ‘login‘ ou ‘register‘.

Vous pouvez à présent démarrer votre serveur backend via la commande en ligne de commande :

npm start

Ce sera tout pour la partie backend en ce qui concerne l’authentification avec adonis.js.

On passe à présent à la partie frontend et attention ça va décoiffer 😀 !

Ajouter le paquet nuxtjs/auth côté frontend

Dans le but de pouvoir mettre en place notre authentification, il faut commencer par installer le paquet nuxtjs/auth.

Pour cela, un petit passage par notre ligne de commande :

npm install @nuxtjs/auth --save

Il convient ensuite de vérifier que les modules sont bien chargés dans la configuration de nuxt. Pour cela, on édite le fichier nuxt.config.js et on s’assure de la présence des modules nécessaires :

  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    '@nuxtjs/auth'
  ],

Il est nécessaire que le module auth et le module axios soient bien présents pour que tout fonctionne correctement.

La gestion des fichiers d’environnements

Dites, vous vous rappelez sans doute que je vous ai dit qu’un développeur se doit d’être fainéant ? Oui ? Un autre aspect de notre fainéantise va être de créer des fichiers d’environnements pour que le programme sache tout seul quelles valeurs de configuration donner en fonction de l’environnement cible de notre programme (dev, preprod, prod).

On réalise cela avec des fichiers ‘.env’ (dotenv en anglais). Evidemment pour pouvoir utiliser ces fichiers, il va falloir indiquer à Nuxt.js que l’on souhaites les utiliser.

Il suffira donc de lancer l’installation du module dotenv dédié pour Nuxt.js :

yarn add @nuxtjs/dotenv

ou si vous préférez npm :

npm install @nuxtjs/dotenv

Il vous est, dès lors, possible de créer un fichier .env à la racine de votre site et de commencer à y enregistrer vos configurations :

HOST=127.0.0.1
PORT=3000
API_PORT=3333
NODE_ENV=development
APP_URL=http://127.0.0.1:3000
API_URL=http://127.0.0.1:3333/api

Pour pouvoir exploiter votre nouveau fichier .env, veuillez ajouter la ligne suivante en tout début du fichier nuxt.config.js, :

require('dotenv').config()

Nota : si vous rencontrez quelques difficultés d’utilisation de votre fichier .env, nous pouvez passer le module dotenv en mode debug en remplaçant la ligne que nous venons d’ajouter dans nuxt.config.js par celle-ci : require('dotenv').config({ debug: true })

Configuration des modules ‘axios’ et ‘auth’

Bon à présent que les modules sont ajoutés, ils ne vont pas fonctionner sans que l’on leur dise quoi faire exactement.

Et oui, vous l’avez deviné, nous allons les configurer.

On reste donc dans le fichier nuxt.config.js et on y ajoute la configuration suivante :

  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {
    baseURL: process.env.API_URL
  },
  auth: {
    strategies: {
      local: {
        endpoints: {
          login: { url: 'login', method: 'post', propertyName: 'data.token' },
          user: { url: 'me', method: 'get', propertyName: 'data' },
          logout: false
        }
      }
    }
  },

Modifier le menu pour intégrer l’authentification

Ici vous allez remplacer notre fichier layouts/main.vue, construit dans le chapitre n°2 par celui que je vous livre ci-dessous.

<template>
  <div>
    <nav class="navbar is-primary">
      <div id="logo" class="navbar-brand">
        <a class="navbar-item" href="/">
          Meet The Coders
        </a>
        <div class="navbar-burger burger" data-target="navbarExampleTransparentExample">
          <span />
          <span />
          <span />
        </div>
      </div>

      <div id="navbarExampleTransparentExample" class="navbar-menu">
        <div class="navbar-start">
          <a class="navbar-item" href="/">
            Home
          </a>
          <a class="navbar-item" href="/languages">
            Languages
          </a>
        </div>

        <div class="navbar-end">
          <div v-if="isAuthenticated" class="navbar-item has-dropdown is-hoverable">
            <a class="navbar-link" href="#">
              {{ loggedInUser.username }}
            </a>
            <div class="navbar-dropdown">
              <a class="navbar-item" href="/me">
                Profile
              </a>
              <a class="navbar-item" @click="logout">
                Logout
              </a>
            </div>
          </div>
          <template v-else>
            <a class="navbar-item" href="/register">
              Register
            </a>
            <a class="navbar-item" href="/login">
              Login
            </a>
          </template>
        </div>
      </div>
    </nav>
    <nuxt />
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters(['isAuthenticated', 'loggedInUser'])
  },
  methods: {
    async logout () {
      await this.$auth.logout()
    }
  }
}
</script>

<style>
@import url('https://fonts.googleapis.com/css?family=Montserrat:300,700');
html,
body {
  height: 100%;
  width: 100%;
}
body {
  font-family: 'Montserrat', sans-serif;
  font-weight: 300;
  background-color: #fff;
}
* {
  margin: 0;
  padding: 0;
}
ul {
  list-style-type: none;
}
header,
.navbar {
  display: grid;
  grid-template-columns: 30% auto;
}
header a {
  color: white;
  text-decoration: none;
  text-transform: uppercase;
}
#logo {
  font-weight: bold;
  font-size: 1.3em;
  padding: 1.5em 0 2em 0;
  margin: 0;
}
#logo a:hover {
  color: #fff;
}

nav {
  justify-self: right;
}
nav {
  display: inline;
}
nav a {
  padding: 2em;
  display: inline-block;
}
.container {
  width: calc(100% - 4em);
  padding: 2em;
  margin-top: 2em;
}
h1 {
  margin-bottom: 1em;
}

.coder-container,
.coder-container2 {
  background: white;
  border-radius: 7px;
  padding: 1em;
  margin-bottom: 10px;
  display: grid;
  grid-template-columns: 120px auto;
  justify-content: left;
  text-align: left;
  background-color: rgba(65, 184, 131, 0.1);
}
.blank {
  width: 100px;
  height: 100px;
  border: 1px solid lightgray;
  border-radius: 5px;
}
a:hover {
  background: #41b883;
  color: #fff;
}
.navbar-item,
.navbar-link {
  color: #fff;
  font-weight: 700;
}
.navbar-dropdown {
  background-color: #00B89C;
}
#logo:hover {
  background-color: #00B89C;
  color: #fff;
}
</style>

Si tout s’est bien passé, vous devriez obtenir quelque chose de similaire à ceci :

Authentification : intégration du nouveau menu

Enregistrement des nouveaux utilisateurs : register

Nous souhaitons pouvoir enregistrer de nouveaux utilisateurs dans notre système. C’est là que nos codeurs pourront commencer leurs aventures et faire connaître au monde entier leurs skills dans différents languages.

Pour cela, vous allez créer un nouveau fichier register.vue dans le répertoire pages et vous y insérerez le code suivant :

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-4 is-offset-4">
          <h2 class="title has-text-centered">
            Register!
          </h2>

          <Notification v-if="error" :message="error" />

          <form method="post" @submit.prevent="register">
            <div class="field">
              <label class="label">Username</label>
              <div class="control">
                <input
                  v-model="username"
                  type="text"
                  class="input"
                  name="username"
                  required
                >
              </div>
            </div>
            <div class="field">
              <label class="label">Email</label>
              <div class="control">
                <input
                  v-model="email"
                  type="email"
                  class="input"
                  name="email"
                  required
                >
              </div>
            </div>
            <div class="field">
              <label class="label">Password</label>
              <div class="control">
                <input
                  v-model="password"
                  type="password"
                  class="input"
                  name="password"
                  required
                >
              </div>
            </div>
            <div class="control">
              <button type="submit" class="button is-dark is-fullwidth">
                Register
              </button>
            </div>
          </form>

          <div class="has-text-centered" style="margin-top: 20px">
            Already got an account? <nuxt-link to="/login">
              Login
            </nuxt-link>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
  layout: 'main',
  middleware: 'guest',
  components: {
    Notification
  },

  data () {
    return {
      username: '',
      email: '',
      password: '',
      error: null
    }
  },

  methods: {
    async register () {
      try {
        await this.$axios.post('register', {
          username: this.username,
          email: this.email,
          password: this.password
        })

        await this.$auth.loginWith('local', {
          data: {
            email: this.email,
            password: this.password
          }
        })

        this.$router.push('/')
      } catch (e) {
        this.error = e.response.data.message
      }
    }
  }
}
</script>

Système de notifications

Afin d’informer l’utilisateur d’une erreur ou d’un problème, il est important de disposer d’un système de notifications.

C’est ce que nous allons mettre en place à présent avant de tester l’enregistrement de nos nouveaux utilisateurs.

Pour cela, nous créer un composant Notification. Créer un nouveau fichier Notification.vue dans le répertoire components et ajoutez y le code suivant :

<template>
  <div class="notification is-danger">
    {{ message }}
  </div>
</template>

<script>
export default {
  name: 'Notification',
  props: {
    message: {
      type: String,
      default: ''
    }
  }

}
</script>

Enregistrer de nouveaux utilisateurs

Un petit tour à l’adresse http://localhost:3000/register vous amènera sur la page suivante :

Essayez d’enregistrer un nouvel utilisateur.

Par exemple :

  • username : Deadpool
  • email : deadpool@myemailprovider.space
  • password : 1234 😀

Cela fonctionne :

Retour à la page d’accueil en étant authentifié

Le système a créé notre nouvel utilisateur et l’a connecté directement dans la foulée.

Si on vérifie en base de données, l’utilisateur Deadpool a bien été créée :

A présent si vous cliquez sur logout et que vous cherchez à nouveau à enregister le même user, vous aurez un message d’erreur via le système de notifications que nous avons mis en place un peu plus haut :

Savoir si un utilisateur est connecté au système ou pas

Afin de s’assurer de l’état de connexion d’un utilisateur, nous allons mettre en place des getters dans le store de Vuex.

Pour cela, créer un fichier index.js dans le répertoire store.

Ajoutez à ce fichier le code suivant :

export const getters = {
  isAuthenticated (state) {
    return state.auth.loggedIn
  },

  loggedInUser (state) {
    return state.auth.user
  }
}

C’est grâce à ces getters que l’on peut personnaliser la barre de menu du fichier main.vue avec des v-if sur la fonction isAuthenticated.

Gestion du formulaire de login

Nous allons voir à présent la mise en place du formulaire de login. Il reprends les grandes lignes du formulaires register. Créez un fichier login.vue dans le répertoire pages de l’arborescence de votre frontend. Ajoutez y le code suivant :

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-10 is-offset-1">
          <h2 id="login-welcome-back" class="is-size-2 has-text-centered has-text-primary has-text-weight-bold">
            Welcome back!
          </h2>

          <Notification v-if="error" :message="error" />

          <form method="post" @submit.prevent="login">
            <div class="field">
              <label class="label">Email</label>
              <div class="control">
                <input
                  v-model="email"
                  type="email"
                  class="input"
                  name="email"
                >
              </div>
            </div>
            <div class="field">
              <label class="label">Password</label>
              <div class="control">
                <input
                  v-model="password"
                  type="password"
                  class="input"
                  name="password"
                >
              </div>
            </div>
            <div class="control">
              <button type="submit" class="button is-info is-fullwidth">
                Log In
              </button>
            </div>
          </form>
          <div class="has-text-centered" style="margin-top: 20px">
            <p>
              Don't have an account? <nuxt-link to="/register">
                Register
              </nuxt-link>
            </p>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
  layout: 'main',
  middleware: 'guest',
  components: {
    Notification
  },

  data () {
    return {
      email: '',
      password: '',
      error: null
    }
  },

  methods: {
    async login () {
      try {
        await this.$auth.loginWith('local', {
          data: {
            email: this.email,
            password: this.password
          }
        })

        this.$router.push('/')
      } catch (e) {
        this.error = e.response.data.message
      }
    }
  }
}
</script>

<style>
  #login-welcome-back {
    padding-bottom: 1.2rem;
  }
</style>

Vous trouvez cet article intéressant ?

Ecrire ces articles est passionnant 😉, aussi je souhaite y consacrer plus de temps et votre soutien est capital pour cela.
Si vous souhaitez m’encourager et récompenser mon travail, vous pouvez me payer une bière 🍺 ou tout autre montant de votre choix *
Mille mercis 🙏 et maintenant la suite de cet article.
Bonne journée !

* Pas de panique : Le paiement est sécurisé par Paypal

Afficher le profil de l’utilisateur

Pour commencer nous allons faire très simple et afficher uniquement des informations basiques sur l’utilisateur connecté.

Pour cela, créer une nouvelle page ‘me.vue’ dans le répertoire pages.

<template>
  <section class="section">
    <div class="container">
      <h2 class="title">
        My Profile
      </h2>
      <div class="content">
        <p>
          <strong>Username:</strong>
          {{ loggedInUser.username }}
        </p>
        <p>
          <strong>Email:</strong>
          {{ loggedInUser.email }}
        </p>
      </div>
    </div>
  </section>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  layout: 'main',
  middleware: 'auth',
  computed: {
    ...mapGetters(['loggedInUser'])
  }
}
</script>

Se déconnecter de l’application

Dans le fichier main.vue, la partie logout est gérée par la ligne suivante :

              <a class="navbar-item" @click="logout">
                Logout
              </a

Conclusion sur l’authentification avec adonis.js et nuxt.js

Cet article nous a permis de mettre en place un système complet d’authentification dans notre application Meet The Coders.

Vous pouvez retrouver les sources de notre projet sur le github dédié au frontend.

Et si vous souhaitez approfondir vos connaissances sur Adonis.js, vous pouvez vous jeter à corps perdu dans ce livre :

mais si vous préférez approfondir vos connaissances en Vue.js 2 et ses patterns tout en abordant Nuxt.js, je vous conseillerai plutôt celui-ci :

Aidez nous à nous améliorer en évaluant cet article :
[Total: 0 Moyenne: 0]

Par Aldew

Absolument passionné depuis toujours par l'informatique et la programmation, je souhaite via ce blog vous faire passer un peu du savoir que j'ai accumulé depuis des années.
Et même, pourquoi pas, vous aider à faire le premier pas dans le développement web.

Vous êtes prêts ?
Alors allons y ensemble et n'oubliez pas que les débutants sont vraiment les bienvenus.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.