Laravel 9

Cours Laravel 9 – la sécurité – l’authentification

L’authentification constitue une tâche fréquente. En effet il y a souvent des parties d’un site qui ne doivent être accessibles qu’à certains utilisateurs, ne serait-ce que l’administration. La solution proposée par Laravel est d’une grande simplicité parce que tout est déjà préparé comme nous allons le voir dans ce chapitre.

Au fil de l’avolution de Laravel ce domaine a connu plusieurs fois des changements. Ainsi avec Laravel 6 est apparue une nouveauté. Précédemment il y avait la commande php artisan make:auth pour générer les vues. Cette commande a disparu au profit d’un package à installer (laravel/ui). Mais les changements ont continué parce que désormais on nous dit d’utiliser un starter kit avec comme choix Breeze ou Jetstream.

En résumé pour l’authentification on a ces possibilités :

  • Utiliser laravel/breeze qui utilise Tailwind, c’est le sujet du présent article
  • Utiliser Jetstream, mais à ce niveau on a deux possibilités :
    • Livewire
    • Inertia (ça concerne surtout ceux qui veulent s’orienter sur une SPA)
  • Utiliser Fortify en créant soi-même les vues et accessoirement le traitement de fonctionnalités complémentaires

Je sais que le débutant peut trouver cette situation un peu confuse mais c’est comme ça ! L’option la plus simple est certainement d’installer laravel/breeze. Le code est simple et lisible et facilement accessible et modifiable.

Quelques principes

Comment fonctionne une authentification ? En général l’utilisateur utilise un navigateur, renseigne son nom et son mot de passe dans un formulaire, ces informations sont reçues par le serveur, vérifiées, et si tout est bon on mémorise cette authentification en session. Cette session possède un identifiant qui est transmis à chaque requête pour éviter d’avoir à s’authentifier à chaque fois. Lorsqu’une requête HTTP arrive avec un identifiant de session l’application considère l’utilisateur correspondant comme authentifié. Ce principe ne s’applique évidemment plus dans le cas d’une API parce qu’on ne dispose pas de session et aucune persistance et on utilise alors un token pour chaque requête.

Laravel est équipé de tout ce qui est nécessaire pour cette authentification au travers des façades Auth et Session. La gestion du cookie est automatisée. D’autre part on dispose de nombreuses méthodes pratiques, par exemple pour retrouver facilement les renseignements sur l’utilisateur authentifié.

On peut très bien utiliser cette infrastructure pour construire sois-même un système complet d’authentification mais c’est assez laborieux. Il est beaucoup plus simple d’utiliser les starter kits.

Laravel Fortify gère les outils d’authentification de Laravel sans se soucier de la partie frontend. Autrement dit lorsqu’on utilise Fortify on n’a aucune vue. Si on veut quelque chose de complet il faut aussi utiliser Jetstream qui est un ensemble de vues qui utilisent l’intendance de Fortify. Mais dans ce cas on se voit imposer un certain nombre de technologies : Tailwind CSS, Laravel Livewire, et/ou Inertia.js. Alors vous avez le choix, soit vous passez par ces technologie modernes et un peu dépaysantes, soit vous vous contentez de Fortify et vous constituez vos vues avec la librairie CSS que vous aimez, soit vous utilisez Breeze.

Dans cet article je vais présenter l’authentification en utilisant Breeze.

La base de données

Par défaut la partie persistance de l’authentification (c’est à dire la manière de retrouver les renseignements des utilisateurs) avec Laravel se fait en base de données avec Eloquent et part du principe qu’il y a un modèle App\Models\User (dans le dossier app).

Lors de l’installation on a vu dans les chapitres précédents qu’il existe déjà des migrations :

Repartez d’une installation vierge et faites la migration avec Artisan :

Vous devriez normalement obtenir ces tables :

Je rappelle que la table migrations sert seulement d’intendance pour les migrations et que vous ne devez pas y toucher.

  • table users : par défaut Laravel considère que cette table existe et il s’en sert comme référence pour l’authentification.
  • table password_resets : cette table va nous servir pour la réinitialisation des mots de passe.

Les middlewares

Je vous ai déjà parlé des middlewares qui servent à effectuer un traitement à l’arrivée (ou au départ) des requêtes HTTP. On a vu le middleware de groupe web qui intervient pour toutes les requêtes qui arrivent. Dans ce chapitre on va voir deux autres middlewares qui vont nous permettre de savoir si un utilisateur est authentifié ou pas pour agir en conséquence.

