Implementar Laravel Socialite: Autenticación con Google, GitHub y Consideraciones

Video thumbnail

La autenticación social en Laravel se gestiona de forma excelente a través del paquete oficial Laravel Socialite. Este paquete nos permite integrar fácilmente enlaces de inicio de sesión con redes sociales en nuestra aplicación.

Más allá de la instalación técnica, quiero darte una perspectiva de por qué el uso de IA es tan interesante en este desarrollo. Este tipo de integraciones suelen ser tediosas porque, en muchos casos, solo se pueden probar correctamente en entornos de producción debido a las restricciones de las URLs de retorno (callbacks) de las redes sociales.

Configuración de Credenciales

Al igual que ocurre con plataformas de pago como PayPal o Stripe, para la autenticación social necesitamos un par de claves: el Client ID y el Secret ID. Estas se obtienen creando una aplicación en el panel de desarrolladores de cada plataforma:

  • Google y GitHub: Son las más sencillas de configurar.
  • Facebook y X (Twitter): Suelen ser más complejas debido a que sus paneles de administración cambian constantemente.

Una vez obtenidas, se configuran en el archivo config/services.php. En mi caso, prefiero definir las claves directamente allí o mediante variables de entorno para mantener el código limpio.

config\services.php

 // SOCIAL
    'github' => [
        'client_id' => env('GITHUB_CLIENT_ID', 'Iv23li***OI'),
        'client_secret' => env('GITHUB_CLIENT_SECRET', '5d6f3e***efa4'),
        'redirect' => '/auth/github/callback',
    ],

    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID', '9045***0384-***'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET', 'GOCSPX-***'),
        'redirect' => '/auth/google/callback',
    ],

Implementación de las Rutas de Redirección

Para que el sistema funcione, necesitamos un par de rutas principales: Redirect y Callback.

  1. Redirect: Es la función encargada de generar el token y redirigir al usuario hacia la red social correspondiente. Programáticamente, Socialite se encarga de todo el flujo de autorización.
  2. Callback: Es donde recibimos la información del usuario una vez que la red social autoriza el acceso.
return Socialite::driver('github')->redirect();

Te dejo el resto de la solución:

app\Http\Controllers\Social\SocialController.php

<?php

namespace App\Http\Controllers\Social;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;

class SocialController extends Controller
{
    /**
     * Redirect the user to the provider authentication page.
     *
     * @param string $provider
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function redirect(string $provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    /**
     * Obtain the user information from the provider.
     *
     * @param string $provider
     * @return \Illuminate\Http\RedirectResponse
     */
    public function callback(string $provider)
    {
        try {
            $socialUser = Socialite::driver($provider)->user();
        } catch (\Exception $e) {
            return redirect()->route('login')->withErrors(['error' => 'No se pudo autenticar con ' . ucfirst($provider)]);
        }

        // Try to find user by social ID
        $user = User::where($provider . '_id', $socialUser->getId())->first();

        if ($user) {
            Auth::login($user);
            return redirect()->intended('https://academy.desarrollolibre.net/');
        }

        // No user found by social ID, check for email
        $email = $socialUser->getEmail();

        if (!$email) {
            // Store social data in session to use after they provide an email
            session([
                'social_user' => [
                    'id' => $socialUser->getId(),
                    'name' => $socialUser->getName() ?? $socialUser->getNickname() ?? 'User',
                    'avatar' => $socialUser->getAvatar(),
                    'provider' => $provider,
                ]
            ]);

            return redirect()->route('social.email.request');
        }

        // Try to find user by email
        $user = User::where('email', $email)->first();

        if ($user) {
            // Link social ID to existing user
            $user->update([
                $provider . '_id' => $socialUser->getId(),
                'avatar' => $user->avatar ?? $socialUser->getAvatar(),
            ]);
        } else {
            // Create new user
            $user = User::create([
                'name' => $socialUser->getName() ?? $socialUser->getNickname() ?? 'User',
                'email' => $email,
                $provider . '_id' => $socialUser->getId(),
                'avatar' => $socialUser->getAvatar(),
                'password' => Hash::make(Str::random(24)),
                'email_verified_at' => $provider === 'google' ? now() : null,
            ]);

            event(new \Illuminate\Auth\Events\Registered($user));
        }

        Auth::login($user);

        return redirect()->intended('https://academy.desarrollolibre.net/');
    }
}

Las rutas:

routes/web.php

<?php

use App\Http\Controllers\Social\SocialController;
use Illuminate\Support\Facades\Route;

