Laravel5でJWT+Twitter OAuthの認証機能をつくる 3/3


認証機能のある、APIサーバーを作ろうとしたときにJWT（Json Web Token）という仕組みが使えそうだったので、
Laravel5で実装してみました。

[Laravel5でJWT+Twitter OAuthの認証機能をつくる 1/3](http://qiita.com/foresta/items/f770e8de906074107e57)
[Laravel5でJWT+Twitter OAuthの認証機能をつくる 2/3](http://qiita.com/foresta/items/cd75fa720f0fd275d084)
Laravel5でJWT+Twitter OAuthの認証機能をつくる 3/3 ←いまここ

#### その３ということでTwitter OAuthを用いてJWTトークンを発行するところまでを説明します。

# laravel-5-oauthのインストール＆準備

今回使用するライブラリは[oauth-5-laravel](https://github.com/oriceon/oauth-5-laravel)です。
oauth-5-laravel
https://github.com/oriceon/oauth-5-laravel

まずは、composer.jsonのrequireに、追記します。

```json
"require": {
     // ...
     "oriceon/oauth-5-laravel":"dev-master",
```

そしてインストール。

```
$ composer update
```

そして、config/app.phpに追記します。
今回も、providersとaliasesに追記します。

```php:config/app.php

'providers' => [
     // ...
     /*
      * oauth-5-laravel
      */
      Artdarek\OAuth\OAuthServiceProvider::class
],

'aliases' => [
    // ...
    'OAuth'     => Artdarek\OAuth\Facade\OAuth::class,
]

```

# DBの設定
今回、ID/PASSの他にtwitterのアカウントでも認証ができるようにするため、usersテーブルに変更を加えます。

カラムの変更するにあたって、doctrine/dbal依存パッケージをインストールする必要があるのでcomposer.jsonに追記し```composer update```します。

```json

“require”:  {
     // ...
     "doctrine/dbal": "~2.3”,
}
```

```
$ composer update
```

まずはmigrationの作成をします。

```
$ php artisan make:migration modified_users_table --table=users
```

--tableオプションをつけることで、テーブル変更のひな形を生成することができます。
passwordとemailをnullableに変更し、twitter_idのカラムを追加するために以下のように記載します。

```php:database/migration/yyyy_mm_dd_HHiiss_modified_users_table.php

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class ModifiedUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('password', 60)->nullable()->change();
            $table->string('email')->nullable()->change();
            $table->integer('twitter_id')->nullable();
        });
    }
 
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }
 }
```

そして、migrateします

```
$ php artisan migrate
```

以上でDBの準備は完了です。

# twitter-oauthの設定
今回TwitterのOAuth認証を用いるため、Twitterでのアプリ登録を先にしておいてください。
[こちらの記事](http://qiita.com/gabu/items/4a63e6b56dd50da1f528)など参考になりました。

Twitterのconsumer key, consumer secret等は、.envというファイルを用いて設定を行います。
.envはデフォルトで.gitignoreに入っているので、バージョン管理したくない情報（今回のTwitterのアプリIDなど）はこちらに記載していくと良いと思います。
（YOUR_CLIENT_IDがconsumer key, YOUR_CLIENT_SECRET_KEYがconsumer secretです）

以下のように定義しておきます。

```.env
TWITTER_CLIENT_ID=YOUR_CLIENT_ID
TWITTER_CLIENT_SECRET=YOUR_CLIENT_SECRET_KEY
```

設定ファイルを外だしします。

```
$ php artisan vendor:publish
```

ここで定義したものをoauth-5-laravelの設定ファイルから参照します。
configファイルは

```php:config/oauth-5-laravel.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | oAuth Config
    |--------------------------------------------------------------------------
    */

    /**
     * Storage
     */
    'storage' => 'Session',

    /**
     * Consumers
     */
    'consumers' => [

        'Twitter' => [
            'client_id'     => env('TWITTER_CLIENT_ID'),
            'client_secret' => env('TWITTER_CLIENT_SECRET'),
        ],
    ]
];

```

Strageは認証に用いるで、デフォルトのSessionで問題ないかと思います。
consumer keyの設定ですが、上記の```env(’TWITTER_CLIENT_ID’)```と```env('TWITTER_CLIENT_SECRET’)```で取得して設定しています。
今回はTwittterのみの紹介となりますが、FacebookやGoogleもこのライブラリでは扱えます。

# ルーティング
Twitter認証用に追加します。
URLは ```/authenticate/twitter``` とします。

```php:app/Http/routes.php
// ・・・
// twitter認証用
Route::get('authenticate/twitter', 'AuthenticateController@twitterAuth');
```
# コントローラー
コントローラを以下のように記載します。

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class AuthenticateController extends Controller
{
    /**
     * コンストラクタ
     */
    public function __construct(){
        // twitterAuth追加
        $this->middleware('jwt.auth', ['except' => ['showSignIn', 'auth', 'twitterAuth']]);
    }
    // 省略

    /**
     * authenticate with twitter oauth
     * @param Request $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function twitterAuth(Request $request)
    {
        // get data from request
        $token  = $request->get('oauth_token');
        $verify = $request->get('oauth_verifier');

        // get twitter service
        // エラーがでたのでデフォルトの StreamClient から CurlClient に変更
        \OAuth::setHttpClient('CurlClient');
        $tw = \OAuth::consumer('Twitter');

        if ( ! is_null($token) && ! is_null($verify) ){

            // twitterのAPIから認証情報を取得してくる
            $token = $tw->requestAccessToken($token, $verify);
            //$accessToken = $token->getAccessToken();
            //$accessTokenSecret = $token->getAccessTokenSecret();
            $params = $token->getExtraParams();
            $twitterId = $params['user_id'];
            $username = $params['screen_name'];

            // ユーザー情報の保存
            $user = User::firstOrNew(['twitter_id' => $twitterId]);
            $user->name = empty($user->name) ? $username : $user->name;
            $user->twitter_id = $twitterId;
            $user->save();

            // Json Web Tokenの発行
            $apiAccessToken = JWTAuth::fromUser($user);
            return response()->json(['token' => $apiAccessToken]);
        }
        else {
            $reqToken = $tw->requestRequestToken();

            $url = $tw->getAuthorizationUri(['oauth_token' => $reqToken->getRequestToken()]);

            return redirect((string) $url);
        }
    }
    // 省略
}
```
まず、コンストラクタでmiddlewareにtwitterAuthアクションを追加します。

```php
    /**
     * コンストラクタ
     */
    public function __construct(){
        // twitterAuth追加
        $this->middleware('jwt.auth', ['except' => ['showSignIn', 'auth', 'twitterAuth']]);
    }
```

twitterAuthの中ではまず、Twitterからのcallbackで呼ばれた時に送られるパラメータを以下のコードで取得しています。

```php
        // get data from request
        $token  = $request->get('oauth_token');
        $verify = $request->get('oauth_verifier');
```

その下では、OAuthライブラリのtwitter用のインスタンスを作成しています。
OAuthライブラリで使われているデフォルトのHttpClientはStreamClientなのですが、TwitterAPIへのアクセスがうまく行かずに、CurlClientに変更しています。（もしかしたら、デフォルトのStreamClientのままでも動くかも知れません）

```php
        // get twitter service
        // エラーがでたのでデフォルトの StreamClient から CurlClient に変更
        \OAuth::setHttpClient('CurlClient');
        $tw = \OAuth::consumer('Twitter');
```

そして、tokenとverifyがあればtwitterから認証情報を取得し、ユーザーを保存します。
どっちかがなければ、callbackじゃなく初回アクセスと見なして、twitterへのログイン画面へリダイレクトします。

今回は認証機能だけの実装なためtwitterのuser_idとscreen_nameのみを取得しています。
TwitterのAPIを利用したい等の場合は、twitterのaccess_tokenなども別途保持する必要があると思います。

# 動作確認

* /authenticate/twitterにアクセスして、twitterのログイン連携画面にリダイレクトされることを確認する
* ログイン処理したのちに、tokenが取得できれば成功！
* /authenticate?token=取得したtokenでユーザー情報が取得できればOK
 
※初回にアクセスした時に、認証したtwitterアカウントとtwitter_idで紐づくusersレコードが無ければ生成されます。

# まとめ
laravel5 + JWT + Twitter OAuthで認証機能をざっくりと作ってみました。
実際に運用しているサービスで使用する際はもっと考慮しなければ行けない点がありそうですが、
ひとまずサンプルとしては十分うごくかなーと思います。
実際に動かないとか、もっとこうした方がいいなどありましたらコメントください。