LaravelAPI + Nuxt でMultiAuthを実装する

# はじめに

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

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

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

# 前提

既に必要なマイグレーションファイルは作成されていて、あとは実装するのみの状態

## バックエンド
- Laravel 5.8
- jwt-auth

## フロントエンド
- nuxt 2.4
- auth-module



# やりたいこと

下記の3つのロールがあったとして認証の切り分けを行いたい
それぞれ認証されていても他のロールの機能は使えないようにしたい
今回はAPI側の認証を実装します。

1. 購入ユーザ
2. 出店ユーザ
3. イベント管理者

# 参考サイト
[Multi-User API Authentication Using Laravel JWT](https://medium.com/@JinoAntony/multi-user-api-authentication-using-laravel-jwt-8ae572b0c4cf)

[Multi-user Authentication with Laravel and JWT](https://medium.com/@OlabodeAbesin/multi-user-authentication-with-laravel-and-jwt-dcbe0e4b4fbf)

# 実装手順
1. `config/auth.php`で認証の設定をする
2. `routes/api.php`で認証が必要なAPIを定義する
3. `App/Exceptions/Handler.php`で未認証エラー時にJsonで返すように実装する
4. 各モデルの実装
5. 各コントローラの実装

## 認証ガードの設定

`config/auth.php`

ロール毎に認証ガードを設定する

```auth.php
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'users' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],

        'shops' => [
            'driver' => 'jwt',
            'provider' => 'shops',
        ],

        'marche' => [
            'driver' => 'jwt',
            'provider' => 'marches',
        ],
    ],
```

認証時に紐づくModelを定義する

```auth.php
'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'shops' => [
            'driver' => 'eloquent',
            'model' => App\Models\Shop::class,
        ],

        'marches' => [
            'driver' => 'eloquent',
            'model' => App\Models\Marche::class,
        ],

    ],
```

パスワードリマインダーの際に使用するテーブルを設定する

```auth.php
'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        'shops' => [
            'provider' => 'shops',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        'marches' => [
            'provider' => 'marches',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],
```

## 認証が必要なAPIを定義する

`routes/api.php`
認証が必要なAPIは`'middleware' => ['auth:XXXX']`で定義をする

```api.php
Route::group(['prefix' => 'user','middleware' => ['auth:users']], function (){
    Route::get('/me', 'Client\MeController@index');
});

Route::group(['prefix' => '/shop','middleware' => ['auth:shops']], function (){
    Route::get('/me', 'Shop\MeController@index');
});

Route::group(['prefix' => '/marche','middleware' => ['auth:marches']], function (){
    Route::get('/me', 'Marche\MeController@index');
});
```

## 未認証エラー時にJsonで返すように実装する
APIが認証エラー時には、デフォルトのままだとJson形式で返されないため
認証エラー時にJson形式で返却する

```App/Exceptions/Handler.php
protected function unauthenticated($request, AuthenticationException $exception)
{
    return response()->json(['message' => $exception->getMessage()], 401);
}
```

[参考資料](https://readouble.com/laravel/5.8/ja/authentication.html)

## モデルの実装

ユーザモデルを代表例としてこんな感じで実装


```User.php
<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Notifications\Client\ResetPasswordNotification;
use Tymon\JWTAuth\Contracts\JWTSubject;
use App\Notifications\Client\VerifyEmail;

class User extends Authenticatable implements JWTSubject, MustVerifyEmail
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'name_phonetic', 'email', 'password', 'address', 'phone_number'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }

    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }

    public function sendEmailVerificationNotification()
    {
        $this->notify(new VerifyEmail);
    }
}

```

## コントローラの実装

コントローラの認証機能の実装は過去の下記を参照しながら実装した
[モダンな技術を全く知らないSIer5年目がWebサービスを作ることになったため0から勉強する ~Laravel jwt-auth編~](https://qiita.com/gemetasu1103/items/cc41e65dff7824a63bef)

### 注意点

MultiAuthの対応でガード名を指定してあげないと認証がうまく行かなかった。
もし何かいい方法があれな教えてもらえると幸いです。。。

```php
// authの部分をguard名を指定してあげる
$this->auth->attempt($request->only('email', 'password'))
↓↓↓
auth("users")->attempt($request->only('email', 'password'))
```