Laravel SocialiteでTwitterログイン機能を実装する

# はじめに
Web系自社開発企業への転職を目標にLaravel+VuejsでWebアプリケーションを作成しています！
今回は、転職用ポートフォリオにTwitterログイン機能を実装したのでTwitter認証を実装するまでの流れを解説していきます。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [前提](#Chapter1)
1. [Laravel Socialiteのインストールと設定ファイルの編集](#Chapter2)
1. [Twitterログイン機能を実装する](#Chapter3)
1. [DB設定](#Chapter4)
1. [まとめ](#Chapter5)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 前提
- 「Twitter Developer」登録済み
- 「Callback URI/Redirect URL」の設定
- 以下を取得済み
    - 「API Key」
    - 「API secret key」
    - 「Access token」
    - 「Access token secret」

https://developer.twitter.com/en

Twitterログイン機能を実装するためには「Twitter API」を利用する必要があるので、まだ「Twitter API」を取得していない方は、上記サイトで「Twitter Developer」の登録を行ってください。

また、「Twitter Developer」の登録をする際に**`「Twitter APIを何に使うのか」`**を**`英語で200文字以上入力`**してアプリケーションの利用申請を行う場面があります。ここが個人的につまずいたポイントでした。

こちらが実際の申請文です。参考になれば幸いです。
　
Twitter API申請文

```
The purpose of using APIs is to deliver comfortable web services to users.
Specifically, I want to provide more users with useful learning by making the process of users logging in to my web services more comfortable.
The scope of using the Twitter API is only when a user logs in to the web service.
```
>
快適なWebサービスをユーザーに届けることがAPIを利用する目的です。
具体的には、私が運営するWebサービスにユーザーがログインするまでのプロセスを快適にすることで、より多くのユーザーに有益な学びを提供したい。
Twitter APIを利用する範囲としては、ユーザーがWebサービスにログインするときのみとする。


「Twitter API」の利用申請をする時に参考にしたサイトを下記に記載します。

- [翻訳ツール](https://www.deepl.com/translator)
- [Twitter APIの申請方法](https://qiita.com/QrioYagi/items/6afcbfb7193562921ee6)


<a id="#Chapter2"></a>
# Socialiteをインストールと設定
Laravelの公式パッケージであるLaravel Socialiteをインストールします。
[Laravel Socialite - Laravel公式](https://readouble.com/laravel/6.x/ja/socialite.html)

以下コマンドを実行

```
composer require laravel/socialite
```

Laravelの設定ファイルの編集

```config/services.php

'twitter' => [
        'client_id' => env('TWITTER_CLIENT_ID'),
        'client_secret' => env('TWITTER_CLIENT_SECRET'),
        'redirect' => env('APP_URL') . '/login/twitter/callback',
],
```

.envの編集

```php:.env
TWITTER_CLIENT_ID = TwitterAPI Key
TWITTER_CLIENT_SECRET = TwitterAPI Secret Key
```


<a id="#Chapter3"></a>
# Twitterログイン機能を実装する

`まずは、ルーティングを追加します。`

routes/web.phpで下記ルーティングを追加します。

```web.php
Route::prefix('login')->name('login.')->group(function () {
    Route::get('/{provider}', 'Auth\LoginController@redirectToProvider')->name('{provider}');
});
```
GoogleやFacebookなどの他サービスのアカウントを使ったログイン機能を実装したいという場面を想定して、今後の拡張性を持たせるために、{provider}の部分に、他サービスの名前を入力可能としました。

`次に、コントローラーを作成します。`
app/Http/Controllers/Auth/LoginController.phpを以下のように編集しました。

```LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = RouteServiceProvider::HOME;

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    //SNS認証ページへユーザーをリダイレクト
    public function redirectToProvider(string $provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    //ログイン
    public function handleProviderCallback(Request $request, string $provider)
    {
        //認証結果の受け取り
        $providerUser = Socialite::driver($provider)->user();

        //Google
        if($provider === 'google') {
            //Googleから取得したユーザー情報からメールアドレスを取得
            $user = User::where('email', $providerUser->getEmail())->first();

        //Twitter 
        } elseif($provider === 'twitter') {
            //Twitterから取得したユーザー情報からユーザーIDを取得
            $user = User::where('twitter_id', $providerUser->getId())->first();
        }

        //ログイン処理
        if ($user) {
            $this->guard()->login($user, true);
            return $this->sendLoginResponse($request);
        }

        //Google
        if($provider === 'google') {
            return redirect()->route('register.{provider}', [
                'provider' => $provider,
                'email' => $providerUser->getEmail(),
                'token' => $providerUser->token,
            ]);

        //Twitter
        } elseif($provider === 'twitter') {
            //DBにユーザー情報がなければ作成する
            return redirect()->route('register.{provider}', [
                'provider' => $provider,
                'twitter_id' => $providerUser->getId(),
                'token' => $providerUser->token,
                'tokenSecret' => $providerUser->tokenSecret,
            ]);
        }

    }
}
```

今回は、GoogleとTwitterの2つのサービスを使ってログイン機能を実装しました。
また、

- Googleアカウントを使ったログインではGoogleのユーザー情報から`メールアドレス`
- Twitterアカウントを使ったログインではTwitterのユーザー情報から`ユーザーID`

をそれぞれ取得して、usersテーブルに存在するかを調べています。

最初は、Googleと同じようにTwitterのユーザー情報から`メールアドレス`を取得して、Twitterアカウントを使ったログインを実装しようと考えましたが、

- Twitterアカウントにメールアドレスを設定していない場合、メールアドレスが取得できない
- GoogleとTwitterで同じメールアドレスを使っているとは限らない
- Twitterは、デフォルトではメールアドレスを取得できない（Twitter Developerで設定する必要がある）
- Twitterアカウントでのメールアドレスは非公開情報なので安易に取得するべきではない

これらの理由から、`メールアドレス`ではなく`ユーザーID`を取得してログイン機能を実装しました。

https://teratail.com/questions/284237

`最後に、ログインボタンを実装します。`
resources/views/auth/login.blade.phpを以下のように編集しました。

```login.blade.php
<a href="{{ route('login.{provider}', ['provider' => 'google']) }}" class="btn btn-block btn-danger mb-4 col-lg-8 col-md-9 col-sm-10 col-xs-12 mx-auto">
    <i class="fab fa-google mr-1"></i>Googleでログイン
</a>
<a href="{{ route('login.{provider}', ['provider' => 'twitter']) }}" class="btn btn-block btn-info mb-4 col-lg-8 col-md-9 col-sm-10 col-xs-12 mx-auto">
    <i class="fab fa-twitter mr-1"></i>Twitterでログイン
</a>
```


<a id="#Chapter4"></a>
# DB設定
Laravelアプリケーションを作成した際に作られるデフォルトのusersテーブルに、カラムを新たに追加する必要があります。

app/User.phpを以下のように編集しました。

```User.php
protected $fillable = [
    'name', 'email', 'password', 'twitter_id',
];
```

database/migrations/2014_10_12_000000_create_users_table.phpを以下のように編集しました。

```php:2014_10_12_000000_create_users_table.php
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name')->unique();
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('twitter_id')->nullable();
            $table->string('password')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }
```


<a id="#Chapter5"></a>
# まとめ
今回は、Laravel SocialiteでTwitterログイン機能を実装するまでの流れを解説しました。
本記事が、LaravelアプリケーションのTwitterログイン機能のご参考となりましたら幸いです。

<a id="#reference"></a>
# 参考文献
- [Twitter APIを使うためのAPI KeyやAccess Tokenの取得方法｜アプリケーションの利用申請から紹介](https://wporz.com/twitterapi-apikey-accesstoken/#Access_tokenAccess_token_secret)
- [TwitterAPI+LaravelでTwitterログイン機能を実装する](https://qiita.com/sasao3/items/ffa31e2b591fd7a22ba2)
- [【図解あり】LaravelにTwitterでログインする機能を実装する（Laravel8.x対応 ）](https://uedive.net/2021/5471/laravel8twitterlogin/)