Logomark

LARAVEL

Un framework qui rend heureux
Voir cette catégorie
Vers le bas
Comprendre et utiliser Fortify
Mercredi 1 avril 2026 18:36

Laravel Fortify est une solution backend dédiée à l’authentification dans le cadre de Laravel. À la différence d'autres packages, il est totalement agnostique du frontend : il gère toute la logique serveur (routes, contrôleurs, sécurité) sans imposer de framework CSS (Tailwind, Bootstrap), de bibliothèque JavaScript (Vue, React) ou de design spécifique. Vous gardez ainsi le contrôle total de l'expérience utilisateur tout en bénéficiant d'une base solide et sécurisée.

Généralités

1. Fortify est un moteur d’authentification complet et prêt à l’emploi

Il automatise la création des routes et contrôleurs essentiels pour une application moderne, avec des fonctionnalités clés intégrées :

  • Connexion et inscription (avec validation et protection CSRF) ;
  • Réinitialisation de mot de passe (liens sécurisés, tokens temporaires) ;
  • Vérification d’e-mail (confirmation en un clic) ;
  • Authentification à deux facteurs (2FA) (via TOTP ou SMS) ;
  • Confirmation de mot de passe (pour les actions sensibles).

2. Une liberté créative sur le frontend

Fortify ne fournit aucune vue (pas de fichiers .blade.php). Votre front-end (React, Vue, Svelte ou même du HTML pur) communique avec le back-end via des requêtes HTTP standard (REST ou API). Vous concevez l’interface comme vous le souhaitez, sans compromis.

Exemple : un formulaire de connexion en React peut envoyer une requête POST à l'adresse /login, et Fortify gère la validation, la session et la réponse.

3. Flexible : un outil, pas une contrainte

Fortify a été conçu pour accélérer votre développement, et non pour le contraindre.

Vous avez besoin d'un contrôle total ? Utilisez les services natifs de Laravel (Auth, Session, etc.) pour une personnalisation poussée.

Préférez-vous une solution clé en main (frontend inclus) ? Optez pour un kit de démarrage officiel (qui intègre Fortify en coulisses) ou l'un des multiples disponibles ici.

Pourquoi choisir Fortify ?

  • Robustesse : bénéficiez de la sécurité et de la fiabilité de Laravel.
  • Liberté : intégrez n’importe quel frontend (ou même une application mobile).
  • Modularité : activez ou désactivez les fonctionnalités selon vos besoins.

Il est idéal pour les développeurs qui souhaitent une authentification solide sans sacrifier leur stack frontend. Fortify est le compromis parfait entre puissance backend et flexibilité frontend. Que vous construisiez une SPA, une API ou un site traditionnel, il vous offre les fondations, à vous de bâtir le reste.

Guide d'installation

Fortify s'installe rapidement et s'intègre discrètement à votre projet, sans imposer de structure rigide. voyons à présent comment le déployer et le configurer efficacement.

Ajoutez Fortify à vos dépendances via Composer :

composer require laravel/fortify

Générez les fichiers de configuration et les composants clés avec :

php artisan fortify:install

Cette commande vous apporte :

Éléments générés Rôle Personnalisation possible ?
app/Actions/ Classes métiers (ex: CreateNewUser, UpdateUserPassword) pour gérer la logique utilisateur. Modifiez les règles de validation, les workflows, etc.
FortifyServiceProvider Point central de liaison entre Fortify et votre application. Ajoutez/supprimez des fonctionnalités.
config/fortify.php Fichier de configuration pour activer/désactiver des modules (2FA, enregistrement, etc.). Adaptez les options selon vos besoins.
Migrations Tables nécessaires (ex: two_factor_columns_to_users). Étendez ou modifiez les schémas.

Appliquez les migrations pour créer les tables nécessaires :

php artisan migrate

Vérifiez que votre fichier .env est correctement configuré (connexion à la base de données).

Configuration et activation des fonctionnalités

Laravel Fortify se distingue par sa flexibilité : vous n'activez que les fonctionnalités dont votre application a besoin. Tout se configure dans un seul fichier : config/fortify.php. Ce tableau agit comme un interrupteur pour chaque service d’authentification. Chaque entrée correspond à une fonctionnalité que vous pouvez activer ou désactiver en une ligne.

