2019-10-06に更新

Laravel Socialiteを使ってTwitterアカウントでログイン機能

読了目安:12分

まず最初にどこから作成していけばよいかを考えてみます。

質問箱のようなサービスは、まずTwitterに投稿されるツイートを見て、そのツイートからサービスにアクセスし、そこから各ユーザーに質問を送る、という流れになっています。そのため、質問を送る側にログインは不要ですが、質問される側はユーザーとしての登録が必要になります。ということで全てのベースとなるユーザー登録の部分を作ってみたいと思います。

Twitterアカウントでログイン

ログインはTwitterアカウントでログインできるようにします。基本的にほとんどがTwitterユーザーのアクセスになると思いますので、数回のクリックで簡単にログインできるTwitterログインが一番適していると思います。

Twitterアプリケーションを作成

事前準備として、TwitterのデベロッパーダッシュボードにてTwitterアプリケーションを作成する必要があります。下記で作成を行ってください。

Twitter Developer

具体的な登録方法は下記などが参考になると思います。

Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報 - Qiita

Laravel Socialiteをインストール

LaravelでのSNS認証はLaravel Socialiteを使うと楽です。

Laravel Socialite - Laravel - The PHP Framework For Web Artisans

とりあえずインストールだけ先にしておきましょう。

composer require laravel/socialite

Twitterとの連携情報を設定

次に.envにTwitterアプリケーションとの連携情報を入れておきます。Twitter Developerページのアプリケーション詳細のここのConsumer API Keysというところです。

image.png

TWITTER_CONSUMER_KEY=yourtwitterconsumerkey
TWITTER_CONSUMER_SECRET=yourtwitterconsumersecret
TWITTER_REDIRECT=http://localhost:8000/auth/twitter/callback

一応.env.exampleにも空のものをコピーしておきます。

TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_REDIRECT=

上記は単に環境変数として指定しただけですので、これをLaravel側にも割り当てます。Socialiteには Socialite Providerというページがあり、様々な認証方法が掲載されています。Twitterの設定方法の解説どおりにconfig/services.phpに下記を追記します。

    'twitter' => [
        'client_id' => env('TWITTER_CONSUMER_KEY'),
        'client_secret' => env('TWITTER_CONSUMER_SECRET'),
        'redirect' => env('TWITTER_REDIRECT'),
    ],

上記の設定を利用するためのパッケージをインストールします。

composer require socialiteproviders/twitter

このパッケージを有効化するため、config/app.phpのproviders配列に下記を追加します。

    \SocialiteProviders\Manager\ServiceProvider::class,

これでLaravel側への設定自体は完了です。最後にTwitter側にリダイレクトURLを設定しておく必要があります。Twitter Developerのアプリケーション詳細画面から編集ボタンをクリックして編集画面を開き、先程.envに設定したリダイレクトURLであるhttp://localhost:8000/oauth/twitter/callbackを下記の箇所に設定します。

ログイン処理を実装

諸々の準備ができたので実際にログイン処理を実装していきます。とりあえずログイン用のコントローラを作成します。

php artisan make:controller AuthController

できあがったapp/Http/Controllers/AuthController.phpにログインの処理を入れるとひとまず下記のようになります。Socialiteをuseしてログイン用のアクションを入れています。

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Socialite;

class AuthController extends Controller
{
    public function login()
    {
        return Socialite::with('Twitter')->redirect();
    }

    public function callback()
    {
        $user = Socialite::driver('Twitter')->user();
        dd($user);
    }
}

上記をルーティングに追加してアクセスできるようにするためroutes/web.phpに下記を追記します。

Route::prefix('auth')->group(function () {
    Route::get('twitter', '[email protected]');
    Route::get('twitter/callback', '[email protected]');
});

これで一旦試してみましょう。下記にアクセスします。

http://localhost:8000/auth/twitter

