Maison >développement back-end >tutoriel php >Implémentation et test de l'authentification Socialite dans Laravel

Implémentation et test de l'authentification Socialite dans Laravel

DDD
DDDoriginal
2025-01-03 11:17:42163parcourir

Implementing & testing Socialite authentication in Laravel

Laravel Socialite est un package Laravel propriétaire qui aide les développeurs à implémenter l'authentification sociale OAuth et OAuth2 dans leurs applications. Il prend en charge Facebook, Twitter, Google, LinkedIn, GitHub, GitLab et Bitbucket. Socialite peut soutenir d'autres prestataires via des packages communautaires.

Ce message :

  • Expliquez ce que Socialite fait et ne fait pas.
  • Montrez comment intégrer l'authentification Google dans un nouveau projet Laravel via Socialite.
  • Montrez un exemple de test de Socialite.

TLDR : vous pouvez voir le projet terminé sur mon GitHub. Jetez-y un œil si vous préférez simplement lire le code complété.

Que fait et ne fait pas Laravel Socialite ?

Socialite est un petit package, avec son API principale composée principalement de deux méthodes principales :

  • Socialite::driver($authProvider)->redirect() redirigera l'utilisateur vers le fournisseur d'authentification spécifié, en transmettant toutes les informations nécessaires au fournisseur via les paramètres d'URL.
  • Socialite::driver($authProvider)->user() récupère les données utilisateur renvoyées par le fournisseur d'authentification et les met à la disposition du point de terminaison.

Il existe des méthodes de prise en charge supplémentaires pour les étendues de paramètres et les paramètres facultatifs. Vous pouvez en savoir plus sur eux dans la documentation Socialite.

Socialite ne pas faire ce qui suit : il laisse la mise en œuvre de ces fonctionnalités au développeur :

  • ❌ Créez des tables ou des colonnes de base de données nécessaires pour stocker les données d'authentification sociale.
  • ❌ Créez des utilisateurs qui n'existent pas lors du processus d'authentification.
  • ❌ Authentifiez l'utilisateur après un flux OAuth réussi.
  • ❌ Actualisez les jetons OAuth.

Prérequis : créer un projet Google Cloud

Nous allons mettre en place un petit projet Socialite qui permet à l'utilisateur de s'authentifier via Google. Pour ce faire, vous devez créer une application Google.

Créez d'abord un nouveau projet Google Cloud, puis configurez un écran de consentement OAuth pour le projet. Définissez le type d'utilisateur sur externe, puis activez les étendues suivantes :

  • .../auth/userinfo.email
  • .../auth/userinfo.profile

Après avoir configuré l'écran de consentement, créez un ID client OAuth 2.0 en visitant la page d'informations d'identification Google Cloud. Conservez le ID client et le secret client : nous les utiliserons plus tard dans le projet.

Mise en place d'un projet Laravel minimal avec Socialite

Créez un nouveau projet Laravel :

laravel new socialite-tests

Sélectionnez les options suivantes dans le programme d'installation :

 ┌ Would you like to install a starter kit? ────────────────────┐
 │ No starter kit                                               │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which testing framework do you prefer? ──────────────────────┐
 │ Pest                                                         │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which database will your application use? ───────────────────┐
 │ SQLite                                                       │
 └──────────────────────────────────────────────────────────────┘

 ┌ Would you like to run the default database migrations? ──────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘

Accédez au répertoire du projet et installez Socialite.

laravel new socialite-tests

Créez une nouvelle migration.

 ┌ Would you like to install a starter kit? ────────────────────┐
 │ No starter kit                                               │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which testing framework do you prefer? ──────────────────────┐
 │ Pest                                                         │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which database will your application use? ───────────────────┐
 │ SQLite                                                       │
 └──────────────────────────────────────────────────────────────┘

 ┌ Would you like to run the default database migrations? ──────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘

Placez le code suivant dans le fichier de migration nouvellement créé dans base de données/migrations :

cd socialite-tests
composer require laravel/socialite