Voici un exemple de configuration de base :

// config/fortify.php
'features' => [
    Features::registration(),       // Inscription des utilisateurs
    Features::resetPasswords(),     // Réinitialisation des mots de passe
    Features::emailVerification(),  // Vérification des emails
    // Features::updateProfileInformation(),
    // Features::updatePasswords(),
    // Features::twoFactorAuthentication(['confirm' => true, 'confirmPassword' => true, // 'window' => 0,]),
],

Pour répondre aux besoins de base, Laravel recommande d'activer ces trois fonctionnalités :

Fonctionnalité Description Cas d’usage
Features::registration() Active les routes et la logique pour la création de comptes (formulaire d’inscription, validation). Applications ouvertes au public (ex: réseaux sociaux, plateformes SaaS).
Features::resetPasswords() Gère l’envoi d’emails de récupération et la réinitialisation des mots de passe oubliés. Obligatoire pour toute application avec authentification.
Features::emailVerification() Force les utilisateurs à valider leur email avant d’accéder à certaines zones. Renforce la sécurité et réduit les comptes frauduleux (ex: marketplaces, forums).

Avantages de cette approche

  • Renforcer la sécurité
    Vous n’exposez que les routes strictement nécessaires.
    Exemple : si votre application est en accès privé (par exemple, un intranet), désactivez la méthode registration() pour bloquer les inscriptions publiques.
  • Performance optimisée
    Fortify ne charge que les contrôleurs et services associés aux fonctionnalités activées.
    Résultat : une application plus légère et des temps de réponse améliorés.
  • Évolutivité
    Ajoutez des modules au fur et à mesure de vos besoins.
    Par exemple, activez la fonction twoFactorAuthentication() plus tard, si votre application gère des données sensibles.

Fonctionnalités avancées (optionnelles)

Fortify propose d’autres modules pour répondre à des besoins spécifiques :

Fonctionnalité Utilité Quand l’activer ?
Features::twoFactorAuthentication() Ajoute une couche de sécurité 2FA (via TOTP ou SMS). Applications financières, administratives, ou sensibles.
Features::updateProfileInformation() Permet aux utilisateurs de mettre à jour leurs informations (email, nom, etc.). Plateformes avec profils utilisateurs (ex: réseaux sociaux).
Features::updatePasswords() Autorise la modification du mot de passe depuis le tableau de bord.

Bonnes pratiques

Désactivez les fonctionnalités que vous n’utilisez pas :

'features' => [
    Features::registration()  // à désactiver si les inscriptions sont fermées,
    Features::resetPasswords(),
    Features::emailVerification(),
],
  • Testez après chaque modification.
  • Utilisez la commande php artisan route:list pour vérifier que seules les routes attendues sont exposées.
  • Documentez vos choix : ajoutez des commentaires dans le fichier fortify.php pour expliquer pourquoi une fonctionnalité est activée ou désactivée.

Exemple de configuration pour une application SaaS :

'features' => [
    Features::registration(),           // Ouvert aux nouveaux clients
    Features::resetPasswords(),         // Obligatoire
    Features::emailVerification(),      // Validation des emails
    Features::twoFactorAuthentication(['confirmPassword' => true]), // 2FA + confirmation pour les actions sensibles
    Features::updateProfileInformation('confirm' => true, 'confirmPassword' => true,), // Les clients gèrent leurs infos
],

Personnalisation de l’authentification avec Laravel Fortify

Fortify est une bibliothèque headless : elle gère toute la logique d’authentification en arrière-plan, mais ne fournit aucune vue. C’est à vous de créer l’interface utilisateur et de la connecter à Fortify. Voici comment faire, étape par étape.

Déclaration de la vue de connexion

Pour que Fortify sache quelle vue afficher lors d'une requête GET /login, vous devez la déclarer dans le FortifyServiceProvider. Voici un exemple de configuration :

// app/Providers/FortifyServiceProvider.php
use Laravel\Fortify\Fortify;

public function boot(): void
{
    Fortify::loginView(function () {
        return view('auth.login'); // Votre vue Blade personnalisée
    });
}
  • Cette étape permet de lier la logique backend de Fortify à votre design frontend.
  • Vous pouvez utiliser le système de votre choix (Blade, React, Vue, etc.), à condition qu'il envoie les données au bon point d'accès.