問題なければ一旦Twitterのが画面が現れて、その後callback URLに戻り、callbackメソッドのddで認証したユーザー情報が表示されていると思います。

エラーになる場合はなにか間違っている可能性があります。.envを編集した後は状況によってはキャッシュが効いている可能性もあるかもしれませんのでphp artisan serve等で起動している場合は一度起動し直してみたほうが良いかもしれません。

何にしろここまでできたらほぼできたも同然です。あとは取得した情報をDBに入れて実際にそれでログインするだけです。

DBにユーザーデータの保存領域を作成

Laravelでは元々ユーザーデータの保存用のマイグレーションがあります。そのためその後の拡張用のマイグレーションを作っていきます。

SNS認証用のテーブル作成

SNS認証用のデータはsocial_usersというテーブルに保存することにします。usersに直接いれても良いのですが、セキュリティ上重要な情報を誤って漏らしてしまったり、今後GoogleやFacebook認証も追加したい、という時に大変になってしまう場合があるためusersテーブルと連携するためのテーブルとして作っておきます。

ひとまずsocial_users用のマイグレーションファイルを作成します。--createオプションで新規テーブル作成のマイグレーション雛形を作ってくれます。

php artisan make:migration create_social_users --create social_users

database/migrationsフォルダにファイルが追加されているため、そちらを下記のように編集します。

        Schema::create('social_users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('user_id')->unsigned()->index();
            $table->string('provider_user_id')->index();
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });

今回はTwitterのAPIなどは利用しないため、上記だけにしています。利用する場合は適宜保存しておくと良いでしょう。

ちなみにprovider_user_idというのは先程ddで出力した情報のidというところです。これはTwitter側でユニークですので、ログイン時にそれと照合し、存在しなければ新規ユーザー、存在すれば単にログインするだけ、という判定に使います。

桁数が増えすぎて数値だと扱えない言語やライブラリがあったりするため、文字列でも提供されています。アプリケーション側でも文字列として扱います。

provider_usr_idもuser_idも検索に利用しますのでインデックスを付けておきます。

ユーザーテーブルの拡張

ユーザー用のテーブルも拡張します。既存のマイグレーションは編集せず、追加のマイグレーションファイルを作成します。--tableオプションで既存テーブル用のマイグレーション雛形を作ってくれます。

php artisan make:migration add_social_columns_to_users --table users

さっきのddでダンプした情報を見ながら必要な情報を入れるためのカラムを追加していきます。こんな感じでしょうか。

    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('unique_id')->after('id');
            $table->string('avatar')->after('password');
            $table->text('bio')->after('avatar');
            $table->string('email')->nullable()->change();
            $table->string('password')->nullable()->change();
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('password')->change();
            $table->string('email')->change();
            $table->dropColumn('bio');
            $table->dropColumn('avatar');
            $table->dropColumn('unique_id');
        });
    }

unique_idはTwitterのプロフィールのURLで利用されている値です。それをそのまま作成するアプリケーション側でも利用します。(実際にはTwitter側で変更されて競合したりおかしくなったりするパターンもありますが、ひとまずそれは無視します。必要に応じてそのあたりの処理は追々追加してください)

マイグレーションを実行する

なにはともあれここまでで一度マイグレーションしておきましょう。emailとpasswordは空でもOKなように変更しています。変更機能は下記のパッケージが必要ですので先にインストールしておきます。

composer require doctrine/dbal

マイグレーションを実行します。

php artisan migrate

問題なければ下記のように表示されます。

Migrating: 2019_09_16_020812_create_social_users
Migrated:  2019_09_16_020812_create_social_users (0.03 seconds)
Migrating: 2019_09_16_021714_add_social_columns_to_users
Migrated:  2019_09_16_021714_add_social_columns_to_users (0.07 seconds)

ログイン時にデータを保存する

DB側のデータも準備できたので、実際にログイン時の処理を書いていきます。先程ddしていただけのcallbackメソッドのところです。

