トモロログ

仕事や趣味でのメモや記録など

Laravel の twitter Oauthの設定

現在G's Academy というプログラミング学校に週末通っており、そこでPHPフレームワークLaravelを利用しています。

Laravelは認証系の仕組みがコマンド打つだけであっという間にできてしまい楽勝だったので、色気を出してtwitterOauth認証に手を出してハマってしまいました。 その時のメモです。

twitter API

まずはTwitterAPIキーを取得するために公式サイトでの設定が必要です。

https://apps.twitter.com/

Laravel Socialiteを使っています。

公式URL Laravel Socialite - Laravel - The PHP Framework For Web Artisans

参考URL

https://php-junkie.net/framework/laravel/socialite/ https://qiita.com/KeisukeKudo/items/18dd8a342a4bdd43913c

他にも ' laravel socialite twitter 認証' などでググると結構情報は出てきます。

インストールと初期設定

まずはsocialiteをインストールします。コンソールにて

>composer require laravel/socialite

twitter developersにて取得したAPIキー等は.envに記載

TWITTER_API_KEY = ''; //Consumer Key (API Key)
TWITTER_API_SECRET = ''; //Consumer Secret (API Secret)
TWITTER_CALLBACK_URL= 'http://xxxxxx' 
TWITTER_ACCESS_TOKEN = "";
TWITTER_ACCESS_TOKEN_SECRET = "";

上記は全てtwitter developersで取得した値と一致する必要があります。callback_urlは自身でせってしなければなりませんが、とりあえずは localhost 指定でも大丈夫なのでログイン後に遷移する先のルーティングを指定してください。

続いてconfig/services.phptwitter APIのキー設定を追記。これでモジュール内で.env内から具体的な値にアクセスすることができるようになります。

'twitter' => [
        'client_id'     => env('TWITTER_API_KEY'),
        'client_secret' => env('TWITTER_API_SECRET'),
        'redirect'      => env('TWITTER_CALLBACKURL'),
    ],

 モジュールの設定

twitterのログイン認証を行う実行先のルーティングを指定します。
web.phpにルート情報を追加します。下記は私の例ですので任意のルーティングで大丈夫です。以下自身の設定に合わせて読んでください。

Route::get('login/twitter', 'Auth\LoginController@redirectToTwitterProvider');
Route::get('login/twitter/callback', 'Auth\LoginController@handleTwitterCallback');

app/http/Controllers/Auth/LoginController.php にメソッド追加

use Laravel\Socialite\Facades\Socialite;

// 途中略

public function redirectToTwitterProvider() {
       return Socialite::driver('twitter')->redirect();
}

config/app.php の該当箇所に以下を追加

'providers' => [
    // これを追加
    Laravel\Socialite\SocialiteServiceProvider::class,


'aliases' => [
  // これを追加
  'Socialite' => Laravel\Socialite\Facades\Socialite::class,

以上の設定で /login/twitter のルートにアクセスすると自動的にtwitterの認証画面に遷移します。
そしてそこでログイン処理をすると上記で設定したcallback先に自動的に遷移します。素晴らしい。

 ユーザ情報の取得&ハマりポイント

ログイン処理後twitterからユーザ情報を取得します。一応公式や参考URLには

$user = Socialite::driver("twitter")->user(); 

で取得できるとありますが、何度やってもエラーになります。ここで結構ハマってしまいました。

※補足:その後サーバにデプロイしたケースでは上記の取り方でうまく行きました。エラーはローカル環境の時だけ起こることがわかりました。

他のSNSgoogle経由では大丈夫みたいですが、twitterはうまく行きませんでした。
そこでエラーの発生場所のソースコードを見てみると原因は SocialiteのAbstractProvider.php 内にありました。

protected function hasNecessaryVerifier()
    {
        return $this->request->has('oauth_token') && $this->request->has('oauth_verifier');
    }

原因はrequest に auth_token, oauth_verifier が設定されていないからのようです。
本来ならcallbackしてきたあとで自動で設定されるはずのようなのですがうまくいかずのようです。しかしこちらではすでに上記の情報は既知であるので、 直接代入して設定することにしました。

$token = env('TWITTER_ACCESS_TOKEN');
$secret = env('TWITTER_ACCESS_TOKEN_SECRET');

$user = Socialite::driver('twitter')->userFromTokenAndSecret($token, $secret);

Laravel Socialite公式にトークン等の設定する値が分かっている場合は上記のように設定しましょうとの説明がありました。

Laravel Socialite - Laravel - The PHP Framework For Web Artisans
Socialiteを使ってLaravelでTwitterログイン機能を実装 - Qiita

これでユーザ情報を取得できるはずです。しかし!
このようにしてtwitterからユーザ情報を取得した際に、emailが取れていないかもしれません。それはtwitter api の設定時にprivacy policy とterms_of_service のURLを設定していないからです。
それらを設定すればemail取得の許可の設定チェックボックスが表示され、それをオンに設定すればメール情報を取得することができます。以下参考。

omniauth-twitterでemail情報を取得する - Qiita

ユーザ情報の登録

twitterからログインしてきたユーザが新規であった場合は取得したユーザ情報をデータベースに登録します。 基本ユーザはemailでユニークにしたい。そしてtwitter経由登録の場合はパスワード無しになるのでusersテーブルのpasswordはnullableに変更する必要があります。

そこでUsersテーブルのpasswordをnullableに変更するためmigration

> php artisan make:migration change_password_users_table

migrationファイルの中

public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            //passwordカラムにnullを許可
            $table->string('password')->nullable()->change();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('password')->nullable(false)->change();
        });
    }

migrationの実行

>php artisan migrate

migrationでテーブルのカラム変更が初めての場合はエラーになるので、以下をインストールしてからmigrateを再実行しましょう。

> composer require doctrine/dbal

基本的にはメールアドレスでユーザの判断をしますが、twitter id も併用するように私はしました。
理由はtwitterに紐づいたemailが変更された時を考えてのことです。(あらゆるケースを想定しだすと何がベストか迷いますが、ひとまずそのように判断)。

その為にUserテーブルにtwitter_id に対応したカラムを追加しました。これは通常のmigrationで対応可能。

そして実際にデータベースの更新をしますが、Userのデータベース更新メソッドは特殊なようです。
単純に User::find や User::where で取得したオブジェクトに対してsaveメソッドを実行してもエラーになります(メソッドがないと怒られます)。
一旦ログイン操作をしてからfindやwhereするとうまくいきます。

$user = User::where('twitter_id', twitter_idの何か)->get();
$user->email = xxx@yyy.com; // 取得したemailの設定

 // ダメなパターン
$user->save(); // このままではエラーになる 

// good!
Auth::login($user); // これをやってから
$user->save();  // saveがうまくいった

ちなみに上記処理をLoginControllerでやっている場合はAuthを呼び出しているか注意(下記)。

use Illuminate\Support\Facades\Auth;
use App\User;

上記のsave方法以外にUserには、firstOrCreate メソッドがあり、これは上記のlogin操作なしでも使えます。
このメソッドでは、第一引数で検索して見つかればselect 、なければ第二引数と合わせてcreateし、Userのオブジェクトを返します。

$user = User::firstOrCreate(
 ['email'=> $user_info->email], // これで検索 第一引数 あれば$userに検索結果を返す
 ['name' => $user_info->name, 'twitter_id'=>$user_info->id,
                'img_url'=>$user_info->avatar]); // 第一引数で存在しなければこれも合わせてcreateし、新たなユーザを$userに返す

Auth::login($user); // その後の操作用にログイン

以上で一通り完了。あとは通常ログインユーザと同様に動くはずです。お疲れ様でした。