Structure du formulaire de connexion

Votre formulaire doit envoyer une requête POST vers /login et respecter les conventions suivantes :

Champs obligatoires :

Champ Type Nom du champ (par défaut) Description
Identifiant text/email email Adresse email ou nom d’utilisateur.
Mot de passe password password Mot de passe de l’utilisateur.
"Remember Me" checkbox remember (Optionnel) Pour une session prolongée.

Exemple de formulaire simple :

<!-- resources/views/auth/login.blade.php -->
<form method="POST" action="/login">
    @csrf
    <div>
        <label for="email">Email</label>
        <input id="email" type="email" name="email" required autofocus>
    </div>
    <div>
        <label for="password">Mot de passe</label>
        <input id="password" type="password" name="password" required>
    </div>
    <div>
        <input type="checkbox" name="remember" id="remember">
        <label for="remember">Se souvenir de moi</label>
    </div>
    <button type="submit">Se connecter</button>
</form>
  • Le nom du champ d'identification (email par défaut) doit correspondre à la valeur définie dans le fichier config/fortify.php (clé « username »).
  • Si vous utilisez un champ différent (par exemple, « username »), mettez à jour la configuration :
// config/fortify.php
'username' => 'username', // Au lieu de 'email'

Vous devez également informer Fortify de l'existence et de l'emplacement de la vue de votre formulaire :

class FortifyServiceProvider extends ServiceProvider
{
    ...

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Fortify::loginView(function () { return view('auth.login'); });
        

Et vous obtenez ainsi votre magnifique formulaire de connexion :

Gestion des réponses et des erreurs

Fortify adapte ses réponses en fonction du type de requête (web classique ou AJAX). Comportement selon le type de requête :

Scénario Requête Web (HTML) Requête AJAX/XHR
Succès Redirection vers home (configurable). Réponse HTTP 200 OK.
Échec Redirection vers /login avec les erreurs dans $errors. Réponse HTTP 422 avec les erreurs en JSON.

Voici un exemple de gestion des erreurs avec Blade :

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

Exemple schématique de gestion des erreurs en AJAX (JavaScript) :

fetch('/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
    },
    body: JSON.stringify({
        email: 'user@example.com',
        password: 'password123',
        remember: true,
    }),
})
.then(response => {
    if (!response.ok) {
        return response.json().then(err => { throw err; });
    }
    return response.json();
})
.then(data => {
    window.location.href = '/dashboard'; // Redirection en cas de succès
})
.catch(errors => {
    console.error('Erreurs de connexion:', errors);
    // Afficher les erreurs à l'utilisateur
});

Je reste volontairement avec un traitement simplifié pour me focaliser sur les fonctionnalités.

Pour une connexion réaliste

Maintenant qu'on a vu les principes, on peut améliorer l'aspect en toute liberté pour notre formulaire, par exemple avec Bulma :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Connexion</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
</head>
<body class="has-background-light" style="min-height: 100-screen; display: flex; align-items: center; justify-content: center; height: 100vh;">

<div class="column is-4-tablet is-3-desktop">
    <div class="box">
        <h1 class="title has-text-centered has-text-grey-darker">Connexion</h1>

        <form method="POST" action="/login">
            @csrf

            <div class="field">
                <label class="label" for="email">Email</label>
                <div class="control has-icons-left">
                    <input
                        id="email"
                        name="email"
                        type="email"
                        class="input @error('email') is-danger @enderror"
                        placeholder="e.g. alex@exemple.com"
                        value="{{ old('email') }}"
                        required
                        autofocus
                    >
                    <span class="icon is-small is-left">
                            <i class="fas fa-envelope"></i>
                        </span>
                </div>
                @error('email')
                <p class="help is-danger">{{ $message }}</p>
                @enderror
            </div>

            <div class="field">
                <label class="label" for="password">Mot de passe</label>
                <div class="control has-icons-left">
                    <input
                        id="password"
                        name="password"
                        type="password"
                        class="input @error('password') is-danger @enderror"
                        required
                    >
                    <span class="icon is-small is-left">
                            <i class="fas fa-lock"></i>
                        </span>
                </div>
            </div>

            <div class="field">
                <div class="control">
                    <label class="checkbox">
                        <input type="checkbox" name="remember" id="remember">
                        Se souvenir de moi
                    </label>
                </div>
            </div>

            <div class="field mt-5">
                <button type="submit" class="button is-primary is-fullwidth">
                    <strong>Se connecter</strong>
                </button>
            </div>
        </form>
    </div>