Middleware auth

Le middleware auth permet de n’autoriser l’accès qu’aux utilisateurs authentifiés. Ce middleware est déjà présent et déclaré dans app\Http\Kernel.php :

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    ...
];

On voit que la classe se trouve dans l’application :

On peut utiliser ce middleware directement sur une route :

Route::get('comptes', function() {
    // Réservé aux utilisateurs authentifiés
})->middleware('auth');

Ou un groupe de routes :

Route::middleware('auth')->group(function () {
    Route::get('/', function ()    {
        // Réservé aux utilisateurs authentifiés
    });
    Route::get('comptes', function () {
        // Réservé aux utilisateurs authentifiés
    });
});

Le groupement de routes avec la méthode group permet de mutualiser des actions et de simplifier le code.

Ou dans le constructeur d’un contrôleur :

public function __construct()
{
    $this->middleware('auth');
}

Dans ce cas on peut désigner les méthodes concernées avec only ou non concernées avec except :

public function __construct()
{
    $this->middleware('auth')->only(['create', 'update']);
}

Middleware guest

Ce middleware est exactement l’inverse du précédent : il permet de n’autoriser l’accès qu’aux utilisateurs non authentifiés. Ce middleware est aussi déjà présent et déclaré dans app\Http\Kernel.php :

protected $routeMiddleware = [
    ...
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    ...
];

La classe se trouve aussi dans l’application :

De la même manière que auth, le middleware guest, comme d’ailleurs tous les middlewares, peut s’utiliser sur une route, un groupe de routes ou dans le constructeur d’un contrôleur, avec la même syntaxe.

Les routes de l’authentification

Dans l’installation de base vous ne trouvez aucune route pour l’authentification. Pour les créer (et ça ne créera pas seulement les routes) il faut déjà installer un package :

composer require laravel/breeze --dev

Une fois que le package est installé vous pouvez lancer cette commande :

php artisan breeze:install

C’est avec cette commande que vont être créés les routes, vues, controlleurs…

Il ne reste plus qu’à compiler les assets pour générer le CSS :

npm install
npm run dev

Regardez ce qui a été ajouté dans le fichier routes/web.php :

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth'])->name('dashboard');

require __DIR__.'/auth.php';

On a un fichier routes/auth.php bien garni.

Voyons quelles sont les routes générées :

Nous allons voir bientôt tout ça en action.

Les vues de l’authentification

Il y a également eu la génération de nombreuses vues :

On va aussi voir à quoi servent toutes ces vues…

Les contrôleurs de l’authentification

On a aussi la création de nombreux contrôleurs :

Nous allons aussi voir à quoi ils servent…

L’enregistrement d’un utilisateur

L’enregistrement d’un utilisateur passe par l’url …/register. Et cette route :

On voit qu’on utilise le contrôleur RegisteredUserController avec sa méthode create qui se contente de renvoyer une vue :

public function create()
{
    return view('auth.register');
}

On va donc chercher cette vue :

Elle a cet aspect :

Cette vue utilise entre autres le composant resources/views/layouts/guest.blade.php. Je n’ai pas encore parlé des composants de Blade dans ce cours, c’est une autre manière d’organiser le code des vues, la documentation est ici. Breeze fait un usage intensif de ces composants. Je ne vais pas entrer dans le détail du code.

La validation

La validation pour l’enregistrement est présente dans le contrôleur :

public function store(Request $request)
{
    $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
        'password' => ['required', 'confirmed', Rules\Password::defaults()],
    ]);

De cette manière elle est facile à modifier si vous devez changer des règles ou ajouter un champ.

On peut vérifier que ça fonctionne :

Je rappelle qu’on peut ajouter la langue française avec ce complément.

La création de l’utilisateur

La création de l’utilisateur se fait aussi dans le contrôleur :

public function store(Request $request)
{
    ...

    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
    ]);

    ...
}

On utilise la méthode create d’Eloquent comme on l’a déjà vu au chapitre précédent. Vous trouvez dans le modèle App\Models\User.php la propriété $fillable renseignée :

protected $fillable = [
    'name', 'email', 'password',
];

Donc là encore il est facile d’apporter des modifications si nécessaire.

Une fois que l’utilisateur est créé dans la base il est automatiquement connecté et est redirigé sur le dashboard :

La connexion d’un utilisateur

Pour se connecter,il faut utiliser l’url …/login. Voici la route concernée :

Si vous venez juste d’enregistrer un utilisateur il faudra le déconnecter avant d’avoir accès au formulaire de login.

On voit qu’on utilise le contrôleur AuthenticatedSessionController avec sa méthode create qui se contente de renvoyer une vue :

public function create()
{
    return view('auth.login');
}

On va donc chercher cette vue :

Elle a cet aspect :

Cette vue utilise aussi le composant resources/views/layouts/guest.blade.php.

Dans le formulaire de connexion il y a une case à cocher « se rappeler de moi » (remember me) :

Si on coche cette case on reste connecté indéfiniment jusqu’à ce qu’on se déconnecte intentionnellement. Pour que ça fonctionne il faut une colonne remember_token dans la table users :

Il se trouve que cette colonne est prévue dans la migration de base pour la table.

La déconnexion d’un utilisateur

L’utilisateur dispose d’un lien pour se déconnecter :

Si on clique sur Logout on utilise l’url …/logout et cette route ::

Remarquez que la méthode est POST. Du coup la vue (layouts.navigation) utilise un formulaire et une soumission en Javascript :

<x-slot name="content">
    <!-- Authentication -->
    <form method="POST" action="{{ route('logout') }}">
        @csrf

        <x-dropdown-link :href="route('logout')"
                onclick="event.preventDefault();
                            this.closest('form').submit();">
            {{ __('Log Out') }}
        </x-dropdown-link>
    </form>
</x-slot>

C’est la méthode destroy du contrôleur AuthenticatedSessionController qui accomplit la déconnexion :

public function destroy(Request $request)
{
    Auth::guard('web')->logout();

    $request->session()->invalidate();

    $request->session()->regenerateToken();

    return redirect('/');
}

La vérification de l’email

Si vous voulez que l’utilisateur confirme son email, et ainsi être sûr que ce n’est pas un fake il faut que le modèle User implémente l’interface MustVerifyEmail :

<?php
 
namespace App\Models;
 
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
 
class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;
 
    // ...
}

Et il reçoit cet email lorqu’il s’enregistre :

Lorsqu’il utilise le lien de validation l’email est bien vérifié et il est dirigé sur la page de connexion.

Il ne reste plus qu’à utiliser le middleware verified sur les routes qu’on veut protéger et qui ne seront donc pas accessible avant la vérification de l’email. Par exemple pour le dashboard :

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Il reçoit alors ce message :

Changer la langue

On a déjà vu qu’on peut ajouter le français à Laravel en utilisant Laravel-Lang. Dans le fichier fr.json figurent aussi les traductions pour l’authentification.

En résumé

  • L’authentification est totalement et simplement prise en charge par Laravel.
  • Les migrations pour l’authentification sont déjà dans l’installation de base de Laravel.
  • L’ajout du package Breeze permet de générer les routes et les vues pour l’authentification.
  • Les middlewares auth et guest permettent d’interdire les accès aux routes qui concernent uniquement les utilisateurs authentifiés ou non authentifiés.
Print Friendly, PDF & Email

