revolution/laravel-google-sheets
OAuth 2.0 authentication allows users to grant your application access to their personal Google Sheets. This method is ideal for user-facing applications where each user needs to access their own spreadsheets.
https://www.googleapis.com/auth/spreadsheets and https://www.googleapis.com/auth/drivehttp://localhost:8000/auth/google/callbackhttps://yourdomain.com/auth/google/callbackAdd the following to your .env file:
GOOGLE_CLIENT_ID=your-client-id-here
GOOGLE_CLIENT_SECRET=your-client-secret-here
GOOGLE_REDIRECT=http://localhost:8000/auth/google/callback
Update your config/google.php:
'client_id' => env('GOOGLE_CLIENT_ID', ''),
'client_secret' => env('GOOGLE_CLIENT_SECRET', ''),
'redirect_uri' => env('GOOGLE_REDIRECT', ''),
'scopes' => [
\Google\Service\Sheets::SPREADSHEETS,
\Google\Service\Drive::DRIVE,
],
'access_type' => 'offline', // Required for refresh tokens
'prompt' => 'consent select_account',
composer require laravel/socialite
Add to config/services.php:
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT'),
],
Create authentication controller:
// app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
class AuthController extends Controller
{
public function redirectToGoogle()
{
return Socialite::driver('google')
->scopes(config('google.scopes'))
->with([
'access_type' => config('google.access_type'),
'prompt' => config('google.prompt'),
])
->redirect();
}
public function handleGoogleCallback()
{
try {
$googleUser = Socialite::driver('google')->user();
$user = User::updateOrCreate(
['email' => $googleUser->email],
[
'name' => $googleUser->name,
'email' => $googleUser->email,
'google_access_token' => $googleUser->token,
'google_refresh_token' => $googleUser->refreshToken,
'google_expires_in' => $googleUser->expiresIn,
'google_token_created' => now()->timestamp,
]
);
auth()->login($user);
return redirect('/dashboard')->with('success', 'Successfully connected to Google!');
} catch (\Exception $e) {
return redirect('/login')->with('error', 'Authentication failed: ' . $e->getMessage());
}
}
public function logout(Request $request)
{
auth()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}
// routes/web.php
Route::get('/auth/google', [AuthController::class, 'redirectToGoogle'])->name('google.redirect');
Route::get('/auth/google/callback', [AuthController::class, 'handleGoogleCallback'])->name('google.callback');
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Add the necessary fields to store Google tokens:
// database/migrations/add_google_tokens_to_users_table.php
Schema::table('users', function (Blueprint $table) {
$table->text('google_access_token')->nullable();
$table->text('google_refresh_token')->nullable();
$table->integer('google_expires_in')->nullable();
$table->integer('google_token_created')->nullable();
});
Update your User model:
// app/Models/User.php
protected $fillable = [
'name',
'email',
'password',
'google_access_token',
'google_refresh_token',
'google_expires_in',
'google_token_created',
];
protected $hidden = [
'password',
'remember_token',
'google_access_token',
'google_refresh_token',
];
public function getGoogleTokenArray(): array
{
return [
'access_token' => $this->google_access_token,
'refresh_token' => $this->google_refresh_token,
'expires_in' => $this->google_expires_in,
'created' => $this->google_token_created,
];
}
public function hasValidGoogleToken(): bool
{
return !empty($this->google_access_token) && !empty($this->google_refresh_token);
}
use Revolution\Google\Sheets\Facades\Sheets;
// In your controller or service
public function getSheetData(Request $request)
{
$user = $request->user();
if (!$user->hasValidGoogleToken()) {
return redirect()->route('google.redirect');
}
try {
$token = $user->getGoogleTokenArray();
$values = Sheets::setAccessToken($token)
->spreadsheet('user-spreadsheet-id')
->sheet('Sheet1')
->all();
return view('sheets.data', compact('values'));
} catch (\Exception $e) {
// Token might be expired, redirect to re-authenticate
if (str_contains($e->getMessage(), 'invalid_grant') || str_contains($e->getMessage(), 'unauthorized')) {
return redirect()->route('google.redirect');
}
throw $e;
}
}
The package automatically handles token refresh when using setAccessToken() with a valid refresh token:
$token = [
'access_token' => $user->google_access_token,
'refresh_token' => $user->google_refresh_token,
'expires_in' => $user->google_expires_in,
'created' => $user->google_token_created,
];
// This will automatically refresh the token if expired
Sheets::setAccessToken($token)->spreadsheet('id')->sheet('Sheet1')->all();
// Get the updated token after refresh
$updatedToken = Sheets::getAccessToken();
if ($updatedToken) {
$user->update([
'google_access_token' => $updatedToken['access_token'],
'google_token_created' => time(),
]);
}
Create middleware to ensure users are authenticated with Google:
// app/Http/Middleware/RequireGoogleAuth.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RequireGoogleAuth
{
public function handle(Request $request, Closure $next)
{
$user = $request->user();
if (!$user || !$user->hasValidGoogleToken()) {
if ($request->expectsJson()) {
return response()->json(['error' => 'Google authentication required'], 401);
}
return redirect()->route('google.redirect');
}
return $next($request);
}
}
"redirect_uri_mismatch"
"invalid_grant" or "unauthorized"
"access_denied"
Create a test route to verify your OAuth setup:
Route::get('/test-oauth', function (Request $request) {
$user = $request->user();
if (!$user->hasValidGoogleToken()) {
return 'No Google token available. <a href="' . route('google.redirect') . '">Authenticate</a>';
}
try {
$token = $user->getGoogleTokenArray();
$sheets = Sheets::setAccessToken($token)->spreadsheetList();
return 'OAuth working! Found ' . count($sheets) . ' spreadsheets.';
} catch (\Exception $e) {
return 'OAuth error: ' . $e->getMessage();
}
})->middleware('auth');
How can I help you explore Laravel packages today?