モデルの配置を変える

……とその前に、現在ユーザーのモデルがappフォルダ直下にあります。モデルが増える度にここにファイルが増えると邪魔ですので、app/Modelsフォルダを作ってそちらに移動しましょう。また、それに伴い各ファイルの中身も修正します。

app/Models/User.php はnamespaceを合わせます。

namespace App\Models;

あとは全体置換でApp\UserとなっているところをApp\Models\Userに置換しておきます。置換前後でcommitしておくとgit resetですぐもとに戻したりおかしな差分を探せますので安心です。

モデルの連携

まだsocial_usersのモデルがないため作っておきます。

php artisan make:model Models/SocialUser

そしてUserモデルと連携させましょう。User.phpに下記のメソッドを追加しておきます。

    public function socialUsers()
    {
        return $this->hasMany(SocialUser::class);
    }

これでUserは複数のSocialUserを持っている、というアソシエーションの設定ができるため、例えば$user->socialUsers等で簡単に呼び出すことができるようになります。

あとはSocialUser.phpに逆も定義しておきます。

class SocialUser extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

callbackの実装

さて、いい加減にcallbackメソッドを実装していきます。

    public function callback()
    {
        $providerUser = Socialite::driver('Twitter')->user();

        // 既に存在するユーザーかを確認
        $socialUser = SocialUser::where('provider_user_id', $providerUser->id)->first();

        if ($socialUser) {
            // 既存のユーザーはログインしてトップページへ
            Auth::login($socialUser->user, true);
            return redirect('/');
        }

        // 新しいユーザーを作成
        $user = new User();
        $user->unique_id = $providerUser->nickname;
        $user->name = $providerUser->name;
        $user->avatar = $providerUser->user['profile_image_url_https'];
        $user->bio = $providerUser->user['description'];

        $socialUser = new SocialUser();
        $socialUser->provider_user_id = $providerUser->id;

        DB::transaction(function () use ($user, $socialUser) {
            $user->save();
            $user->socialUsers()->save($socialUser);
        });

        Auth::login($user, true);
        return redirect('/');
    }

これに合わせてuseも増やしています。

use App\Models\User;
use App\Models\SocialUser;
use Auth;
use DB;
use Illuminate\Http\Request;
use Socialite;

これで再度ログインURLにアクセスして認証し、トップページに戻ってくれば正常に完了できています。あとは念の為もう一度そのままログインを試して、データが増えてしまわないか、重複エラーなどが発生しないかも確認しておきましょう。問題なければユーザ登録&ログインは完成です。

ログインユーザーを表示

しかしこれだとログインできているか分かりづらいため、welcome.blade.phpに下記のようなコードを追加してアイコンを表示してみましょう。

@auth
<div>
    <img src="{{ Auth::user()->avatar }}" width="48" height="48">
</div>
@endif

表示されました。

ちなみに、barryvdh/laravel-debugbarを導入しておけばフッターに表示されるデバッグバーでログイン状態も確認できます。

まとめ

Twitterアカウントでログインできるところまでを作成しました。次回はカレーは何日寝かせれば一番美味しいかを検証していきたいと思います。


view_list [連載] Laravelで質問箱みたいなサービスを作る
第1回 はじめに
第2回 Laravel Socialiteを使ってTwitterアカウントでログイン機能
第3回 質問できるようにする
第4回 Bootstrapでベースデザインを整える
第5回 質問に回答する機能を作る

だら@Crieit開発者

Crieitの開発者です。 主にLAMPで開発しているWebエンジニアです(在宅)。大体10年程。 記事でわかりにくいところがあればDMで質問していただくか、案件発注してください。 業務依頼、同業種の方からのコンタクトなどお気軽にご連絡ください。 業務経験有:PHP, MySQL, Laravel5, CakePHP3, JavaScript, RoR 趣味:Elixir, Phoenix, Node, Nuxt, Express, Vue等色々

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント