【Laravel】JetstreamでSNS認証（ソーシャルログイン）

- Laravel8でのソーシャルログインの実装メモ。

# Laravel Socialite（ソーシャライト）
- OAuth認証のパッケージ。
- Facebook、Twitter、Google、GitHub、Instagram、YouTube、LINE、Slackなどに対応。
- [Laravel Socialite(GitHub)](https://github.com/laravel/socialite)
- [Laravelのドキュメント](https://laravel.com/docs/8.x/socialite)
- [Socialite Providers](https://socialiteproviders.netlify.app/)（※ Socialite 4.x は、PHP >= 7.1.3、 Laravel5.6 以上で！）


# 実装
## 0. インストール

```php:インストール
% composer require laravel/socialite            // socialite

// 例） 各メディア用もインストール ↓
% composer require socialiteproviders/twitter   // Twitter
% composer require socialiteproviders/line      // LINE
% composer require socialiteproviders/facebook  // Facebook
% composer require socialiteproviders/google    // Google
% composer require socialiteproviders/yahoo     // Yahoo      
```
```php:（エラー）インストールできない。。
Your requirements could not be resolved to an installable set of packages.
% composer update       // バージョンエラーだったので、アップデートし、再度インストールで解決。
```




## 1. ソーシャルメディア側でやること
- id と シークレットの取得。
- コールバックURLの設定（アプリ側に返すURLを指定）。
  - ローカルなら、http：//localhost:8000/login/twitter/callback とか。


## 2. アプリ側でやること
### 2-1. id、シークレット、コールバックするURL を設定

```php:環境変数で呼び出して使えるように設定
// .env
TWITTER_KEY="xxxxxxxxxx"
TWITTER_SECRET="xxxxxxxxxx"
TWITTER_REDIRECT_URI="http://localhost:8000/login/twitter/callback"
                  :

// config/services.php
'twitter' => [
    'client_id'        => env('TWITTER_KEY'),
    'client_secret' => env('TWITTER_SECRET'),
    'redirect'        => env('TWITTER_REDIRECT_URI'),
],
                  :
```

### 2-2. ルーティング
- 各ソーシャルメディアごとに、「/login/twitter、 /login/twitter/callback」って感じでルートを作成。
  - アプリ → ソーシャルメディアに送ってログインするルート と、 ログイン後に返ってくるルート。

```php:routes/web.php
Route::prefix('login/{provider}')->where(['provider' => '(line|twitter|facebook|google|yahoo)'])->group(function(){
    Route::get('/', [App\Http\Controllers\LoginController::class, 'redirectToProvider'])->name('sns_login.redirect');
    Route::get('/callback', [App\Http\Controllers\LoginController::class, 'handleProviderCallback'])->name('sns_login.callback');
});
```

### 2-3. プロバイダ
① ソーシャルメディアが使えるよーに、プロバイダを登録（config/app.php）。
② イベントリスナーを作成（Eventサービスプロバイダ）。

```php:プロバイダの作成と登録
// ① プロバイダの登録（config/app.php）
'providers' => [
    \SocialiteProviders\Manager\ServiceProvider::class,   // 追加
],

// ② イベントリスナーの作成（app/Providers/Eventサービスプロバイダ）
use SocialiteProviders\Manager\SocialiteWasCalled;
protected $listen = [
    Registered::class => [
        SendEmailVerificationNotification::class,
    ],
    // 追加 ↓  
     \SocialiteProviders\Manager\SocialiteWasCalled::class => [
        'SocialiteProviders\\Line\\LineExtendSocialite@handle',
        'SocialiteProviders\\Twitter\\TwitterExtendSocialite@handle',
        'SocialiteProviders\\Facebook\\FacebookExtendSocialite@handle',
        'SocialiteProviders\\Google\\GoogleExtendSocialite@handle',
        'SocialiteProviders\\Yahoo\\YahooExtendSocialite@handle',
                          :
    ],
];
```

### 2-4. コントローラー
- Laravel7.xまでは、ログイン関連はLoginコントローラが担当だったらしいけど、Laravel8（Jetstream）では、不要になり、デフォルトでは作成されない。なので、作成。

```php:Loginコントローラの作成
% php artisan make:controller Auth\\LoginController


// app/Http/Controllers/Auth/LoginController.php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class LoginController extends Controller {

    // メディア側へのリダイレクト
    public function redirectToProvider(Request $request) {
        $provider = $request->provider;
        return Socialite::driver($provider)->redirect();
    }

    // メディア側から返されるユーザー情報
    public function handleProviderCallback(Request $request) {
        $provider = $request->provider;
        $sns_user = Socialite::driver($provider)->user();
        $sns_email = $sns_user->getEmail();
        $sns_name = $sns_user->getName();

        // 登録済ならログイン。未登録ならアカウント登録してログイン
        if(!is_null($sns_email)) {
            $user = User::firstOrCreate(   // Userモデルに、レコードがあれば取得、なければ保存
                [ 'email' => $sns_email ],
                [ 'email' => $sns_email, 'name' => $sns_name, 'password' => Hash::make(Str::random())
            ]);
            auth()->login($user);
            session()->flash('oauth_login', $provider.'でログインしました。');
            return redirect('/');
        }
        return '情報が取得できませんでした。';
    }
}
```
- `redirectToProvider` ： OAuth側へのリダイレクト。
- `handleProviderCallback` ： OAuth認証後に返ってきた情報の処理（ユーザー情報の取得）。




### 2-5. ビュー
- ログイン画面にソーシャルログインボタンを設置。

```php:resources/views/auth/login.blade.php
<div>
    <a href="{{ route('sns_login.redirect', 'twitter') }}">Twitter</a>
</div>
```

### 例） ユーザー情報の取得方法
- SNS認証でログイン後に、返されるユーザー情報の取得方法のイロイロ。

```php:参）ユーザー情報の取得方法
$provider = $request->provider;
$user = Socialite::driver($provider)->user();  // ユーザー情報全部の取得

$user->getId();                      // id
$user->getNickname();                // @xxxxの部分とか
$user->getName();                    // 名前
$user->getEmail();                   // メールアドレス
$user->getAvatar();                  // アバターの画像のURL
$user->accessTokenResponseBody;      // トークン（ Array([oauth_token]=>xxxxx, [oauth_token_secret]=>xxxxx, [user_id]           =>xxxxx, [screen_name]=>xxxxx ) ）
```

### 参） エラー時のメモ

```php:ユーザー情報が格納できてるか？を確認してみる（コントローラー）
               :
$sns_user = Socialite::driver($provider)->user();
dd($sns_user);      // $sns_userの中身を確認
```

```php:キャッシュクリアしてみる
% php artisan config:clear
```