Cette migration ajoute des champs qui seront fournis par Socialite lorsque l'utilisateur s'authentifiera avec succès. Dans notre implémentation, nous ajoutons ces champs directement à la table utilisateur pour plus de simplicité. Si vous souhaitez prendre en charge plus de fournisseurs que Google, vous souhaiterez peut-être créer un tableau distinct pouvant stocker plusieurs fournisseurs par utilisateur.

Nous définissons le mot de passe pour qu'il soit nullable, car un utilisateur ne définira jamais de mot de passe s'il s'authentifie uniquement via Google. Si votre application permet l'authentification sociale et l'authentification par mot de passe, vous devez valider que le mot de passe n'est pas vide ou nul lorsqu'un utilisateur tente de se connecter via un mot de passe.

Exécutez la migration.

php artisan make:migration add_socialite_fields_to_users

Dans config/services.php, ajoutez le bloc de code suivant à la fin du tableau services. Vous pouvez trouver une liste complète des noms de services Socialite valides dans les documents de configuration.

<?php
// database/migrations/2024_12_31_075619_add_socialite_fields_to_users.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('google_id')->default('');
            $table->string('google_token')->default('');
            $table->string('google_refresh_token')->default('');

            // If your app allows both password and social logins, you
            // MUST validate that the password is not blank during login.
            // If you do not, an attacker could gain access to an account
            // that uses social login by only knowing the email.
            $table->string('password')->nullable()->change();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('google_id');
            $table->dropColumn('google_token');
            $table->dropColumn('google_refresh_token');
            $table->string('password')->nullable(false)->change();
        });
    }
};

Ajoutez ce qui suit à .env, en utilisant les informations d'identification de votre application Google que vous avez créées dans la section « prérequis ».

php artisan migrate

Remplacez le contenu de routes/web.php par le code suivant.

// config/services.php

'google' => [
    'client_id' => env('GOOGLE_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CLIENT_SECRET'),
    'redirect' => '/auth/google/callback',
],

Le nouveau code de ce fichier implémente les routes pour :

  • Redirection vers Google pour la connexion sociale avec les informations appropriées.
  • Gestion du rappel de Google. Cette route crée ou met à jour un utilisateur lors de la connexion, puis l'authentifie et le redirige vers la page d'accueil.
  • Déconnexion d'un utilisateur authentifié.

Enfin, remplacez le contenu de resources/views/welcome.php par le balisage suivant.

# .env

GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"

Une fois ceci terminé, nous pouvons tester manuellement l'application en exécutant le serveur de développement.

<?php
// routes/web.php

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\Two\InvalidStateException;
use Laravel\Socialite\Two\User as OAuth2User;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/auth/google/redirect', function () {
    return Socialite::driver('google')->redirect();
});

Route::get('/auth/google/callback', function () {
    try {
        /** @var OAuth2User $google_user */
        $google_user = Socialite::driver('google')->user();
    } catch (InvalidStateException $exception) {
        abort(400, $exception->getMessage());
    }

    $user = User::updateOrCreate([
        'email' => $google_user->email,
    ], [
        'google_id' => $google_user->id,
        'name' => $google_user->name,
        'google_token' => $google_user->token,
        'google_refresh_token' => $google_user->refreshToken,
    ]);

    Auth::login($user);
    return redirect('/');
});

Route::get('/auth/logout', function () {
    Auth::logout();
    return redirect('/');
});

Lorsque vous cliquez sur le lien Connexion avec Google, vous devez passer par le flux OAuth2 et être redirigé vers la page d'accueil où vous pouvez voir des informations sur l'utilisateur nouvellement créé par Google.

Test de Socialite avec Pest

Nos tests manuels fonctionnent, mais nous aimerions que des tests automatisés vérifient que nous ne cassons pas accidentellement cette fonctionnalité à l'avenir.

Nous pouvons créer un nouveau fichier de test avec la commande suivante.

