Laravel Socialiteを使用したOAuth認証の実装

# はじめに
今回はプロダクト作成の際に使用したLaravel SocialiteによるOAuth認証と、Laravel Passportによるユーザー認証の組み合わせについてお話しようと思います。

まず**OAuth認証**については@TakahikoKawasaki様作成の以下の記事がとてもわかりやすかく参考になりましたのでご参照ください。

[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be "一番分かりやすい OAuth の説明")

## Laravel Socialiteとは？
Laravel Socialiteは、**LaravelによるWebアプリケーションでのソーシャル認証をサポートするライブラリ**です。

Laravel上で、OAuth2.0によるWeb APIの認証、OAuth認証が実装できるようになります。

Laravel SocialiteでOAuth認証を実装することには、いくつかの利点があります。

### セキュリティ
Socialiteは、OAuth 1.0やOAuth 2.0のセキュリティベストプラクティスに従っており、安全な認証フローを提供します。

また、ステートレスモードを利用することで、APIベースのアプリケーションでも安全に認証を行うことができます。

### 簡単な設定と使いやすさ
Laravel Socialiteは、Laravelの公式パッケージであり、Laravelフレームワークと深く統合されており、設定や使用が非常に簡単です。

## OAuth認証の実装
今回は**Laravel SocialiteでSpotifyアカウントのOAuth認証を実装する**。といった前提でやっていきます。
### 前提条件
- Laravelアプリケーションが設定済み
- Spotify Developerアカウントを登録し、クライアントIDとクライアントシークレットを取得済み、リダイレクトURIを設定済み

### Laravel Socialite、Passportの設定

今回**Laravel Passportはユーザー認証に使用します**。

Laravel Socialiteはソーシャル認証を簡単に実装するためのライブラリであり、APIトークンの生成は行いません。
APIトークンの生成は、Laravel Passportなどの別のパッケージを使用して行います。

**SocialiteとPassportを組み合わせることで、ソーシャルメディア認証とAPIトークン認証をスムーズに実装することができます**。

ターミナルで以下のコマンドを実行して、**Laravel SocialiteとLaravel Passportをインストール**します。
```bash
composer require laravel/socialite laravel/passport
```

**config/app.phpで、サービスプロバイダーに下記を追加**します。

```php
'providers' => [
Laravel\Passport\PassportServiceProvider::class,
Laravel\Socialite\SocialiteServiceProvider::class,
SocialiteProviders\Manager\ServiceProvider::class,
],

'aliases' => [
    'Socialite' => Laravel\Socialite\Facades\Socialite::class,
],
```
次に**マイグレーションを実行して必要なテーブルを作成**します。
必要なマイグレーションファイルはLaravel Passportをインストールした時点で作成されます。
```bash
php artisan migrate
```
更にサーバーとのやり取りに必要な**暗号鍵を生成**するために以下のコマンドを実行します。
```bash
php artisan passport:install
```
**ユーザーモデル (Models/User.php) にHasApiTokensトレイト**を追加します。

HasApiTokensトレイトを追加することによって、そのモデルがAPIトークンを生成、管理、検証するための機能を利用できるようになります。

```php
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}
```
## Spotifyアカウントの設定
Spotify Developer Dashboardにアクセスし、**設定したリダイレクトURI、取得したクライアントID、クライアントシークレット**をコピーして、それぞれ.envファイルに環境変数として設定します。

```.env
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret
SPOTIFY_REDIRECT_URI=https://yourapp.com/callback
```
OAuth認証におけるリダイレクトURIとは、ユーザーが認証後にリダイレクトされるアドレスです。

**リダイレクトURIを受け取り、ルートでコントローラにデータを渡す**。といった流れになります。

Spotifyアカウント側と環境変数側で設定を一致させます。

## コントローラーの作成

```php

<?php

namespace App\Http\Controllers\Auth;

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

class AuthController extends Controller
{
    public function redirectToSpotify()
    {
        /** @var SocialiteProviders\Spotify\Provider $driver */
        $driver = Socialite::driver('spotify');
        return $driver->stateless()->redirect();
    }

    public function handleSpotifyCallback()
    {

        try {
            /** @var SocialiteProviders\Spotify\Provider $driver */
            $driver = Socialite::driver('spotify');
            $spotifyUser = $driver ->stateless()->user();

            $user = User::updateOrCreate(
                ['spotify_id' => $spotifyUser->getId()],
                [
                    'name' => $spotifyUser->getName(),
                    'email' => $spotifyUser->getEmail(),
                    'avatar' => $spotifyUser->getAvatar(),
                    'spotify_access_token' => $spotifyUser->token,
                    'spotify_refresh_token' => $spotifyUser->refreshToken,
                    'spotify_token_expires_at' => now()->addSeconds($spotifyUser->expiresIn),
                ]
            );

            Auth::login($user, true);

            $userId = $user->id;
            $tokenResult = $user->createToken('Laravel Token');
            $token = $tokenResult->accessToken;

            session(['spotify_access_token' => $spotifyUser->token,
                        'token' => $tokenResult->accessToken,
                        'user_id' => $user->id]);

            $token = $tokenResult->accessToken;

            return redirect('/Top')

        } catch (\Exception $e) {
            Log::error('Spotify認証エラー: ' . $e->getMessage());
            Log::error('例外の詳細: ' . $e);
        }
    }
}
```

上記のコード、**redirectToSpotify**()では、

Socialiteが対応している複数のソーシャルメディアプロバイダー（Google、Facebook、Twitterなど）の中から、**Spotifyを選択し、OAuthプロバイダー**(**ユーザーの認証と認可を管理し、サービスを提供する組織やプラットフォーム**)を取得後、Spotifyの認証ページにリダイレクトしています。


次に**handleSpotifyCallback**()
このコードは、**SpotifyからのOAuth認証コールバックを処理するメソッド**です。

SocialiteでSpotifyからのユーザー情報を取得し、その情報を基にアプリケーション内のユーザーを作成または更新し、ユーザーをログインさせます。

**SpotifyでのユーザーIDがこのプロダクト内でのユーザーアカウントと紐づけられます**。

その後でLaravel Passportを使用し、APIトークンを生成、セッションにSpotifyから発行されたアクセストークンと、Laravel Passportで生成されたAPIトークン、そしてログインしたユーザーIDを保存し、Topページにリダイレクトします。

上記のコードに適切なルートやフロントエンドを作成することでOAuth認証が行えるはずです。

## まとめ
Laravel SocialiteによるOAuth認証と、Laravel Passportによるユーザー認証についてお話させていただきました。

今回プロダクト作成のためにOAuth認証を行っているときは、とにかく実装をするために、エラーを解消するためにと目の前のことで一杯になっていましたが、こうして流れとしてまとめてみると設定が一つでもないと成り立たないほどにかみ合っているように思いました。

自分も含めて大抵の人は、実装したことがない技術、もしくは構築環境が違う記事などは見ていても難しそうといった苦手意識が浮かぶと思いますが、こういった流れをなんとなくでも理解しておくと苦手意識が薄まり、実装する時の助けになると、実感しています。

長い文章を読んでくださり、ありがとうございました！