How to Implement Google Socialite in Laravel

How to Implement Google Socialite in Laravel

Introduction

Social authentication has become an essential feature in modern web applications. In this article, we’ll explore how to implement Google authentication in Laravel using Laravel Socialite, with a focus on creating a flexible and secure implementation that works across different environments.


Steps for logging in with a Gmail Account in Laravel

  • Step 1: Installing Laravel

  • Step 2: Install Laravel Breeze

  • Step 3: Install Laravel Socialite

  • Step 4: Create a Google App

  • Step 5: Add the google_id column

  • Step 6: Create Routes

  • Step 7: Create Controller

  • Step 8: Update Blade File

  • Run Laravel App


Step 1: Installing Laravel

This step is not required; however, if you have not created the Laravel app, then you may go ahead and execute the following command:

laravel new social-auth-tutorial

Step 2: Installing Laravel Breeze

Now, in this step, we need to use the Composer command to install Laravel Breeze, so let's run the following command and install the Breeze package:

composer require laravel/breeze

Once the package is installed, we also need to run the breeze:install command to properly install Laravel Breeze into our project:

php artisan breeze:install

Step 3: Install Socialite

composer require laravel/socialite

Step 4: Create a Google App

Go to https://console.cloud.google.com/projectcreate and create a new project.

Once the project is created, click on the Get Started button of the Clients tab, since we need to create a new OAuth Client

You now need to provide the app details like the App name and support email, as shown in the image below:

On the audience tab, select External and click next:

You also need to provide the email contact so that Google can notify you about any changes that might happen to your app.

Once you complete this flow, you will be redirected to the OAuth Overview page. Click on the Create OAuth Client Button

On this page, set the application type as Web and provide the name of your app, also Add "Authorized redirect URI", and click on "CREATE" as shown in the following image:

Once that is created, you will now be able to copy the client_id and the client_secret, which we will paste into our .env, and to copy both of these credentials, we need to click on the edit button of the Clients Overview page:

On the right side of this page, you can see the client ID and the Client secret, copy and save these since we’ll be needing them later.

Now we have to set the Client ID, Client Secret, and the callback URL in the services.php config file, so open config/services.php and set the ID, secret and callback URL this way:

return [
    ....

    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_REDIRECT'),
    ],
]

Then you need to add Google client ID and client secret in the .env file, along with the GOOGLE_REDIRECT key:

GOOGLE_CLIENT_ID=XXXXXsvqcn3d.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=XXXXXT6tR1rWpR-Jxy3jkdzs
GOOGLE_REDIRECT=http://127.0.0.1:8000/auth/callback/google

Step 5: Add the google_id column

In this step, first, we have to create a migration to add the google_id In our users’ table. So let's run the below command:

php artisan make:migration add_google_id_column

We’ll also set the password as a nullable column, since we won’t be defining a password when a user signs up with Google.

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up(): void
    {
        Schema::table('users', function ($table) {
            $table->string('password')->nullable()->change();
            $table->string('google_id')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down(): void
    {
        $table->string('password');
        $table->dropColumn('google_id');
    }
};

Let’s now update the User Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'google_id'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

Step 6: Create Routes

After adding the google_id column, we have to add new routes for Google login. So let's add the below route in the routes.php file.

routes/web.php

<?php

use Illuminate\Support\Facades\Route;

use App\Http\Controllers\GoogleController;

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

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::controller(GoogleController::class)->group(function(){
    Route::get('auth/google', 'redirect')->name('auth.google');
    Route::get('auth/google/callback', 'handleCallback');
});

Step 7: Defining the Controller

After adding the routes, we need to define the necessary methods in our controller for handling the Google authentication flow.

app/Http/Controllers/GoogleController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Exception;
use App\Models\User;
use Illuminate\Support\Facades\Auth;

class GoogleController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function redirectToGoogle()
    {
        return Socialite::driver('google')->redirect();
    }

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function handleGoogleCallback()
    {
        try {

            $user = Socialite::driver('google')->user();
            $userFromDb = User::where('google_id', $user->id)->first();

            if($userFromDb){
                Auth::login($userFromDb);

                return redirect()->intended('home');

            }else{
                $newUser = User::updateOrCreate(['email' => $user->email],[
                        'name' => $user->name,
                        'google_id'=> $user->id,
                    ]);

                Auth::login($newUser);

                return redirect()->intended('home');
            }

        } catch (Exception $e) {
            // implement proper error handling here
        }
    }
}

Step 8: Updating the Frontend Files

The last step remaining in this flow is to update the login/registration file to include a link to the Google OAuth flow, so that the user can click on it, to either log in or register with their Google account.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

                        <div class="row mb-3">
                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

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

                        <div class="row mb-3">
                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>

                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>

                        <div class="row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <br/>
                                <a href="{{ route('auth.google') }}">
                                    <img src="https://developers.google.com/identity/images/btn_google_signin_dark_normal_web.png">
                                </a>
                            </div>
                        </div>

                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Running the Laravel App:

All the required steps have been done, now you have to type the command below and hit enter to run the Laravel app:

php artisan serve

Now, go to your web browser, type the given URL, and view the app output:

http://localhost:8000/login

Conclusion

And that’s it, we have now implemented a Google sign-in feature in our Laravel App. I hope this was helpful.


🔥 New Course Alert
Not Rated/5

Social Authentication in Laravel: A Practical Guide

Learn how to add social authentication to your Laravel apps by using the Laravel Socialite package.

30-Day Money Back
10+ Students
Limited Time
80% OFF
Enroll Now