In today's fast-paced digital world, user experience is paramount. One of the most critical aspects of any application is its login and registration process. Making it easy for users to log in or register to your app is key to a good user experience, and this is where Laravel Socialite shines. Social authentication, often called social login, allows users to access your application with just one click using their existing social media accounts, such as Google, Facebook, or GitHub. This isn't just a convenience; it's a game-changer for user adoption and retention.
As we know, social media is becoming increasingly popular, and virtually everyone has accounts on platforms like Gmail or Facebook. If your application offers social login, it instantly becomes more appealing. Many users prefer not to fill out lengthy sign-up or sign-in forms, and social authentication eliminates this barrier, encouraging more people to connect with your website. In the Laravel ecosystem, this complex task becomes incredibly easy thanks to the official and highly robust package called Laravel Socialite. In this comprehensive tutorial, we'll walk you through how to integrate Google, GitHub, and Facebook sign-in into your Laravel 12 application step by step, applying principles that are also relevant to Laravel 11 and earlier versions.
Why Social Authentication is a Game-Changer for User Experience
Imagine a user landing on your website, eager to explore its features. They're presented with a traditional registration form asking for their name, email, password, and possibly more. This can be a significant hurdle. Many users abandon the process if it feels too long or intrusive. Social authentication, powered by tools like Laravel Socialite, solves this by offering a streamlined, familiar, and secure login method.
Here's why it's a game-changer:
- Enhanced User Experience: Users can sign in with a single click using accounts they already trust and manage daily. This familiarity reduces cognitive load and friction.
- Increased Conversion Rates: By simplifying the signup process, you reduce abandonment rates and increase the likelihood of new user registrations.
- Reduced Password Fatigue: Users don't need to remember another set of credentials, minimizing password fatigue and support requests related to forgotten passwords.
- Access to Verified User Data: Social providers often offer basic, verified user information (like email address, name), which can be useful for initial user profiling and personalization.
- Improved Security: Leveraging the robust security infrastructure of major social platforms (like Google, Facebook, GitHub) means you don't have to build and maintain complex authentication systems from scratch.
By integrating social login, you're not just adding a feature; you're significantly enhancing your application's accessibility and appeal, directly contributing to its success.
In this tutorial, we'll learn how to log in to Laravel 12 with a Google account using the socialite composer package with Laravel Brezee. We can use Laravel UI, Laravel Jetstream, and Laravel Breeze to log in with a Gmail account.
The first stage
First, you must have a Laravel project with basic authentication. In this tutorial, I'm using a Laravel project with Brezee authentication and TailwindCSS styling. Additionally, this project already uses username- or email-based authentication. Those of you who still use email can still follow this tutorial without the username data field and the generateUsername method (skipping that process).
Getting Started with Laravel Socialite Integration
Integrating Laravel Socialite involves a few key steps: installation, configuration of your chosen providers, setting up your database, defining routes, and implementing controller logic. Let's break down each part.
socialite documentation: https://laravel.com/docs/12.x/installation
Installation of Laravel Socialite
First things first, you need to install the Laravel Socialite package via Composer. Open your terminal in your Laravel project's root directory and run the following command:
composer require laravel/socialiteConfiguration for Social Providers
Next, you'll need to configure your application to communicate with the social providers. This involves obtaining client IDs and secrets from each provider's developer console and adding them to your Laravel application's configuration. We'll store these sensitive credentials in your .env file and reference them in config/services.php.
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_CALLBACK_URL'),
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_CALLBACK_URL'),
],
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_CALLBACK_URL'),
],
// can add other services supported by socialiteNext, open your file .env Add provider variables such as client id, secret id, and callback URL.
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=http://localhost:8000/auth/github/callback
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=http://localhost:8000/auth/google/callback
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
FACEBOOK_CALLBACK_URL=http://localhost:8000/auth/facebook/callback
// add according to your service providerCustomizing the database Schema
Now, let's prepare the database. We need to add several new columns to the users table. Run the command to create a new migration file. Following this command:
php artisan make:migration add_provider_to_users_table --table=usersIn the migration file, add columns for provider_id, provider_name, provider _token, and provider_refresh_token. And don't forget to change the password column so that it can be left blank/nullable. If you have run PHP artisan migrate.
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('provider_id')->nullable()->after('password');
$table->string('provider_name')->nullable()->after('provider_id');
$table->string('provider_token')->nullable()->after('provider_name');
$table->string('provider_refresh_token')->nullable()->after('provider_token');
$table->string('password')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'provider_id',
'provider_name',
'provider_token',
'provider_refresh_token',
]);
$table->string('password')->nullable(false)->change();
});
}After running the migrate command php artisan migrate, then open the `user` model and fill in the $fillable and $hidden attributes.
# App\Models\User.php
protected $fillable = [
// your other attributes
'provider_id', // -> add this
'provider_name', // -> add this
'provider_token', // -> add this
'provider_refresh_token' // -> add this
];
protected $hidden = [
'password',
'remember_token',
'provider_token', // -> add this
'provider_refresh_token' // -> add this
];Create Routing
Next we will create two main routes. Before this, create two controllers with invoke methods in them; namely 1) ProviderRedirectController 2) ProviderCallbackController
php artisan make:controller Socialite/ProviderRedirectController --invokable
php artisan make:controller Socialite/ProviderCallbackController --invokableNext create two main routes. Open the routes/web.php
use App\Http\Controllers\Socialite\ProviderCallbackController;
use App\Http\Controllers\Socialite\ProviderRedirectController;
Route::get('/auth/{provider}/redirect', ProviderRedirectController::class)->name('auth.redirect');
Route::get('/auth/{provider}/callback', ProviderCallbackController::class)->name('auth.callback');The first route is to redirect users to the Google/provider login page. The second route is a callback route that will handle user data after they successfully log in from Google/provider. Here I have modified the route from the socialite documentation because we will be using multiple providers. Now let's fill the controllers.
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
# App\Http\Controllers\Socialite\ProviderRedirectController.php
public function __invoke(Request $request, string $provider)
{
if (!config("services.{$provider}")) {
return redirect()->route('login')->withErrors(['provider' => 'Invalid provider']);
}
try {
return Socialite::driver($provider)->redirect();
} catch (\Exception $e) {
return redirect()->route('login')->withErrors(['provider' => 'Something went wrong']);
}
}
# App\Http\Controllers\Socialite\ProviderCallbackController.php
public function __invoke(string $provider)
{
if (!config("services.{$provider}")) {
return redirect()->route('login')->withErrors(['provider' => 'Invalid provider']);
}
try {
$socialUser = Socialite::driver($provider)->user();
// Generate username from provider or fallback to name/email
$username = $this->generateUsername($socialUser, $provider); // -> If you don't use the `username` field delete this
$user = User::updateOrCreate([
'provider_id' => $socialUser->id,
'provider_name' => $provider,
], [
'name' => $socialUser->name,
'email' => $socialUser->email,
'email_verified_at' => now(),
'username' => $username, // -> If you don't use the `username` field delete this
'provider_token' => $socialUser->token,
'provider_refresh_token' => $socialUser->refreshToken,
]);
Auth::login($user);
return redirect('/dashboard');
} catch (\Exception $e) {
return redirect()->route('login')->withErrors(['provider' => 'Unable to login using ' . ucfirst($provider) . '. Please try again.']);
}
}
private function generateUsername($socialUser, $provider) // -> If you don't use the `username` field delete this method
{
// Try to get username from provider
$username = $socialUser->getNickname() ?? null;
// If no username from provider, generate from name or email
if (!$username) {
if (!empty($socialUser->name)) {
// Use name and convert to username format
$username = strtolower(str_replace(' ', '_', $socialUser->name)) . '_' . rand(1000, 9999);
} else {
// Use email prefix as fallback
$username = strtolower(explode('@', $socialUser->email)[0]) . '_' . rand(1000, 9999);
}
}
// Clean username (remove special characters, keep only alphanumeric and underscore)
$username = preg_replace('/[^a-z0-9_]/', '', strtolower($username));
// Ensure username is unique
$baseUsername = $username;
$counter = 1;
while (User::where('username', $username)->exists()) {
$username = $baseUsername . '_' . $counter;
$counter++;
}
return $username;
}First provider redirect controller. This controller is responsible for redirecting users to the provider using the socialite driver. We add validation to ensure that the requested providers are Google, GitHub, and Facebook. If the validation is correct, we call the socialite provider. Second provider callback controller. This is where the core process takes place. This controller retrieves user data from the provider. We add validation to ensure that the requested provider is Google, GitHub or Facebook. Then we check whether the user is already registered in our database. If not, we create a new user. If they already exist, we simply update their data. After that, we log in the user using the Auth login facade from Laravel and redirect them to the dashboard page.
Since our authentication system uses usernames as well, we handle the username field by adding username after email. Then we provide a method to handle the username before running update or create. If you are not using a username in your authentication, then skip/delete this step.
Button Login/Register View
Next, add a login button to our view page. Open the login or register blade file. Then add a link or button that points to the provider redirect route we created earlier. Here I've set a condition to determine whether the provider's client ID and secret ID exist. If so, the login button with that provider will appear in the view.
@if ((env('GOOGLE_CLIENT_ID') && env('GOOGLE_CLIENT_SECRET')) || (env('GITHUB_CLIENT_ID') && env('GITHUB_CLIENT_SECRET')) || (env('FACEBOOK_CLIENT_ID') && env('FACEBOOK_CLIENT_SECRET')))
<div class="space-y-3">
@if (env('GOOGLE_CLIENT_ID') && env('GOOGLE_CLIENT_SECRET'))
<a class="bg-background text-foreground flex w-full items-center justify-center space-x-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors duration-200 hover:opacity-70" type="button" href="{{ route('auth.redirect', ['provider' => 'google'] + (request()->has('redirect') ? ['redirect' => request()->get('redirect')] : [])) }}">
<svg class="h-auto w-4" width="40" height="42" viewBox="0 0 46 47" fill="none">
<path d="M46 24.0287C46 22.09 45.8533 20.68 45.5013 19.2112H23.4694V27.9356H36.4069C36.1429 30.1094 34.7347 33.37 31.5957 35.5731L31.5663 35.8669L38.5191 41.2719L38.9885 41.3306C43.4477 37.2181 46 31.1669 46 24.0287Z" fill="#4285F4" />
<path d="M23.4694 47C29.8061 47 35.1161 44.9144 39.0179 41.3012L31.625 35.5437C29.6301 36.9244 26.9898 37.8937 23.4987 37.8937C17.2793 37.8937 12.0281 33.7812 10.1505 28.1412L9.88649 28.1706L2.61097 33.7812L2.52296 34.0456C6.36608 41.7125 14.287 47 23.4694 47Z" fill="#34A853" />
<path d="M10.1212 28.1413C9.62245 26.6725 9.32908 25.1156 9.32908 23.5C9.32908 21.8844 9.62245 20.3275 10.0918 18.8588V18.5356L2.75765 12.8369L2.52296 12.9544C0.909439 16.1269 0 19.7106 0 23.5C0 27.2894 0.909439 30.8731 2.49362 34.0456L10.1212 28.1413Z" fill="#FBBC05" />
<path d="M23.4694 9.07688C27.8699 9.07688 30.8622 10.9863 32.5344 12.5725L39.1645 6.11C35.0867 2.32063 29.8061 0 23.4694 0C14.287 0 6.36607 5.2875 2.49362 12.9544L10.0918 18.8588C11.9987 13.1894 17.25 9.07688 23.4694 9.07688Z" fill="#EB4335" />
</svg>
<span>
{{ __('Continue with Google') }}
</span>
</a>
@endif
@if (env('FACEBOOK_CLIENT_ID') && env('FACEBOOK_CLIENT_SECRET'))
<a class="bg-background text-foreground flex w-full items-center justify-center space-x-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors duration-200 hover:opacity-70" type="button" href="{{ route('auth.redirect', ['provider' => 'facebook'] + (request()->has('redirect') ? ['redirect' => request()->get('redirect')] : [])) }}">
<svg class="w-4.5 h-auto" xmlns="http://www.w3.org/2000/svg" viewBox="-204.79995 -341.33325 1774.9329 2047.9995">
<path d="M1365.333 682.667C1365.333 305.64 1059.693 0 682.667 0 305.64 0 0 305.64 0 682.667c0 340.738 249.641 623.16 576 674.373V880H402.667V682.667H576v-150.4c0-171.094 101.917-265.6 257.853-265.6 74.69 0 152.814 13.333 152.814 13.333v168h-86.083c-84.804 0-111.25 52.623-111.25 106.61v128.057h189.333L948.4 880H789.333v477.04c326.359-51.213 576-333.635 576-674.373" fill="#1877f2" />
<path d="M948.4 880l30.267-197.333H789.333V554.609C789.333 500.623 815.78 448 900.584 448h86.083V280s-78.124-13.333-152.814-13.333c-155.936 0-257.853 94.506-257.853 265.6v150.4H402.667V880H576v477.04a687.805 687.805 0 00106.667 8.293c36.288 0 71.91-2.84 106.666-8.293V880H948.4" fill="#fff" />
</svg>
<span>
{{ __('Continue with Facebook') }}
</span>
</a>
@endif
@if (env('GITHUB_CLIENT_ID') && env('GITHUB_CLIENT_SECRET'))
<a class="bg-background text-foreground flex w-full items-center justify-center space-x-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors duration-200 hover:opacity-70" type="button" href="{{ route('auth.redirect', ['provider' => 'github'] + (request()->has('redirect') ? ['redirect' => request()->get('redirect')] : [])) }}">
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0C5.374 0 0 5.373 0 12 0 17.302 3.438 21.8 8.207 23.387c.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z" />
</svg>
<span>
{{ __('Continue with GitHub') }}
</span>
</a>
@endif
</div>
<div class="my-4 mb-3 flex items-center text-xs uppercase text-gray-400 before:me-6 before:flex-1 before:border-t before:border-gray-200 after:ms-6 after:flex-1 after:border-t after:border-gray-200 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">{{ __('or') }}</div>
@endif
The final step is to configure and obtain the client ID and secret ID. for each provider. This tutorial only shows how to obtain them for the Google provider.
Google Social Login Setup
To enable Google login, follow these steps:
- Go to Google Cloud Console: Visit console.cloud.google.com.
- Create a New Project: If you don't have one, create a new project.
Enable OAuth Consent Screen and Get started
selecting "External," click “Next” following the form and click on “Create”.
After Finish Click "Audience" menu and “Publish App” and click "Confirm"
Then Click "Clients" menu and click “Create client”
Once completed, you will receive the client_id and secret_id. Please copy and paste them into your
.envfile.
And that's it. Your Laravel application now has a Google login feature. Pretty easy, right?
With Socialite, the authentication process becomes much simpler.
If you want to use this Laravel starter pack project, you can click this repository link here And don't forget to give it a star.
All the required steps have been done, now you have to type the given below command and hit enter to run the Laravel app:
php artisan serve
npm run devNow, Go to your web browser, type the given URL and view the app output: http://localhost:8000/login
I hope it can help you... Thank you!!
zakialawi
Comments