</div>

<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</body>
</html>

L'enregistrement des utilisateurs

Tout ce qu'on a vu jusque-là pour l'authentification reste valable également pour l'enregistrement avec les mêmes principes. Fortify simplifie l’enregistrement des utilisateurs en gérant toute la logique backend (validation, création de compte, envoi d’e-mails), tout en vous laissant le contrôle total de l’interface et de l’expérience utilisateur. Voyons comment configurer et personnaliser cette fonctionnalité.

Déclaration de la vue d’inscription

Pour que Fortify sache quelle vue afficher lors d’une requête GET /register, déclarez-la dans le FortifyServiceProvider :

// app/Providers/FortifyServiceProvider.php
use Laravel\Fortify\Fortify;

public function boot(): void
{
    Fortify::registerView(function () {
        return view('auth.register'); // Votre vue Blade personnalisée
    });
}
  • Cette étape permet de connecter la logique backend de Fortify à votre interface utilisateur.
  • Vous pouvez utiliser le framework frontend de votre choix (Blade, React, Vue, etc.), à condition que le formulaire envoie les données à l'adresse /register.

Structure du formulaire d'inscription

Votre formulaire doit envoyer une requête POST vers /register et inclure les champs nécessaires :

Champ Type Nom du champ Description
Nom text name Nom complet de l’utilisateur.
Email email email Adresse email (doit être unique).
Mot de passe password password Mot de passe (doit être confirmé).
Confirmation password password_confirmation Confirmation du mot de passe.

Exemple épuré de formulaire :

<!-- resources/views/auth/register.blade.php -->
<form method="POST" action="/register">
    @csrf
    <div>
        <label for="name">Nom complet</label>
        <input id="name" type="text" name="name" required autofocus>
    </div>
    <div>
        <label for="email">Email</label>
        <input id="email" type="email" name="email" required>
    </div>
    <div>
        <label for="password">Mot de passe</label>
        <input id="password" type="password" name="password" required>
    </div>
    <div>
        <label for="password_confirmation">Confirmer le mot de passe</label>
        <input id="password_confirmation" type="password" name="password_confirmation" required>
    </div>
    <button type="submit">S'inscrire</button>
</form>
  • Les règles de validation (longueur du mot de passe, format de l'adresse e-mail, etc.) sont définies dans le fichier app/Actions/Fortify/CreateNewUser.php.
  • Vous pouvez ajouter des champs personnalisés (par exemple, « phone » ou « company_id ») en étendant cette classe.

On se retrouve à nouveau avec un magnifique formulaire :

Personnalisation des règles de validation

Pour modifier les règles de validation (par exemple, pour exiger un mot de passe plus complexe), éditez la classe CreateNewUser. Exemple : ajouter une validation personnalisée :

// app/Actions/Fortify/CreateNewUser.php
use Laravel\Fortify\Contracts\CreatesNewUsers;

class CreateNewUser implements CreatesNewUsers
{
    public function create(array $input): User
    {
        Validator::make($input, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => [
                'required',
                'string',
                'min:10', // Mot de passe d'au moins 10 caractères
                'regex:/[A-Z]/', // Au moins une majuscule
                'regex:/[0-9]/', // Au moins un chiffre
                'confirmed',
            ],
            'phone' => ['required', 'string', 'max:15'], // Champ personnalisé
        ])->validate();

        return User::create([
            'name' => $input['name'],
            'email' => $input['email'],
            'password' => Hash::make($input['password']),
            'phone' => $input['phone'], // Sauvegarde du champ personnalisé
        ]);
    }
}

Gestion des réponses et des erreurs

Fortify adapte ses réponses en fonction du type de requête (Web ou AJAX). Comportement selon le type de requête :

