Laravel SocialiteでGoogleログインを実装する|OAuth設定から初回登録まで
作成日:2026.09.18
Laravel Socialiteを使ったGoogleログインを、OAuthクライアントの設定からコールバック、Laravelユーザーの登録・ログインまで解説します。Googleアカウントは変更されるメールアドレスではなくsubで識別し、初回登録時は検証済みメールアドレスを確認します。既存アカウントへ自動連携しない設計や、セッション管理、Socialite Fakeで確認するテスト項目も紹介します。
目次
LaravelアプリケーションにGoogleログインを追加する場合、Googleアカウントの認証だけでなく、認証後にLaravel側のユーザーをどう特定してログイン状態にするかも実装する必要があります。
この記事ではLaravel Socialiteを使い、Google認証へのリダイレクト、コールバック、ローカルユーザーの作成とログインまでを説明します。例はPHP 8.3、Laravel 13、Socialite 5.31を対象にした最小構成です。Google APIを呼び出す用途は扱わず、アクセストークンの保存もしません。
Laravel 13の標準的なusersテーブルとApp\Models\Userを使います。既存のMemberモデルなどを使っている場合は、モデル名とテーブル名を読み替えてください。
Laravelのメールアドレス・パスワード認証を既存テーブルへ追加する方法は、Laravel 13で既存のテーブルを使ってログイン機能を実装するで説明しています。この記事では、その認証方法とは異なるGoogle OAuthとローカルユーザーの対応付けを扱います。
Laravel Socialiteが担当する処理
Socialiteは、Googleの認可画面へ利用者を案内し、認証後のコールバックを処理してGoogleの利用者情報を取得するためのLaravel公式パッケージです。Google側の認証を終えても、Laravelアプリケーションのユーザー登録やログインが自動で完了するわけではありません。
コールバックでGoogleの利用者情報を受け取った後、アプリケーション側で既存ユーザーを検索し、必要に応じてユーザーを作成します。そのユーザーをAuth::login()へ渡すことで、Laravelのセッション認証へつなげます。
Socialiteをインストールする
プロジェクトのルートディレクトリでSocialiteをインストールします。
composer require laravel/socialite
この記事の例はSocialite 5.31を前提にしています。プロジェクトで利用するLaravelやPHPのバージョンに対応したパッケージバージョンを選び、導入後はcomposer.lockで実際のバージョンを確認します。
Google OAuthクライアントを設定する
Google Cloudでウェブアプリケーション用のOAuthクライアントを作成し、アプリケーションのコールバックURLを承認済みのリダイレクトURIへ登録します。ここでは次のURLを例にします。
https://example.com/auth/google/callback
Google Auth Platformの設定では、同意画面に表示するアプリ情報と対象ユーザーも設定します。テスト中にログインできるアカウントや、本番公開に必要な確認手続きは、アプリの対象ユーザーと要求するスコープによって異なります。利用するGoogle Cloudプロジェクトの現在の設定を確認してください。
以前書いたGoogle OAuth を PHP で実装する記事も参考にしてください。
Google Cloudへ登録するURIは、Laravelが実際に使うスキーム、ホスト、パスと一致させます。開発環境と本番環境でURLが異なる場合は、それぞれの環境に合わせて設定してください。本番環境ではHTTPSを使います。
config/services.phpへGoogleの設定を追加します。
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],
各環境の環境変数へOAuthクライアントの値を設定します。以下は値の形式を示す例であり、実際の認証情報ではありません。
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=https://example.com/auth/google/callback
クライアントシークレットをソースコードや公開リポジトリへ含めないでください。環境変数を変更した後に設定キャッシュを使っている場合は、対象環境でLaravelの設定キャッシュも更新します。
Google IDを保存するカラムを追加する
Googleアカウントとローカルユーザーを結び付けるため、usersテーブルへGoogleのユーザー識別子を保存するカラムを追加します。
php artisan make:migration add_google_id_to_users_table --table=users
作成されたMigrationを次のようにします。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->string('google_id')->nullable()->unique();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->dropUnique(['google_id']);
$table->dropColumn('google_id');
});
}
};
google_idは初回ログイン前のユーザーでは空なので、nullableにしています。ユニーク制約を付けることで、同じGoogleアカウントが複数のローカルユーザーへ登録されることを防ぎます。
Migrationを実行します。
php artisan migrate
Userモデルで一括代入を使う場合は、既存のfillable設定へgoogle_idを追加します。モデルの設定方式に合わせて変更してください。
protected $fillable = [
'name',
'email',
'password',
'google_id',
];
認証開始とコールバックのルートを追加する
routes/web.phpに、Google認証を始めるルートとコールバック用のルートを追加します。
use App\Http\Controllers\GoogleAuthController;
use Illuminate\Support\Facades\Route;
Route::get('/auth/google/redirect', [GoogleAuthController::class, 'redirect'])
->name('google.redirect');
Route::get('/auth/google/callback', [GoogleAuthController::class, 'callback'])
->name('google.callback');
ログイン画面には、認証開始ルートへ移動するリンクを追加します。
<a href="{{ route('google.redirect') }}">Googleでログイン</a>
Google認証後にユーザーを検索・作成する
コールバックでは、まずGoogleから返された識別子で既存ユーザーを検索します。OpenID ConnectのsubはGoogleアカウントを識別する値で、SocialiteのGoogleユーザーではgetId()から取得できます。メールアドレスは変更される可能性があるため、アカウントを継続して特定するキーには使いません。
新しいGoogleアカウントの場合は、メールアドレスが取得でき、Google側で検証済みであることを確認してから登録します。同じメールアドレスのローカルユーザーがすでに存在しても、メールアドレスだけを根拠に自動連携しません。既存アカウントとの連携が必要なら、ログイン済みの利用者が明示的に連携操作を行う別のフローを用意します。
次のコントローラー例は、Googleから名前・メールアドレスも取得するためにprofileスコープを要求しています。名前をアプリ内の登録画面で入力する設計なら、profileスコープを外して、初回ログイン後に入力を促す方法もあります。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\Two\InvalidStateException;
use Throwable;
class GoogleAuthController extends Controller
{
public function redirect(): RedirectResponse
{
return Socialite::driver('google')
->setScopes(['openid', 'email', 'profile'])
->redirect();
}
public function callback(Request $request): RedirectResponse
{
if ($request->query('error')) {
return to_route('login')
->withErrors(['google' => 'Googleログインを完了できませんでした。']);
}
try {
$googleUser = Socialite::driver('google')->user();
} catch (InvalidStateException) {
return to_route('login')
->withErrors(['google' => '認証状態を確認できませんでした。もう一度お試しください。']);
} catch (Throwable) {
return to_route('login')
->withErrors(['google' => 'Googleログインに失敗しました。時間をおいて再度お試しください。']);
}
$googleId = $googleUser->getId();
if (! is_string($googleId) || $googleId === '') {
return to_route('login')
->withErrors(['google' => 'Googleアカウントを識別できませんでした。']);
}
$user = User::query()->where('google_id', $googleId)->first();
if ($user === null) {
$email = $googleUser->getEmail();
$name = $googleUser->getName();
$emailVerified = ($googleUser->getRaw()['email_verified'] ?? false) === true;
if (
! is_string($email) || $email === '' ||
! $emailVerified ||
! is_string($name) || trim($name) === ''
) {
return to_route('login')
->withErrors(['google' => '登録に必要なGoogleアカウント情報を確認できませんでした。']);
}
if (User::query()->where('email', $email)->exists()) {
return to_route('login')
->withErrors(['google' => 'このメールアドレスは登録済みです。既存の方法でログインしてください。']);
}
$user = User::query()->create([
'name' => $name,
'email' => $email,
'google_id' => $googleId,
'password' => Hash::make(Str::random(64)),
]);
}
Auth::login($user);
$request->session()->regenerate();
return to_route('dashboard');
}
}
email_verifiedはGoogleのユーザー情報に含まれる値を確認しています。SocialiteのユーザーオブジェクトからgetRaw()で取得する生データの形式はGoogleプロバイダーに依存するため、他のOAuthプロバイダーへ置き換える場合は読み替えが必要です。
標準のLaravelユーザーテーブルではpasswordが必須の場合があるため、この例では利用者が知らないランダムな値をハッシュ化して保存しています。パスワード認証も提供するアプリケーションでは、Googleログイン用ユーザーのパスワード再設定やログイン方法を別途設計してください。パスワードを使わないアプリケーションでは、テーブル定義や認証設計を見直す方法もあります。
登録済みメールアドレスを持つユーザーへGoogleアカウントを自動で紐付けると、メールアドレスの扱いを誤った場合に他人のアカウントへログインできるおそれがあります。この例では自動連携せず、ログイン済みユーザーが自分のアカウント設定から連携を始める設計を前提にしています。
データベースにはemailとgoogle_idのユニーク制約を設けます。複数の初回ログインが同時に発生した場合も考慮し、本番では一意制約違反を適切に処理して、重複登録を防いでください。
セッションとエラーの扱いを確認する
ログイン成功後に$request->session()->regenerate()を呼び、ログイン前後でセッションIDを再生成します。通常のWebアプリケーションではSocialiteのセッション状態検証を有効にしたまま使います。セッションを使わないAPIなどの特別な構成でない限り、stateless()で状態検証を無効にしないでください。
利用者がGoogleの画面で認証をキャンセルした場合、コールバックのerrorパラメーターなどを確認してログイン画面へ戻します。状態不一致、設定不備、Google側の一時的なエラーも、利用者には再試行可能な一般的なメッセージを表示します。調査用ログを追加する場合は、トークン、認証コード、OAuth応答の生データを記録しないようにします。
SocialiteのFakeを使ってテストする
Socialiteには、プロバイダーへの実通信を行わずに認証結果を差し替えるFake機能があります。これを使い、少なくとも次のケースをFeature Testで確認します。
- 初回認証で検証済みメールアドレスのユーザーが作成され、ログイン状態になる。
- 同じGoogle IDで再度認証すると、既存ユーザーへログインする。
- メールアドレスが未検証、または取得できない場合は登録されない。
- 同じメールアドレスのローカルユーザーがいる場合に、自動連携されない。
- 認証キャンセルや状態不一致で、認証失敗として処理される。
Fakeを使ったテストはアプリケーションの分岐を確認するもので、Google Cloudの設定、実際のリダイレクトURI、ブラウザーのCookieやセッションまでは検証しません。公開前には、開発環境と本番環境のOAuth設定を別々に確認し、実際のGoogleアカウントで認証開始からログイン後の画面までを通して確認します。
Google APIを使わないならトークンを保存しない
SocialiteはGoogleから利用者情報を取得する過程でアクセストークンを扱います。ログイン後にGoogle APIを呼び出さないのであれば、アプリケーションのデータベースへアクセストークンやリフレッシュトークンを保存する必要はありません。
Google CalendarなどのAPIを使う場合は、ログインとは別に必要なスコープを確認します。要求する権限を最小限にし、トークンの保存場所、暗号化、更新、失効、利用者が連携を解除した場合の処理を設計してから追加します。
まとめ
Laravel SocialiteはGoogle OAuthのリダイレクトとコールバック、利用者情報の取得を支援します。その後、Googleの安定した識別子でローカルユーザーを探し、初回登録では検証済みメールアドレスを確認し、Laravelの認証状態へ反映する処理はアプリケーション側で用意します。
メールアドレスだけで既存アカウントへ自動連携せず、セッション状態検証を有効にし、Google APIを使わない場合はトークンを保存しないことが基本です。SocialiteのFakeを使ったテストと実際のOAuth設定確認は検証する範囲が異なるため、両方を分けて確認しましょう。
参考資料
奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。
これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。
【制作会社・企業様向けサポート】
Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。
ぜひ、プロフィールやWeb制作会社様向け業務案内、一般企業様向け業務案内もご覧くださいね。
Laravel 13のNamed Rate Limiterでレート制限を実装する:IP・ユーザー・入力値ごとの設定
2026.09.19
LaravelのNamed Rate Limiterを使い、無制限に繰り返されたくない処理へレート制限を追加する方法を解説します。RateLimiter::for()とthrottleミドルウェアの基本から、IPアドレス・ユーザーID・入力値による制限、複数の制限、429レスポンス、ログイン処理への応用、Redis利用時の注意点まで整理します。
Laravel Pintの使い方:PHPコードの書式を整えてCIで検査する
2026.09.15
Laravel Pintを使ってPHPコードの書式を整える方法を紹介します。ローカルでの自動修正、CIで書式違反だけを検査する--test、対象範囲の指定、pint.jsonの設定、Bladeファイルを扱う際の注意点を整理し、Pintを導入する判断基準も説明します。
GitHub ActionsでPHP・LaravelのCIを始める:テスト・Lint・ビルドを自動化するWorkflowの基本
2026.09.13
PHP・Laravelプロジェクトを対象に、GitHub ActionsのWorkflow、イベント、ジョブ、ステップの基本を説明します。Composerのテスト、npm ci、フロントエンドビルド、Laravel PintをCIへ組み込む方法に加え、featureブランチやPull Requestで実行される条件、失敗時のログ確認、Secrets・権限・Actionのバージョン管理についても整理します。
Laravel 13でArtisanコマンドを作成する:引数・オプション・dry-runの基本
2026.09.10
Laravel 13でArtisanコマンドを作成する方法を、引数・オプション・コンソール出力・終了コードの基本から解説します。日時指定した記事を定期的に公開するDB更新処理を例に、--dry-runで対象を確認してから安全に実行する方法や、スケジューラー・cronでの定期実行についても紹介します。