Laravel 7: authentification d’une application SPA avec Sanctum

Cet article présente l’installation du package Sanctum (ex-Airlock) sur une application Laravel SPA (Single Page Application) afin de protéger une partie des ressources exposées dans l’API Laravel par une authentification classique (login / mot de passe).

Il s’agit d’une authentification par Cookie pour un frontend placé sur le même domaine que l’API exposée par Laravel (ou bien un sous-domaine du backend), avec une protection contre les attaques CSRF (Cross-Site Request Forgery).

On s’appuie sur l’application mise en place dans l’article de Microtuto: « Laravel 7: une application SPA avec Vue.js« .

Installation du package Laravel Sanctum

L’installation du package Sanctum est détaillée dans la documentation Laravel relative à ce package. Voici les commandes utilisées successivement pour l’installation de Sanctum:

# Installation du package Sanctum:
composer require laravel/sanctum

 # Publication des fichiers de migration et de configuration Sanctum, lancement des migrations
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate --env=artisan

Mise en place de l’authentification Sanctum de l’application SPA avec Vue.js

Dans un premier temps il faut configurer le ou les domaines du frontend, qui feront appel à l’authentification avec Sanctum. Cette configuration apparait dans les 2 fichiers: config/session.php:

'domain' => env('SESSION_DOMAIN', null),

et config/sanctum.php:

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),

Cependant la meilleure option consiste à fournir la liste des domaines utilisés sur les variables d’environnement SANCTUM_STATEFUL_DOMAINS et SESSION_DOMAIN à la fin du le fichier de configuration: .env:

SANCTUM_STATEFUL_DOMAINS = "mondomaine.com"
SESSION_DOMAIN = ".mondomaine.com"

Il faut ensuite modifier le fichier app/Http/Kernel.php pour ajouter la classe « EnsureFrontendRequestsAreStateful »:

'api' => [
    EnsureFrontendRequestsAreStateful::class,
    'throttle:60,1',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

Configuration CORS

Modification de la configuration de l’application Laravel et du client Ajax (Axios) pour accepter les requêtes CORS (Cross-Origin Resource Sharing), en particulier les requêtes depuis un sous-domaine:

Sur js/components/bootstraps.php:

window.axios = require('axios');
axios.defaults.withCredentials = true;

La encore le mieux à fournir la liste des domaines utilisés sur la variable d’environnement SESSION_DOMAIN à la fin du le fichier de configuration: .env:

'domain' => '.domain.com'

L’application backend Laravel doit également renvoyer la valeur « Access-Control-Allow-Credentials » dans le header en réponse aux requêtes sur l’API. Pour cela, il faut positionner l’option « supports_credentials » à true dans le fichier config/cors.php:

'supports_credentials' => true,

Mise en place de l’authentification avec Sanctum depuis une page Vue.js

Référence: article du blog dev.to sur Sanctum.

Il faut tout d’abord vérifier que la route Laravel exposant la fonctionnalité de login soit bien configurée. La route est en principe ajoutée dans le fichier routes/web.php lors de lors de la mise en place de l’authentification Laravel / Vue.js (voir article Microtuto « Laravel 7: une application SPA avec Vue.js« ) : vérifier que la ligne suivante est bien présente dans web.php:

...
Auth::routes();
...

L’authentification depuis le frontend Vue.js se fait en précédent l’appel à l’API login d’une requête pour mettre en place la protection CSRF:

axios.get('/sanctum/csrf-cookie').then(response => {
    // Login...
});

On va directement tester la fonctionnalité de Login au lancement de l’application Vue.js, sans créer de formulaire dans un premier temps. Pour cela, on ajouter les appels aux API directement lors du chargement du composant Root dans resource/js/app.js:

const app = new Vue({
    el: '#app',
    router,
    // store,
    components: { "index": Index },
    async beforeCreate() {
        //this.$store.dispatch("loadStoredState");
        await axios.get('sanctum/csrf-cookie');
        await axios.post("login", {
            email: "test@mail.fr",
            password: "motdepasse"
        })
    }
});

Les 2 appels en gras ci-dessus, réalise de manière synchrone (l’un après l’autre) à l’aide des mots clés « async » et « wait »:

  • Web service GET sanctum/csrf-cookie pour récupérer le token CRSF
  • Web service POST Login avec un email et un mot de passe

Lorsque l’on relance l’application:

http://localhost

On voit les appels passer dans l’onglet « Réseau » de l’affichage développeur:

Appels web services Laravel Sanctum: CRSF et Login sous Vue.js
Appels web services CRSF et Login – Vue-js et Sanctum

Attention: si on obtient une erreur de type « message »CSRF token mismatch. » lors de l’appel à l’API /login, il se peut que le domain de session (SESSION_DOMAIN) soit mal configuré dans le fichier .env et/ou config/session.php.

Nous allons maintenant faire la même opération depuis un formulaire d’authentification, en créant le nouveau composant: resource/js/components/auth/Login.vue, dont voici la partie « template »:

<template>
    <div class="w-50 m-auto">
        <div class="card card-body">
            <form>
                <div class="form-group">
                    <label for="email">Courriel</label>
                    <input id="email" type="text" name="email" placeholder="Entrez votre adresse e-mail" class="form-control" v-model="email"/>
                </div>
                <div class="form-group">
                    <label for="password">Mot de passe</label>
                    <input id="password" type="text" name="password" placeholder="Entrez votre mot de passe" class="form-control" v-model="password"/>
                </div>
                <button type="submit" class="btn btn-primary btn-lg btn-block" :disabled="loading" @click.prevent="login">Valider</button>
            </form>
        </div>
    </div>
</template>

Et voic la partie « script »:

<script>
export default {
data() {
return { email: null, password: null, errors: null, loading: false}
},
methods: {
async login() {
this.loading = true;
this.errors = null;
try {
await axios.get('sanctum/csrf-cookie');
await axios.post("login", {
email: this.email,
password: this.password
})
} catch (error) {
this.errors = error.response && error.response.data.errors;
}
this.loading = false;
}
}
}
</script>

La route /login étant configurée dans Vue.js, on peut alors tester le formulaire sur l’URL:

on relance l’application pour tester le formulaire d’authentification:

http://localhost/login

Authentification depuis le formulaire du composant Vue.js: Login.vue
Authentification depuis le formulaire du composant Vue.js: Login.vue

Nous avons mis en place l’authentification Laravel Sanctum depuis une application SPA sous Vue.js.

Dans la déclaration des routes de l’API il faut configurer le middleware ‘auth:sanctum’ sur les routes que l’on souhaite rendre accessibles uniquement aux utilisateur authentifiés. Par exemple, dans route/api.php:

Route::get('parties/actives', 'API\PartieController@actives')
    ->middleware('auth:sanctum');

Attention: si la configuration de Sanctum est incomplète (pas de déclaration de la classe EnsureFrontendRequestsAreStateful dans le fichier app/Http/Kernel.php ,
ou bien utilisation du middleware ‘auth:api’ à la place de ‘auth:sanctum’ sur la route déclarée) alors les appels à une API ne fonctionnera pas (erreur 401:Unauthenticated) et peuvent même faire perdre l’authentification sur le frontend Vue.js.

Laisser un commentaire

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