Login with Google from Scratch in Laravel 10
In this step-by-step guide, we'll learn how to implement Google authentication in Laravel 10 without relying on any external authentication packages. Instead, we'll use Google's OAuth 2.0 API endpoints directly, making the integration more customizable. This method ensures better control over the Google login process and enhances user experience with a smooth, secure authentication flow.
Table of Contents
- Initialize a New Laravel Project
- Add Authentication Scaffolding to Your Project
- Configure Google OAuth 2.0 Credentials
- Set Up Environment Variables for Google Login
- Prepare the Database with Migrations
- Create a Controller for Google Authentication
- Define Routes for Google Authentication
- Customize the Login View for Google Sign-In
- Run and Test Your Application
Step 1: Initialize a New Laravel Project
First, create a new Laravel project by running the following command:
<?php
composer create-project laravel/laravel google-integration
?>
Once the project is created, set up an authentication scaffold using Laravel's UI Bootstrap package.
Step 2: Install Authentication Scaffolding
Run the following commands to add authentication scaffolding:
<?php
composer require laravel/ui
php artisan ui bootstrap --auth
npm install
?>
This will set up basic authentication views and routes, which you can customize.
Set up a project on Google Developers Console. Add your Authorized Redirect URI, e.g., http://127.0.0.1:8000/auth/google/callback
.
Step 4: Configure Environment Variables
In your .env
file, add:
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://127.0.0.1:8000/auth/google/callback
These values will be used for the OAuth 2.0 flow to authenticate users via Google.
Step 5: Database Migration Setup
We need to update the users table to store additional information for Google login, such as the **google_id** and **picture_url**. Modify the
create_user_table migration file as follows:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('google_id')->nullable();
$table->string('picture_url')->nullable();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->rememberToken();
$table->timestamps();
});
Now, update the User model to include the new fields in the $fillable array:
protected $fillable = [
'name',
'email',
'google_id',
'picture_url'
]
Run this command to add tables in db.
php artisan migrate
Step 6: Create the Google Login Controller
Next, create a new controller to handle the Google login logic:
php artisan make: controller GoogleController
In this controller, we’ll implement two main methods: one to redirect to Google's OAuth service, and another to handle the callback.
Redirect to Google for Authentication
public function redirectToGoogle()
{
$clientId = env('GOOGLE_CLIENT_ID');
$redirectUri = env('GOOGLE_REDIRECT_URI');
$scope = 'email%20profile'; // Requesting email and profile permissions
// Redirect to Google's OAuth 2.0 authorization endpoint
return redirect("https://accounts.google.com/o/oauth2/auth?client_id=$clientId&redirect_uri=$redirectUri&response_type=code&scope=$scope");
}
Purpose:
This function generates a URL that directs the user to Google's OAuth 2.0 login page.
-
clientId: The
GOOGLE_CLIENT_ID
from your .env
file
(your app's identifier in Google's OAuth system).
-
redirectUri: The URL to which Google will redirect the user after
authorization, specified by
GOOGLE_REDIRECT_URI
in the .env
file.
-
scope: This specifies what user data you're requesting permission for
(email and profile in this case).
-
OAuth Flow: Redirects the user to the Google authorization page where the
user can grant your app permission to access their data.
Handle the Callback from Google
public function handleGoogleCallback(Request $request)
{
$code = $request->query('code');
if (empty($code)) {
return response()->json(['error' => 'Authorization code not provided'], 400);
}
$clientId = env('GOOGLE_CLIENT_ID');
$clientSecret = env('GOOGLE_CLIENT_SECRET');
$redirectUri = env('GOOGLE_REDIRECT_URI');
$response = Http::post('https://accounts.google.com/o/oauth2/token', [
'code' => $code,
'client_id' => $clientId,
'client_secret' => $clientSecret,
'redirect_uri' => $redirectUri,
'grant_type' => 'authorization_code',
]);
$tokens = $response->json();
if (isset($tokens['access_token'])) {
$accessToken = $tokens['access_token'];
$userData = $this->getUserData($accessToken);
$user = $this->findOrCreateUser($userData);
$userRecord = User::where('email', $user['email'])->first();
if ($userRecord) {
Auth::login($userRecord, true);
return redirect('/'); // Redirect the logged-in user
} else {
return redirect()->route('login');
}
}
return response()->json(['error' => 'Failed to obtain access token']);
}
private function getUserData($accessToken)
{
$response = Http::get('https://www.googleapis.com/oauth2/v3/userinfo', [
'access_token' => $accessToken,
]);
return $response->json();
}
Authorization Code:
When Google redirects back to your app, it includes a code (authorization code) in the URL.
Token Exchange:
The code is exchanged for an access token by making a POST request to Google's OAuth token endpoint:
client_id
, client_secret
, code
, redirect_uri
,
and grant_type
are required to request the token.
Access Token:
If successful, the response contains an access_token
which will be used to access
the user’s Google profile.
-
Purpose: This function retrieves the authenticated user’s profile data from
Google using the access token.
-
Google API: The
userinfo
endpoint returns the user's profile
information (email, name, profile picture, etc.).
-
Access Token: The access token is sent in the request to authenticate the API call.
Find or Create User in Database
private function findOrCreateUser($userData)
{
$user = User::where('email', $userData['email'])->first();
if (!$user) {
$user = User::create([
'google_id' => $userData['sub'], // Google ID
'name' => $userData['name'],
'email' => $userData['email'],
'picture_url' => $userData['picture'], // Profile picture
]);
}
return $user;
}
**Find Existing User:** Searches the database for a user with the same email as the one provided by Google.
**Create New User:** If no user exists with that email, a new user record is created using the data from Google:
**Google ID:** The user’s Google ID is saved as google_id.
**Name, Email, Profile Picture:** The user’s name, email, and profile picture are stored.
## Summary:
**Step 1:** Redirect users to Google for OAuth.
**Step 2:** Handle Google's callback by exchanging the code for an access token.
**Step 3:** Retrieve user data using the access token.
**Step 4:** Check if the user exists in the database. If not, create a new user, and then log them in.
Step 7: Define Routes for Google Authentication
In routes/web.php, define the routes for Google login and the callback:
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index']);
Route::get('auth/google', [GoogleController::class, 'redirectToGoogle']);
Route::get('auth/google/callback', [GoogleController::class, 'handleGoogleCallback']);
Step 8: Customize the Login View for Google Sign-In
Update the resources/views/auth/login.blade.php file to add a "Login with Google" button:
<!-- resources/views/auth/login.blade.php -->
@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"><strong>Login</strong></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"><strong>Email Address</strong></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"><strong>Password</strong></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">
<strong>Remember Me</strong>
</label>
</div>
</div>
</div>
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<a href="{{ url('auth/google') }}" class="btn btn-primary"><strong>Login with Google</strong></a>
<button type="submit" class="btn btn-primary">
<strong>Login</strong>
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
<strong>Forgot Your Password?</strong>
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Run and Test Your Application
Finally, run your Laravel application and compile the frontend assets:
php artisan serve
npm run dev
Output

