laravel socialiteでOauth認証機能を加える

laravel socialiteでOauth機能を追加したのでその過程をまとめる

環境構築はsail


## laravel/socialiteのインストール
```
compooser require laravel/socialite
```

## 次に```config/services.php```にクライアントの設定をした
```
//グーグル認証のクライアント設定
    'google' => [
    'client_id' => env('GOOGLE_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CLIENT_SECRET'),
    'redirect' => env('GOOGLE_REDIRECT_URI'),
    ],
```

また、googleのクライアントのキーを取らなければいけないためgoogle consoleからoauth同意画面で設定して、認証情報を追加した。
この時に```リダイレクトURL```を設定しておかないと認証することができなくなる。

## 次に認証機能をまとめるSocialiteコントローラーを作成した
```
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;

class SocialiteController extends Controller
{
    //認証のリダイレクト処理
    public function redirect($provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    //認証のコールバック処理
    public function callback($provider)
    {
        $user = Socialite::driver($provider)->user();
        dd($user);
    }
}
```
## ```web.php```にリダイレクト用のルーティングとコールバック用のルーティングも追加する
//Google認証のルーティング
```
Route::get('/redirect/{provider}', [SocialiteController::class, 'redirect']);
Route::get('/callback/{provider}', [SocialiteController::class, 'callback']);
```

## 次に現在の```user```テーブルでは```password```カラムがあるがoauth認証だけの想定であればそもそも必要ないため```password```カラムは消す
```
./vendor/bin/sail artisan make:migration remove_password_column_from_users_table --table=users
```

マイグレーションファイル
```
<?php

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

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

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('password')->nullable();
        });
    }
};
```

それと同時に```user```テーブルには```provider_id```や```token```を持てるようにしたかったため追加する
```
./vendor/bin/sail artisan make:migration add_provider_columns_to_users_table --table=users
```

マイグレーションファイル
```
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('provider')->nullable()->after('email');
            $table->string('provider_id')->nullable()->after('provider');
            $table->string('avatar')->nullable()->after('provider_id');
            $table->string('token')->nullable()->after('avatar');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('provider');
            $table->dropColumn('provider_id');
            $table->dropColumn('avatar');
            $table->dropColumn('token');
        });
    }
};
```
またコントローラーではDBを触る処理を書きたくなかったため```App/Servives/SocialiteUserController```にサービスクラスを追加して以下のようにした
```
<?php

namespace App\Services;

use App\Models\User;

class SocialiteUserService
{
    //userが存在するか確認し、存在しない場合は新規作成するサービスクラス
    public function findOrCreateUser($providerUser,$provider)
    {
        return User::firstOrCreate(
            ['email' => $providerUser->getEmail()],
            [
                'name' => $providerUser->getName(),
                'provider' => $provider,
                'provider_id' => $providerUser->getId(),
                'avatar' => $providerUser->getAvatar(),
                'token' => $providerUser->token,
            ]
            );
    }
}
```

そしてSocialiteコントローラーにDIしてメソッドを委託する形で使うようにした
```
<?php

namespace App\Http\Controllers;

use App\Services\SocialiteUserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;

class SocialiteController extends Controller
{
    protected $socialiteUserService;
    public function __construct(SocialiteUserService $socialiteUserService)
    {
        $this->socialiteUserService = $socialiteUserService;
    }

    //認証のリダイレクト処理
    public function redirect($provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    //認証のコールバック処理
    public function callback($provider)
    {
        $user = Socialite::driver($provider)->user();

        //socialiteUserServiceクラスのインスタンスを生成してfindOrCreateUserメソッドを呼び出して登録
        $user = $this->socialiteUserService->findOrCreateUser($user, $provider);

        //ログイン処理
        Auth::login($user, true);

        return redirect('/home');
    }
}
```

認証できた！
![スクリーンショット 2025-02-01 22.55.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787859/665484a4-a67b-4040-db05-4bcb7e977638.png)


## 最後に
今回はstatefulな認証でできるようにしたが、spaなどの構成のときはstatelessになるので、token認証でoauth認証するようにしたい。
その際の記述としては、
```Socialite::driver($provider)->stateless()->redirect()```
と加えるだけでできそう。

## 参考
https://readouble.com/laravel/9.x/ja/socialite.html
https://zenn.dev/takumi_n/articles/laravel-socialite-password