vue.js (Nuxt.js)とlaravelPassportを使ってSPAでのログイン機能を実装してみた



最近流行りのvue.jsのフレームワークNuxt.js（ナクストと読むらしい）でSPAを作っているのですが
その際、laravelのAPI認証ライブラリ「laravelPassport」を使ってログイン機能を実装したのでご紹介したいと思います。
1-6の手順は公式：https://readouble.com/laravel/5.5/ja/passport.html に書いてあります
#フレームワーク
nuxt.js フロントサイド
laravel 5.5 サーバーサイド

#ライブラリ
laravel/passport": "4.0"
barryvdh/laravel-cors（インストール方法については解説を省きます）


#サーバーサイドの実装
##1.laravel/passportをインストールする
※ 私の環境はlaravel5.5のためpassportが4.0しか入りませんでした。。。

```
composer require laravel/passport:4.0
```

##2.laravel/passportで使用するテーブルを作成する
vendor/laravel/passport/database/migrationsにmigrationファイルがありました
vender配下のライブラリのマイグレーションを実行する方法がわからなかったので コピーしてdatabase/migrations移動させてから実行しちゃいました。。。もっといい方法があるはず

```
php artisan migrate
```
以下5つのテーブルが追加されます
・oauth_access_tokens
・oauth_auth_codes
・oauth_clients
・oauth_personal_access_clients
・oauth_refresh_tokens

##3.認証に必要なシークレットキーを生成する
フロント側からサーバーサイドにアクセストークンを取得しに行く時に必要になります
このコマンドを実行すると先程生成されたoauth_clientsテーブルにデータが生成されます

```
php artisan passport:install
```
##4.UserモデルにLaravel\Passport\HasApiTokensトレイトを追加

```php
<?php

namespace App\Models;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
```

##5.AuthServiceProviderに以下を追加
```php
namespace App\Providers;

use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{

    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];


    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}
```
##6.config/auth.phpに以下を追加
```php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],
```
##7.config配下をいじったのでキャッシュクリア

```
php artisan config:cache
```
※ CROSSドメイン対応も行う必要がありますが、手順はに関しては今回は解説していません。必要に応じて設定をお願いします

以上でサーバーサイドの準備は完了です!


#フロントサイドの実装
※ユーザー新規登録機能の実装の説明は省略します
username、passwordでログインします

##1.アクセストークンを取得するaxiosを準備する

/oauth/tokenに以下のデータpostすることでアクセストークンを取得できます

```js
grant_type: 'password',           パスワード認証モードを指定
client_id: '2',                   パスワード認証の場合２を指定する
client_secret: 'シークレットキー',     サーバーサイドで生成してシークレットキー
username: this.username,          userテーブルのusername
password: this.password,          userテーブルのpassword
```

```html
<template>
    <main id="registrationForm">
        <h1>
            ログイン
        </h1>
        <label>メール</label><br>
        <input v-model="username"><br>
        <label>パスワード</label><br>
        <input v-model="password"><br>

        <button v-on:click="Login">ログイン</button>
    </main>
</template>

<script>
import axios from 'axios';

export default {
    data () {
        return {
          username: '',
          password: '',
        }
    },
    methods: {
        getHeaders(token) {
          return {
            Authorization: `Bearer ${token}`
          }
        },
        Login() {
            const postData = {
                grant_type: 'password',
                client_id: '2',
                client_secret: 'シークレットキー',
                username: this.username,
                password: this.password,
            }
            console.log(postData)
          axios.post('http://localhost:4242/api/oauth/token', postData)
            .then(response => {

                //取得したアクセストークンをヘッダーに入れる
                const headers = this.getHeaders(response.data.access_token)
                axios.defaults.headers.common['Authorization'] = headers.Authorization;
            })
            .catch(error => {
                console.log('ログインに失敗しました')
            })
        },
    }
}
</script>
```
##2.ログイン後のパスにアクセスする
取得したアクセストークンをheaderに入れることで
->middleware(‘auth:api’)と指定したパスにアクセスできる用になります

```php
Route::get('user/', 'Api\UserController@index')->middleware('auth:api');
```

以上でAPIでのログイン機能の実装は完了です！！
間違っている点、もっと良いやり方がありましたらご指摘頂けると嬉しいです