【Laravel】JWTを使って認証システムを構築する

インターンでLaravelでJWT(Json Web Token)を用いた認証システムの実装をしてみたので、まとめてみる。

## 参考にしたサイト
以下のサイトをとても参考にしました。
https://dev-yakuza.github.io/laravel/jwt/
https://jwt-auth.readthedocs.io/en/develop/

上記二つのサイトを参考に機能を実装しました。今回の記事は公式リファレンスを参考にして書いていきます。

## JWTをインストールする
1.以下のコマンドを実行じてjwt-authをインストールする。

```
$ composer require tymon/jwt-auth
```

2.config/app.phpのproviderの箇所に以下のコードを追加。

```php
'providers' => [
    ...
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]
```

3.configファイルを生成するために以下のコマンドを実行

```
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
```
4.シークレットキーを発行するために以下のコマンドを実行。

```
$ php artisan jwt:secret
```

## 諸々の準備
1.User.phpに以下のように変更。Tymon\JWTAuth\Contracts\JWTSubjectをUserモデル上で使えるようにするための処理。

```php
<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}
```
2.config/auth.phpを以下のように変更。jwtのguardを使えるようにするための処理。

```php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
```

3.routingの追加。

```php
Route::group([
    'middleware' => 'api',
    'prefix' => 'auth'
], function ($router) {
    Route::post('register', 'AuthController@register');
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
});
```

4.コントローラーの新規作成。これで準備完了です！

```
$ php artisan make:controller AuthController
```

## JWTでサインアップ機能とサインイン機能を実装する
完成コードは以下です。主要コードだけ切り取ったのでバリデーションとかは一切つけていません。

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Auth;
use App\User;
use JWTAuth;

class JWTAuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('api', ['except' => ['login']]);
    }

    public function register(Request $request){
        $user = new User;
        $user->fill($request->all());
        $user->password=bcrypt($request->password);
        $user->save();
        return $this->publishToken($request);
    }

    public function login(Request $request){
        return $this->publishToken($request);
    }

    protected function publishToken($request) {
        $token = auth('api')->attempt(['email' => $request->email, 'password' => $request->password]);
        return $this->respondWithToken($token);
    }

    protected function respondWithToken($token) {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60,
            'user' => auth('api')->user(),
        ]);
    }
}
```

以下の部分でユーザーの新規作成(サインアップ)とサインインを行う。最後にpublishTokenメソッドを呼び出す処理を書く。

```php
    public function register(Request $request){
        $user = new User;
        $user->fill($request->all());
        $user->password=bcrypt($request->password);
        $user->save();
        return $this->publishToken($request);
    }

    public function login(Request $request){
        return $this->publishToken($request);
    }
```

publishTokenメソッドでTokenの発行を行い、そのTokenを持たせた状態でrespondWithTokenメソッドを呼び出す。

```php
    protected function publishToken($request) {
        $token = auth('api')->attempt(['email' => $request->email, 'password' => $request->password]);
        return $this->respondWithToken($token);
    }
```

そしてrespondWithTokenメソッドでjsonとサインアップもしくはサインインしたユーザーの情報を返すようにする。

```php
    protected function respondWithToken($token) {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60,
            'user' => auth('api')->user(),
        ]);
    }
```

access_tokenにTokenが入った状態でレスポンスが帰って来れば成功です！