Scénario Requête Web (HTML) Requête AJAX/XHR
Succès Redirection vers home (configurable). Réponse HTTP 201 Created.
Échec Redirection vers /register avec les erreurs dans $errors. Réponse HTTP 422 avec les erreurs en JSON.

On peut facilement gérer les erreurs avec Blade :

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

Pour une inscription réaliste

Maintenant qu'on a vu les principes, on peut améliorer l'aspect en toute liberté pour notre formulaire, par exemple avec Pico (une jolie librairie minimaliste) :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Inscription</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
    <style>
        /* Un petit ajustement pour centrer le formulaire proprement */
        body { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
        article { width: 100%; max-width: 500px; }
        .error-text { color: #d81b60; font-size: 0.8rem; margin-top: -1rem; margin-bottom: 1rem; display: block; }
    </style>
</head>
<body>

<article>
    <header>
        <strong>Créer un compte</strong>
    </header>

    <form method="POST" action="/register">
        @csrf

        <label for="name">Nom complet</label>
        <input
            id="name"
            type="text"
            name="name"
            placeholder="Ex: Jean Dupont"
            value="{{ old('name') }}"
            required
            autofocus
            aria-invalid="{{ $errors->has('name') ? 'true' : 'false' }}"
        >
        @error('name')
        <small class="error-text">{{ $message }}</small>
        @enderror

        <label for="email">Adresse email</label>
        <input
            id="email"
            type="email"
            name="email"
            placeholder="jean@exemple.fr"
            value="{{ old('email') }}"
            required
            aria-invalid="{{ $errors->has('email') ? 'true' : 'false' }}"
        >
        @error('email')
        <small class="error-text">{{ $message }}</small>
        @enderror

        <div class="grid">
            <div>
                <label for="password">Mot de passe</label>
                <input
                    id="password"
                    type="password"
                    name="password"
                    required
                    aria-invalid="{{ $errors->has('password') ? 'true' : 'false' }}"
                >
            </div>

            <div>
                <label for="password_confirmation">Confirmation</label>
                <input
                    id="password_confirmation"
                    type="password"
                    name="password_confirmation"
                    required
                >
            </div>
        </div>
        @error('password')
        <small class="error-text">{{ $message }}</small>
        @enderror

        <button type="submit">S'inscrire</button>

        <footer style="text-align: center;">
            <small>Déjà un compte ? <a href="/login">Se connecter</a></small>
        </footer>
    </form>
</article>

</body>
</html>

Réinitialisation du mot de passe avec Fortify

Fortify propose un système complet pour gérer la réinitialisation des mots de passe, de l'envoi du lien jusqu'à la mise à jour du mot de passe. Vous conservez une liberté totale sur le design des pages et l’expérience utilisateur, tandis que Fortify s’occupe de la logique backend (envoi d’e-mails, validation des tokens, sécurité).

Activer la fonctionnalité de changement de mot de passe

Avant toute chose, assurez-vous que la fonctionnalité est activée dans le fichier de configuration config/fortify.php :

'features' => [
    // ...
    Features::resetPasswords(), // Active la réinitialisation de mot de passe
],

Déclaration des vues 

Fortify requiert deux vues pour gérer le processus de réinitialisation :

  • le formulaire de demande de lien de réinitialisation (/forgot-password) ;
  • le formulaire de réinitialisation du mot de passe (/reset-password).

Déclarez ces vues dans le FortifyServiceProvider :

// app/Providers/FortifyServiceProvider.php
use Laravel\Fortify\Fortify;

public function boot(): void
{
    // Formulaire "Mot de passe oublié"
    Fortify::requestPasswordResetLinkView(function () {
        return view('auth.forgot-password');
    });

    // Formulaire de réinitialisation du mot de passe
    Fortify::resetPasswordView(function ($request) {
        return view('auth.reset-password', ['request' => $request]);
    });
}

Cette étape permet de relier la logique backend de Fortify à votre interface.

Formulaire « Mot de passe oublié »

Ce formulaire permet à l’utilisateur de demander un lien de réinitialisation en saisissant son adresse e-mail. Champ obligatoire :

Champ Type Nom du champ Description
Email email email Adresse email de l’utilisateur.

Exemple minimaliste de formulaire :

<!-- resources/views/auth/forgot-password.blade.php -->
<form method="POST" action="/forgot-password">
    @csrf
    <div>
        <label for="email">Adresse email</label>
        <input id="email" type="email" name="email" required autofocus>
    </div>
    <button type="submit">Envoyer le lien de réinitialisation</button>
</form>

Que se passe-t-il après la soumission ?

  • Fortify envoie un e-mail à l'utilisateur contenant un lien de réinitialisation sécurisé.
  • Ce lien redirige vers l'adresse /reset-password?token={token}&email={email}.

Faisons nous encore plaisir dans la mise en form cette fois avec Bootstrap 5 :

<!-- resources/views/auth/forgot-password.blade.php -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mot de passe oublié</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light d-flex align-items-center justify-content-center" style="height: 100vh;">

<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-5">
            <div class="card shadow-sm border-0">
                <div class="card-body p-4">
                    <h3 class="card-title text-center mb-4">Réinitialisation</h3>

                    <p class="text-muted small mb-4">
                        Saisissez votre adresse email et nous vous enverrons un lien pour réinitialiser votre mot de passe.
                    </p>

                    @if (session('status'))
                        <div class="alert alert-success shadow-sm mb-4" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    <form method="POST" action="/forgot-password">
                        @csrf

                        <div class="mb-3">
                            <label for="email" class="form-label">Adresse email</label>
                            <input
                                id="email"
                                type="email"
                                name="email"
                                value="{{ old('email') }}"
                                required
                                autofocus
                                class="form-control @error('email') is-invalid @enderror"
                                placeholder="nom@exemple.com"
                            >

                            @error('email')
                            <div class="invalid-feedback">
                                {{ $message }}
                            </div>
                            @enderror
                        </div>

                        <div class="d-grid gap-2">
                            <button type="submit" class="btn btn-primary">
                                Envoyer le lien
                            </button>
                            <a href="/login" class="btn btn-link btn-sm text-decoration-none">
                                Retour à la connexion
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

</body>
</html>

Formulaire de réinitialisation du mot de passe

Ce formulaire permet à l’utilisateur de saisir un nouveau mot de passe après avoir cliqué sur le lien reçu par e-mail. Champs obligatoires :

Champ Type Nom du champ Description
Email email email Email de l’utilisateur (pré-rempli).
Token hidden token Token de réinitialisation (passé dans l’URL).
Nouveau mot de passe password password Nouveau mot de passe.
Confirmation password password_confirmation Confirmation du nouveau mot de passe.

Formulaire minimaliste :

<!-- resources/views/auth/reset-password.blade.php -->
<form method="POST" action="/reset-password">
    @csrf
    <input type="hidden" name="token" value="{{ $request->token }}">

    <div>
        <label for="email">Adresse email</label>
        <input id="email" type="email" name="email" value="{{ old('email', $request->email) }}" required>
    </div>

    <div>
        <label for="password">Nouveau mot de passe</label>
        <input id="password" type="password" name="password" required>
    </div>

    <div>
        <label for="password_confirmation">Confirmer le mot de passe</label>
        <input id="password_confirmation" type="password" name="password_confirmation" required>
    </div>

    <button type="submit">Réinitialiser le mot de passe</button>
</form>

Mais comme on préfère les jolis formulaires je vous propose une formule avec DaisyUI :

<!DOCTYPE html>
<html lang="fr" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Nouveau mot de passe</title>
    <link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-base-200 min-h-screen flex items-center justify-center p-4">

<div class="card w-full max-w-sm shadow-2xl bg-base-100">
    <div class="card-body">
        <h2 class="card-title justify-center text-2xl mb-4">Nouveau mot de passe</h2>

        @if ($errors->any())
            <div class="alert alert-error shadow-lg mb-6">
                <div>
                    <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
                    <span class="text-sm">Veuillez corriger les erreurs ci-dessous.</span>
                </div>
            </div>
        @endif

        <form method="POST" action="/reset-password">
            @csrf
            <input type="hidden" name="token" value="{{ $request->token }}">

            <div class="form-control">
                <label class="label" for="email">
                    <span class="label-text">Adresse email</span>
                </label>
                <input
                    id="email"
                    type="email"
                    name="email"
                    value="{{ old('email', $request->email) }}"
                    class="input input-bordered @error('email') input-error @enderror"
                    required
                    readonly
                >
                @error('email')
                <label class="label"><span class="label-text-alt text-error">{{ $message }}</span></label>
                @enderror
            </div>

            <div class="form-control mt-2">
                <label class="label" for="password">
                    <span class="label-text">Nouveau mot de passe</span>
                </label>
                <input
                    id="password"
                    type="password"
                    name="password"
                    placeholder="••••••••"
                    class="input input-bordered @error('password') input-error @enderror"
                    required
                >
            </div>

            <div class="form-control mt-2">
                <label class="label" for="password_confirmation">
                    <span class="label-text">Confirmer le mot de passe</span>
                </label>
                <input
                    id="password_confirmation"
                    type="password"
                    name="password_confirmation"
                    placeholder="••••••••"
                    class="input input-bordered"
                    required
                >
                @error('password')
                <label class="label"><span class="label-text-alt text-error">{{ $message }}</span></label>
                @enderror
            </div>

            <div class="form-control mt-6">
                <button type="submit" class="btn btn-primary">
                    Mettre à jour le mot de passe
                </button>
            </div>
        </form>
    </div>
</div>

</body>
</html>

Personnalisation des règles de validation

Les règles de validation du nouveau mot de passe sont définies dans le fichier app/Actions/Fortify/ResetUserPassword.php. Vous pouvez les modifier pour imposer des contraintes plus strictes :

// app/Actions/Fortify/ResetUserPassword.php
use Laravel\Fortify\Contracts\ResetsUserPasswords;

class ResetUserPassword implements ResetsUserPasswords
{
    public function reset(User $user, array $input): void
    {
        Validator::make($input, [
            'token' => ['required'],
            'email' => ['required', 'email'],
            'password' => [
                'required',
                'string',
                'min:12', // Minimum 12 caractères
                'regex:/[A-Z]/', // Au moins une majuscule
                'regex:/[0-9]/', // Au moins un chiffre
                'regex:/[@$!%*#?&]/', // Au moins un caractère spécial
                'confirmed', // Doit correspondre à password_confirmation
            ],
        ])->validate();

        $user->forceFill([
            'password' => Hash::make($input['password']),
        ])->save();
    }
}

Gestion des réponses et des erreurs

Fortify adapte ses réponses en fonction du type de requête (Web ou AJAX). Comportement selon le type de requête :

Scénario Requête Web (HTML) Requête AJAX/XHR
Succès (demande de lien) Affiche une confirmation : "Lien de réinitialisation envoyé !". Réponse HTTP 200 OK avec un message de succès.
Échec (demande de lien) Redirige vers le formulaire avec les erreurs dans $errors. Réponse HTTP 422 avec les erreurs en JSON.
Succès (réinitialisation) Redirection vers /login avec un message de succès. Réponse HTTP 200 OK.
Échec (réinitialisation) Redirige vers le formulaire avec les erreurs dans $errors. Réponse HTTP 422 avec les erreurs en JSON.

Vérification de l'email

Fortify offre un système complet de vérification des adresses e-mail des utilisateurs, garantissant que chaque compte est associé à une adresse valide (même s'il est facile de créer des adresses email temporaires, ce qui rend la procédure un peu illusoire). Vous gardez le contrôle total sur le design des pages et l’expérience utilisateur, tandis que Fortify gère la logique backend (envoi de liens de vérification, gestion des tokens, mise à jour des statuts).

Activer la fonctionnalité

Pour activer la vérification des e-mails, assurez-vous que la fonctionnalité est bien déclarée dans le fichier config/fortify.php :

'features' => [
    // ...
    Features::emailVerification(), // Active la vérification d'email
],

Renseigner le modèle User

Le modèle User doit implémenter l'interface MustVerifyEmail :

use Illuminate\Contracts\Auth\MustVerifyEmail;

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable implements MustVerifyEmail
{

Il faut aussi protéger les routes concernées, par exemple :

Route::get('/dashboard', function () {
    // ...
})->middleware(['verified']);

Déclaration de la vue

Fortify nécessite une vue pour informer l’utilisateur que son adresse e-mail doit être vérifiée. Déclarez cette vue dans FortifyServiceProvider :

// app/Providers/FortifyServiceProvider.php
use Laravel\Fortify\Fortify;

public function boot(): void
{
    // Page demandant à l'utilisateur de vérifier son email
    Fortify::verifyEmailView(function () {
        return view('auth.verify-email');
    });
}

Cette page s’affiche après l’inscription ou lorsque l’utilisateur accède à une zone nécessitant une vérification de l’adresse email. Elle informe l’utilisateur qu’un lien de vérification lui a été envoyé par e-mail.

Voici un exemple de vue Blade :

<!-- resources/views/auth/verify-email.blade.php -->
<div>
    <h2>Vérifiez votre adresse email</h2>
    <p>
        Un lien de vérification a été envoyé à <strong>{{ auth()->user()->email }}</strong>.
        Veuillez cliquer sur ce lien pour vérifier votre adresse email.
    </p>
    <p>
        Si vous n'avez pas reçu l'email, vous pouvez
        <form method="POST" action="/email/verification-notification">
            @csrf
            <button type="submit">envoyer un nouveau lien</button>
        </form>
    </p>
</div>

Evidemment notre page de notification n'est pour le moment pas très jolie :

On va améliorer ça avec paper css :

<!-- resources/views/auth/verify-email.blade.php -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vérification d'email</title>
    <link rel="stylesheet" href="https://unpkg.com/papercss@1.9.2/dist/paper.min.css">
    <style>
        /* Centrage spécifique à la page */
        body {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            background-color: #f1f1f1;
            padding: 20px;
        }
        .container-paper { max-width: 600px; }
        .inline-form { display: inline; }
    </style>
</head>
<body>

<div class="container-paper">
    <article class="card">
        <div class="card-body">
            <h2 class="card-title text-center">Vérifiez votre adresse email</h2>

            @if (session('status') == 'verification-link-sent')
                <div class="alert alert-success border-dashed">
                    Un nouveau lien de vérification a été envoyé à votre adresse email.
                </div>
            @endif

            <p class="text-lead">
                Un lien de vérification a été envoyé à l'adresse :
                <span class="badge secondary"><strong>{{ auth()->user()->email }}</strong></span>.
            </p>

            <p>
                Veuillez cliquer sur ce lien pour activer votre compte. C'est indispensable pour accéder à toutes les fonctionnalités.
            </p>

            <div class="section shadow-sm p-3 mt-4">
                <p>
                    Si vous n'avez pas reçu l'email, pas de panique ! Vous pouvez
                <form method="POST" action="/email/verification-notification" class="inline-form">
                    @csrf
                    <button type="submit" class="btn-link text-secondary" style="padding: 0; border: none; background: none; font-weight: bold; cursor: pointer; text-decoration: underline;">
                        renvoyer un nouveau lien
                    </button>
                </form>.
                </p>
            </div>

            <div class="text-center mt-3">
                <a href="/" class="paper-btn">Retour à l'accueil</a>
            </div>
        </div>
    </article>
</div>

</body>
</html>

Et maintenant ça a quand même meilleure allure :

Processus :

  • Un email contenant un lien de vérification sécurisé est envoyé à l’utilisateur.
  • Ce lien redirige vers l'adresse /email/verify?expires={timestamp}&hash={hash}&id={user_id}&signature={signature}.

Voilà l'aspect par défaut de l'email :

Vous pouvez facilement publier le code pour modifier ce que vous voulez.

php artisan vendor:publish --tag=laravel-notifications

Gestion de la vérification

Lorsque l’utilisateur clique sur le lien de vérification, Fortify traite automatiquement la requête et met à jour le statut de vérification de l’adresse e-mail.

  • Si la vérification est réussie, l’utilisateur est redirigé vers la page définie dans FortifyServiceProvider (par défaut : /home).
  • Si le lien est invalide ou expiré, une erreur est affichée.

Conclusion

On a vu dans cet article comment utiliser efficacement Fortify. celui-ci gère la totalité de l'intendance backend. On doit seulement créer le frontend pour que ça fonctionne. J'ai traité seulement les éléments indispensables pour une authentification classique. Il y a encore des choses à explorer : confirmation du mot de passe, double facteur... Je pourrai développer ces points spécifiques dans un autre article si ça intéresse.


Par bestmomo

Aucun commentaire