モダンな技術を全く知らないSIer5年目がWebサービスを作ることになったため0から勉強する ~Laravel jwt-auth編~

# はじめに
現在SIer5年目でjavascript(Jqueryのみ)、PHP(フレームワーク無し)を2年ほど、C#(Windowsアプリ)3年ほどやってきました。
色々なご縁があり、個人で最近Webサービスの立ち上げをやることになったのですが何せ本当にWebサービスを立ち上げるための知識がほぼ0に等しいです:sob:

ただ今後のキャリアを考えた時に今のままではいけないと思いチャレンジすることにしました。

まずは最初に技術を習得しないといけないので、学ぶ＆アウトプットするために毎回投稿していこうと思います。
今後身についていこうと思ってるのは下記のような技術です。
**AWS**
**Docker**
**CI/CD環境の構築**
**Laravel**
**Nuxt.js**
今回はLaravel+Nuxtについて学んでいきます。

# 今回学ぶこと
Laravel＋Nuxtでのログイン機能を作成していこうかと思います。
jwt-authを使用してログインを実現してきます。

# 参考サイト
[jwt-auth公式サイト](https://jwt-auth.readthedocs.io/en/develop/)

# 前提
Laravel 5.8
Nuxt　2.5.4 
jwt-auth

このチュートリアルで勉強してみました
[Laravel + NuxtJS authentication](https://www.youtube.com/playlist?list=PLeZmuYT0mSPNmQie2QyoY-3hxQxDZwi22)

## Laravel + NuxtJS認証：プロジェクトの設定
まずはLaravel、Nuxtのプロジェクト設定を行う

認証機能を追加する

```
php artisan make:auth
```

`/app/Models`にUser.phpを移動する

```
mkdir ./app/Models
mv ./app/User.php ./app/Models
```

参照しているファイルのパスを修正する'

```/app/Models/User.php
- namespace App\;
+ namespace App\Models;
```

```/app/Http/Controllers/Auth/RegisterController.php
- use App\User;
+ use App\Models\User;
```

```/config/auth.php
- 'model' => App\User::class,
+ 'model' => App\Models\User::class,
```

```/config/services.php
- 'model' => App\User::class,
+ 'model' => App\Models\User::class,
```

マイグレーションの実行

```
php artisan migrate
```

## composerでjws-authをインストールする


まずはインストールをする

```bash
 composer require tymon/jwt-auth dev-develop
```

次のコマンドを実行してパッケージ設定ファイルを公開します。

```bash
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
```

秘密鍵を作成する

```
php artisan jwt:secret
```

### 注意
Laravelのバージョンによって必要な手順が異なるため
[公式サイト](https://jwt-auth.readthedocs.io/en/develop/)を確認する

## jwt-authのトークン発行を有効にする

JWT用の関数を追加する

```/app/Models/User.php
- class User extends Authenticatable
+ class User extends Authenticatable implements JWTSubject
```

```/app/Models/User.php
public function getJWTIdentifier()
{
    return $this->getKey();
}

public function getJWTCustomClaims()
{
    return [];
}
```

## 認証ガードを設定する

```/config/auth.php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
```

## LaravelのAPIを定義する
LaravelでAPIのルーティングを設定していく

```/routes/api.php
<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::group(['prefix' => '/auth', ['middleware' => 'throttle:20,5']], function (){
   Route::post('/register', 'Auth\RegisterController@register');
   Route::post('/login', 'Auth\LoginController@login');
});

Route::group(['middleware' => 'auth:api'], function (){
    Route::get('/me', 'MeController@index');
    Route::get('/auth/logout', 'MeController@logout');
});

```

## 各APIを実装していく

### ユーザ登録APIのコントローラを実装する
※本来はPasswordは2回入力してもらうためにValidationは`confirmation`も入れるべきですが今回が簡単に実装するために省略します。

```App/Http/Controllers/Auth/RegisterController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Models\User;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Tymon\JWTAuth\JWTAuth;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.

    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '';

    protected $auth;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct(JWTAuth $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     * 登録関数をオーバライドする
     */
    public function register(Request $request)
    {
        $validator = $this->validator($request->all());
        if(!$validator->fails()){
            $user = $this->create($request->all());

            $token = $this->auth->attempt($request->only(`email`, 'password'));

            return response()->json([
                'success' => true,
                'data' => $user,
                'token' => $token
            ], 200);
        }

        return response()->json([
            'success' => false,
            'errors' => $validator->errors()
        ], 422);
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Models\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}

```

### ログインAPIのコントローラを実装する

```/app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\JWTAuth;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '';

    protected $auth;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct(JWTAuth $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return response()->json([
                'success' => false,
                'errors' => [
                    "You ve been locked out"
                ]
            ]);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        try{
            if(!$token = $this->auth->attempt($request->only('email', 'password'))){
                return response()->json([
                    'success' => false,
                    'errors' => [
                        'email' => [
                            "Invalid email address or password"
                        ]
                    ]
                ], 422);
            }
        } catch (JWTException $e){
            return response()->json([
                'success' => false,
                'errors' => [
                    'email' => [
                        "Invalid email address or password"
                    ]
                ]
            ], 422);
        }

        return response()->json([
            'success' => true,
            'data' => $request->user(),
            'token' => $token
        ], 200);



    }
}

```

### ユーザ情報取得APIとログアウトAPIのコントローラを実装する

```/app/Http/Controllers/MeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Tymon\JWTAuth\JWTAuth;

class MeController extends Controller
{
    protected $auth;

    public function __construct(JWTAuth $auth)
    {
        $this->auth = $auth;
    }

    public function index(Request $request){
        return response()->json([
            'success' => true,
            'data' => $request->user()
        ]);
    }

    public function logout()
    {
        auth()->logout();

        return response()->json([
            'success' => true,
        ]);
    }
}

```

# CORSの設定
CORSについては[ここがわかりやすかったので見てみてください](https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34)

Laravelでは[laravel-cors](https://github.com/barryvdh/laravel-cors)という便利なモジュールがあるので今回はこれを使用します。
※本来は信頼できるドメインのみアクセス可能にするべきだが今回は全部のアクセスを許可するようにする

```bash
composer require barryvdh/laravel-cors

```


```app/Http/Kernel.php
    protected $middlewareGroups = [
        ...

        'api' => [
            'throttle:60,1',
            'bindings',
            \Barryvdh\Cors\HandleCors::class,
        ],
    ];
```

# 補足
実装の動画と多少変更してる箇所もあります。(ディレクトリ構成も変えてますが好みかなと思います)
例えば、`api.php`の認可するための`middleware`は`jwt.auth`にしてますが
現在のjwt-authのドキュメントは、`auth.php`のguardsの設定を変更して
`middleware`を`api:auth`にしてます。

正直どちらが正しいかはわからなかったのでもしわかる方は教えていただけると幸いです。