// auth social
Route::get('/auth/{provider}/redirect', [SocialController::class, 'redirect'])->name('social.redirect');
Route::get('/auth/{provider}/callback', [SocialController::class, 'callback'])->name('social.callback');


Agregamos las opciones en la ventana de login:

resources\views\pages\auth\login.blade.php

<div class="grid grid-cols-2 gap-4">
    <a href="{{route('social.redirect', 'github')}}" variant="subtle" class="w-full !py-3 bg-gray-800/50 border-gray-700 hover:bg-gray-800 transition-colors">
        <span class="text-white">GitHub</span>
    </a>

    <a href="{{route('social.redirect', 'google')}}" variant="subtle" class="w-full !py-3 bg-gray-800/50 border-gray-700 hover:bg-gray-800 transition-colors">
        <span class="text-white">Google</span>
    </a>
</div>

Modificamos la migración de usuario para agregar campos adicionales usamos en la autenticación social:

<?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('github_id')->nullable()->unique();
            $table->string('google_id')->nullable()->unique();
            $table->string('avatar')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn(['github_id', 'google_id', 'avatar']);
        });
    }
};

Registro y Login del Usuario Social

En el Callback, buscamos si el usuario ya existe en nuestra base de datos.

  • Si existe: Iniciamos sesión directamente.
  • Si no existe: Lo registramos. En este paso, es recomendable guardar el provider_id y el avatar.

Como los usuarios sociales no tienen una contraseña definida, lo ideal es generar un hash aleatorio de 24 caracteres. Esto asegura que nadie pueda entrar por el formulario de login tradicional sin adivinar esa clave imposible, manteniendo la cuenta segura.

El Desafío de los Datos de Usuario: El Email

Un punto crítico es el manejo del modelo de usuario. Laravel utiliza por defecto el modelo User, donde el email es el identificador principal. Sin embargo, muchas redes sociales (a excepción de Google) no siempre devuelven el email de forma predeterminada:

  • GitHub, Facebook y X: A veces solo entregan el nickname o nombre de usuario.
  • Solución: Si el email es indispensable para tu aplicación (como en mi caso para la recuperación de contraseñas), tienes dos opciones: adaptar tu app para que no dependa del email o implementar una ventana adicional para solicitárselo al usuario antes de completar el registro.

app\Http\Controllers\Social\SocialController.php

<?php
***
class SocialController extends Controller
{
    ***
    
    public function showEmailForm()
    {
        if (!session()->has('social_user')) {
            return redirect()->route('login');
        }

        return view('social.email');
    }

    public function storeEmail(\Illuminate\Http\Request $request)
    {
        $request->validate([
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
        ]);

        $socialData = session('social_user');

        if (!$socialData) {
            return redirect()->route('login');
        }

        $user = User::create([
            'name' => $socialData['name'],
            'email' => $request->email,
            $socialData['provider'] . '_id' => $socialData['id'],
            'avatar' => $socialData['avatar'],
            'password' => Hash::make(Str::random(24)),
        ]);

        event(new \Illuminate\Auth\Events\Registered($user));

        Auth::login($user);

        session()->forget('social_user');

        return redirect()->intended('https://academy.desarrollolibre.net/');
    }
}

Las rutas:

routes/web.php

<?php
***
// proveer el email cuando la red social NO lo trae
Route::get('/social/email', [SocialController::class, 'showEmailForm'])->name('social.email.request');
Route::post('/social/email', [SocialController::class, 'storeEmail'])->name('social.email.store');

Como puedes apreciar, si el login es el de GitHub, se registran los datos en la sesión y se pasa a una pantalla que solicita el email para poder registrar el usuario:

Pruebas en Producción y URLs de Retorno

Es fundamental entender que este desarrollo es difícil de probar en localhost. Las redes sociales exigen una URL de retorno válida (HTTPS y dominio real). Si intentas usar un dominio .test o localhost, la mayoría de las plataformas rechazarán la petición.

Por eso, utilicé la IA para generar gran parte del flujo estático de la interfaz y la lógica. Al ser un proceso con mucho "ensayo y error" en producción, contar con código sólido desde el inicio ahorra mucho tiempo.

Configuración en los Paneles de Desarrollador

  • GitHub: En Settings > Developer Settings, creas una OAuth App y configuras la Authorization callback URL.
  • Google Cloud: Debes crear un proyecto, habilitar las APIs correspondientes y en "Credenciales" crear un ID de cliente de OAuth. Es vital configurar los "Orígenes de JavaScript autorizados" y las "URIs de redireccionamiento".

Aprende a integrar Laravel Socialite para permitir el inicio de sesión con redes sociales. Client IDs, secretos y callbacks de forma fácil y segura y CLAVES para el uso.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english