16 commentaires

  • Nanourgo

    Bonjour !
    Merci pour le tuto, j’ai essayé l’installation de laravel/breeze mais voici l’erreur que je rencontre

    Your requirements could not be resolved to an installable set of packages.

    Problem 1
    – Root composer.json requires laravel/breeze ^1.21 -> satisfiable by laravel/breeze[v1.21.0, 1.x-dev].
    – laravel/breeze[v1.21.0, …, 1.x-dev] require illuminate/console ^10.0 -> found illuminate/console[v10.0.0, …, 10.x-dev] but these were not loaded, likely because it conflicts with another require.

    You can also try re-running composer require with an explicit version constraint, e.g. « composer require laravel/breeze:* » to figure out if any version is installable, or « composer require laravel/breeze:^2.1 » if you know which you need.

    Installation failed, reverting ./composer.json and ./composer.lock to their original content.

  • TH1322Y

    Bonjour et bravo pour ce site qui m’aide vraiment beaucoup dans ma formation.

    Lors de l’installation de Breeze, je ne comprend pas la commande :

    npm run dev

    Elle est associée au fichier package.json et semble lancer un serveur « laravel-vite ». Bien sur, ce serveur tourne sans s’arrêter.

    Avant d’aller plus loin, je voudrai, au moins, savoir si je dois relancer ce serveur à chaque utilisation et, si oui, qu’en faire lors de la mise en ligne?

    Cordialement.

    • bestmomo

      Bonjour,

      Merci et d’abord, je suis désolé, je crois que j’ai supprimé ton compte par erreur.

      Pour la question, il y a deux façons de générer les assets avec Artisan :

      npm run dev pour la phase de développement
      npm run build pour la production

      Si en mode développement tu n’as rien de spécial à faire au niveau des assets autant générer en mode build.

      Le mode dev lance un serveur, mais tu n’es pas obligé de l’utiliser.

  • jire

    Bonjour. J’ai un problème avec L’authentification. En effet, j’obtiens cette erreur :

    $npm install :

    npm WARN registry Unexpected warning for https://registry.npmjs.org/: Miscellaneous Warning EAI_AGAIN: request to https://registry.npmjs.org/escalade failed, reason: getaddrinfo EAI_AGAIN registry.npmjs.org registry.npmjs.org:443
    npm WARN registry Using stale data from https://registry.npmjs.org/ due to a request error during revalidation.
    npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
    npm WARN notsup Unsupported engine for laravel-mix@6.0.43: wanted: {"node":">=12.14.0"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: laravel-mix@6.0.43
    npm WARN notsup Unsupported engine for tailwindcss@3.0.24: wanted: {"node":">=12.13.0"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: tailwindcss@3.0.24
    npm WARN notsup Unsupported engine for fs-extra@10.1.0: wanted: {"node":">=12"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: fs-extra@10.1.0
    npm WARN notsup Unsupported engine for img-loader@4.0.0: wanted: {"node":">=12"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: img-loader@4.0.0
    npm WARN notsup Unsupported engine for postcss-loader@6.2.1: wanted: {"node":">= 12.13.0"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: postcss-loader@6.2.1
    npm WARN notsup Unsupported engine for yargs@17.5.1: wanted: {"node":">=12"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: yargs@17.5.1
    npm WARN notsup Unsupported engine for webpackbar@5.0.2: wanted: {"node":">=12"} (current: {"node":"10.19.0","npm":"6.14.4"})
    npm WARN notsup Not compatible with your version of node/npm: webpackbar@5.0.2

    npm ERR! Unexpected end of JSON input while parsing near '...bpack-cli":"^2.0.14",'

    npm ERR! A complete log of this run can be found in:
    npm ERR! /home/jires/.npm/_logs/2022-05-19T21_32_11_793Z-debug.log

    $npm run dev:

    > @ dev /opt/lampp/htdocs/matter_auth
    > npm run development

    > @ development /opt/lampp/htdocs/matter_auth
    > mix

    sh: 1: mix: not found
    npm ERR! code ELIFECYCLE
    npm ERR! syscall spawn
    npm ERR! file sh
    npm ERR! errno ENOENT
    npm ERR! @ development: `mix`
    npm ERR! spawn ENOENT
    npm ERR!
    npm ERR! Failed at the @ development script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
    npm WARN Local package.json exists, but node_modules missing, did you mean to install?

    npm ERR! A complete log of this run can be found in:
    npm ERR! /home/jires/.npm/_logs/2022-05-19T21_36_19_810Z-debug.log
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! @ dev: `npm run development`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the @ dev script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
    npm WARN Local package.json exists, but node_modules missing, did you mean to install?

    npm ERR! A complete log of this run can be found in:
    npm ERR! /home/jires/.npm/_logs/2022-05-19T21_36_19_906Z-debug.log

    je n’arrive pas a télécharge le dossier css/app.css et d’autre ficher. Merci d’avance

      • jire

        Bonjour oui je suis en local et je travail sur ubuntu 20.04, logiciel installer ce xampp
        Apache HTTP Server.
        svp aider moi à trouver la solution à ce probleme je suis bloque je n’arrive pas à télécharger le dosier CSS/et les sous dosier avec : npm install
        npm run dev, pour compiler les assets pour générer le CSS : on m’affiche des trop des ERR!

  • kingm@n15

    Bonjour. J’ai un problème avec la vérification par email. En effet, j’obtiens une erreur :

    Connection could not be established with host « mailhog:1025 »: stream_socket_client(): php_network_getaddresses: getaddrinfo for mailhog failed: H�te inconnu.

    C’est juste après la création d’un utilisateur. Merci d’avance.

Laisser un commentaire