<!-- resources/views/welcome.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laravel Socialite Testing Example</title>
</head>
<body>
    <h1>Laravel Socialite Testing Example</h1>
    @if (auth()->check())
        <p>User is authenticated.</p>
        <p>Name: {{ auth()->user()->name }}</p>
        <p>Email: {{ auth()->user()->email }}</p>
        <p><a href="/auth/logout">Logout</a></p>
    @else
        <p>User is not authenticated.</p>
        <p>
            <a href="/auth/google/redirect">Login with Google</a>
        </p>
    @endif
</body>
</html>

Remplacez le contenu du test/Feature/AuthRoutesTest.php nouvellement créé par ce qui suit.

php artisan serve

Comment fonctionnent les tests

Lors du test de l'itinéraire de redirection, nous testons que Socialite redirige vers la bonne URL et transmet les paramètres d'URL corrects.

Lorsque nous testons les routes de rappel, nous nous moquons de Socialite. La moquerie n'est pas mon option préférée : dans un monde idéal, nous pourrions remplacer Socialite par une autre implémentation OAuth2 et nos tests fonctionneraient toujours. Cependant, il n'existe pas de moyen simple de se connecter à la demande d'autorisation envoyée par Socialite pour saisir le jeton d'accès. Pour cette raison, la moquerie est l’approche la plus pratique pour tester Socialite.

Les API Fluent sont fastidieuses à simuler via Mockery : vous devez commencer par la fin de l'appel et remonter en arrière.

Voici la méthode Socialite invoquée par notre point de terminaison de rappel.

laravel new socialite-tests

Voici comment cela doit être moqué via Mockery :

 ┌ Would you like to install a starter kit? ────────────────────┐
 │ No starter kit                                               │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which testing framework do you prefer? ──────────────────────┐
 │ Pest                                                         │
 └──────────────────────────────────────────────────────────────┘

 ┌ Which database will your application use? ───────────────────┐
 │ SQLite                                                       │
 └──────────────────────────────────────────────────────────────┘

 ┌ Would you like to run the default database migrations? ──────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘

Enfin, nous avons un test pour garantir que la navigation directe vers l'URL de rappel en dehors du flux OAuth renvoie un code d'état 400. Nous avons encapsulé l'appel à Socialite::driver('google')->user() dans le point de terminaison de rappel dans un bloc try/catch. Si nous n'avions pas enveloppé l'appel Socialite dans un bloc try/catch et que quelqu'un avait tapé l'URL de rappel dans son navigateur, le point de terminaison lèverait une exception avec un code d'état HTTP 500. Si votre équipe a configuré une surveillance pour 500 codes d'état, quelqu'un pourrait être alerté au milieu de la nuit.

Conclusion

Il s'agit d'une intégration minimale, et il y a beaucoup plus qui pourraient être implémentées. Si nous implémentions une intégration avec un fournisseur social dans laquelle l'adresse e-mail de l'utilisateur pourrait changer, cette implémentation ne fonctionnerait pas car elle correspond à l'adresse e-mail. Si l'utilisateur pouvait modifier son adresse e-mail dans notre application, cette implémentation ne fonctionnerait pas non plus pour la même raison. Cependant, maintenant que vous avez vu comment tester Socialite, vous pouvez écrire des tests pour ces scénarios et modifier l'implémentation sous-jacente afin qu'ils réussissent.

J'ai lu beaucoup d'articles de blog et de forums sur Socialite avant de comprendre comment créer ma propre implémentation, comment la tester et à quoi je dois penser. J'aimerais remercier certains d'entre eux ici.

  • Comment j'écris des tests d'intégration pour les applications propulsées par Laravel Socialite par Stefan Zweifel
  • Forum ServerSideUp : Meilleures pratiques sociales, une conversation
  • Stack Overflow : Comment tester Laravel Socialite
  • Stack Exchange : lier ou non les connexions sociales avec un e-mail correspondant
  • Stack Exchange : Gérer les comptes sociaux connectés et les orphelins potentiels

Lisez-les si vous souhaitez approfondir. Faites-moi également savoir si vous seriez intéressé par la partie 2 de cet article dans laquelle j'examine la gestion de plusieurs fournisseurs sociaux, la gestion lorsqu'un utilisateur modifie son adresse e-mail ou la gestion des jetons d'actualisation.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn