Laravel9 Socialite LINEログイン実装


## 開発環境
Laravel Framework 9.7.0  
PHP 8.1.3  
Sailを使用

## Laravel Socialiteをインストールする
```php
sail composer require laravel/socialite
```

## SocialiteのLINEプロバイダーを追加する
```php
composer require socialiteproviders/line
```

## LINE Developersで取得した値を.envファイルに追記する
```php
LINE_CLIENT_ID='取得したチャンネルID'
LINE_CLIENT_SECRET='取得したチャネルシークレット'
LINE_REDIRECT_URI='(アプリのURL)/login/line/callback'
※開発時にngrokを使用して設定した際のリダイレクトURIの一例↓↓
LINE_REDIRECT_URI='https://75ef-118-238-203-22.ngrok.io/login/line/callback'
```
## config/services.phpに設定を追記する（LINE以外も同様）
```php:config/services.php
'line' => [
    'client_id' => env('LINE_CLIENT_ID'),  
    'client_secret' => env('LINE_CLIENT_SECRET'),  
    'redirect' => env('LINE_REDIRECT_URI') 
],
```
## app/EventServiceProvider.phpにイベントを追記する（LINE以外も同様）
```php:app/EventServiceProvider.php
protected $listen = [
      Registered::class => [
          SendEmailVerificationNotification::class,
      ],
      // 下記を追加、先にLINE以外が追加してあればLINEの部分を追加
      \SocialiteProviders\Manager\SocialiteWasCalled::class => [
          'SocialiteProviders\\Line\\LineExtendSocialite@handle',
      ],
  ];
```

## 保存したい値を入れる,テーブル、カラムを用意しておく(一例)
```php:database/migrations/2014_10_12_000000_create_users_table.php
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique()->nullable(); // 変更
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password')->nullable(); // 変更
        $table->enum('provider', ['line','任意で追加 twitter等']);
        $table->string('provided_user_id'); // 各プロバイダーから帰るuidを入れる
        $table->rememberToken();
        $table->timestamps();
    });
}
```
## マイグレーションファイルを変更したらsail php artisan migrateでテーブルを作成

```php:
sail php artisan migrate
```
## テーブルに合わせてModelを変更する（一例）
```php:app/Models/User.php
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'provider', // 追加
        'provided_user_id',// 追加
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
```
## ルーティングを追加（一例）
```php:routes/web.php
// プロバイダーへのリダイレクト処理のルート
Route::get('/login/{provider}', [LoginController::class, 'redirectToProvider']);
// プロバイダーからのコールバック時のルート
Route::get('/login/{provider}/callback', [LoginController::class, 'handleProviderCallback']);

Route::get('/{any?}', function () {
    return view('welcome');
})->where('any', '^(?!api\/)[\/\w\.-]*');
```


## コントローラーの実装（一例）
```php:app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Exception;
use Laravel\Socialite\Facades\Socialite;

class LoginController extends Controller
{

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
    // リダイレクトの処理の追加
    public function redirectToProvider($provider)
    {
        return Socialite::driver($provider)->redirect();
    }
    // プロバイダーからのコールバック時の処理の追加
    public function handleProviderCallback($provider)
    {
        $provided_user = Socialite::driver($provider)->stateless()->user();

        $user = User::where('provider', $provider)
            ->where('provided_user_id', $provided_user->id)
            ->first();

        if ($user === null) {
            $user = User::create([
                'name'               => $provided_user->name,
                'provider'           => $provider,
                'provided_user_id'   => $provided_user->id,
            ]);
        }

        Auth::login($user);
    }

    public function login(Request $request)
    {

        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => 'required',
        ]);

        if(Auth::attempt($credentials)) {
            $request->session()->regenerate();
            return response()->json(['name' => Auth::user()->email], 200);
        }

        throw new Exception('ログインに失敗しました。再度お試し下さい');
    }

    public function logout()
    {
        Auth::logout();
        return response()->json(['message' => 'Logged out'], 200);
    }
}

```