Laravel7で新たに認証に関する機能を持つLaravel Airlockが追加されました。Laravel Airlockは2つの機能を持ち、1つはこれまで馴染みのあるAPI Tokenを利用したAPI Token認証、もうひとつはCookieを利用しSPAでの利用を前提としたSPA認証です。本文書では2つ目のSPAで利用する認証の使用方法を確認します。

Laravel Airlock

本文書ではLaravelのインストール時に一緒にインストールを行なったvue.jsを利用して動作確認を行います(同じドメイン)。Laravelとは別にVue CLIで作成したvue.jsなどのSPAを利用することは可能ですがCORS等の設定が必要となり少し設定項目が増えます。

Laravelのインストール

Laravel Airlockの動作確認を行うためには事前にLaravel7のインストールを行なっておく必要があります。Laravel7のインストールについては下記を参考にしてください。

Laravel Airlockパッケージのインストール

Laravel Airlockを利用するためにはlaravel/airlockパッケージのインストールを行う必要があります。


 $ composer require laravel/airlock

設定ファイルとテーブルの作成

laravel Airlockの設定ファイル(airlock.php)とpersonal_access_tokensテーブルのマイグレーションファイルの作成を行います。personal_access_tokensテーブルはSPA認証では利用しません。Laravel Airlockのもう一つの機能であるAPI Token認証を利用した場合に作成したToken情報を保存します。


 $ php artisan vendor:publish --provider="Laravel\Airlock\AirlockServiceProvider"
Copied Directory [/vendor/laravel/airlock/database/migrations] To [/database/migrations]
Copied File [/vendor/laravel/airlock/config/airlock.php] To [/config/airlock.php]
Publishing complete.

マイグレーションファイルがdatabase¥migrationsの下に作成されているのでphp artisan migrateコマンドを実行します。


 $ php artisan migrate
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (0.01 seconds)

middlewareの設定

app¥Http¥Kernel.phpファイルにSPAの認証に必要なmiddlewareを追加します。

ファイルの上部に以下を追加します。


use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Laravel\Airlock\Http\Middleware\EnsureFrontendRequestsAreStateful; //追加

中盤にあるmiddlewareGroupsのapiに以下を追加します。


'api' => [
    EnsureFrontendRequestsAreStateful::class, //追加
    'throttle:60,1',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

vue.jsの設定

bladeファイルとコンポーネントファイルの更新

vue.js上からLaravelサーバにアクセスを行うため既存のwelcome.blade.phpファイル更新します。


<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>Laravel Airlock SPA Authentification</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>

    <div id="app">
    <example-component />
    </div>

<script src="{{ mix('js/app.js') }}"></script> 
</body>
</html>

resouces¥js¥app.jsファイルで読み込まれているコンポーネントexample-componentを利用します。


Vue.component('example-component', require('./components/ExampleComponent.vue').default);

ExampleComponet.vueファイルは以下のように記述します。


<template>
    <div class="d-flex justify-content-center pt-5">
        <h1>Laravel Airlock</h1>
    </div>
</template>

<script>

</script>
Laravelではデフォルトの設定のままでbootstrapが利用できるので最小限のCSS設定はbootstrapを利用します。
fukidashi

ログイン画面の作成

ユーザのログイン画面を作成します。emailとpasswordの2つの入力項目を持ち、v-modelを設定します。ボタンを押すとloginメソッドが実行されます。


<form @submit.prevent="login">
    <div>
    <label>Email:</label>
    <input v-model="email" />
    </div>
    <div>
    <label>Password:</label>
    <input v-model="password"/>
    </div>
    <button class="btn btn-primary" type="submit">ログイン</button>
</form>

ログインに関連するvue.jsの設定は下記の通りです。


export default {
    data() {
        return{
            email: 'johndoe@example.com',
            password: 'password',
        }
    },
    methods: {
        login(){
            axios.get('/airlock/csrf-cookie').then(response => {
               axios.post('/login', {
                    email: this.email,
                    password: this.password
                })
                .then(response => {
                    console.log(response)
                })
            });
        },
    }
}

Laravel AirlockをSPAで利用するためには、まず/airlock/csrf-cookieにGETリクエストを送りCSRF protectionを初期化する必要があります。その後/loginにPostリクエストでemailとpasswordを送信しで登録されているユーザがいればログインが行われアクセス制限のあるリソースにアクセスすることが可能となります。

ユーザ情報取得の追加

ログインが完了した後api.webファイルにデフォルトで設定されている/api/userにアクセスを行いユーザ情報が取得できるかどうか確認を行います。

デフォルトではauthがapiになっているのでAirlockを利用する際は必ずauth:airlockに変更します。


Route::middleware('auth:airlock')->get('/user', function (Request $request) {
    return $request->user();
});

ユーザ情報取得ボタンをつけてクリックイベントgetUserを追加します。


<button class="btn btn-success mb-3" @click="getUser">ユーザ情報取得</button>

getUserメソッドを下記のように追加します。


getUser(){
    axios.get('/api/user')
        .then(response => {
            console.log(response.data)
        })
        .catch(error => {
            alert(error.response)
        })
}

ログアウト処理の追加

ログアウトも行えるようにログアウトボタンも追加します。ログアウト後はアクセス制限のかかっているリソースにはアクセスすることはできません。


<button class="btn btn-danger mb-3" @click="logout">ログアウト</button>

ログアウトは/logoutにPOSTリクエストを送信して行います。


logout(){
    axios.post('/logout',{})
        .then(response => {
            console.log(response)
        })
},

作成したコード

新たにloggedInデータプロパティを追加しログインしている状態とログアウトしている状態で表示する内容を切り替えます。ログインしている時にはログアウトボタンを表示、ログアウトしている時にはログイン画面を表示します。


<script>
export default {
    data() {
        return{
            email: 'johndoe@example.com',
            password: 'password',
            loggedIn: false,
        }
    },
    methods: {
        login(){
            axios.get('/airlock/csrf-cookie').then(response => {
                axios.post('/login', {
                    email: this.email,
                    password: this.password
                })
                .then(response => {
                    console.log(response)
                    this.loggedIn = true
                })
                .catch(error => {
                    console.log(error.response.data)
                    this.loggedIn = false
                })
            });
        },
        logout(){
            axios.post('/logout',{})
                .then(response => {
                    console.log(response)
                    this.loggedIn = false
                })
        },
        getUser(){
            axios.get('/api/user')
                .then(response => {
                    console.log(response.data)
                })
                .catch(error => {
                    alert(error.response.data.message)
                    this.loggedIn = false
                })
        }
    }
}
</script>

HTML部分は下記のようになります。表示内容の切り替えにはv-ifとv-elseを利用しています。


<template>
    <div class="p-5">
        <h1>Laravel Airlock</h1>
        <button class="btn btn-success mb-3" @click="getUser">ユーザ情報取得</button>
        <button class="btn btn-danger mb-3"  v-if="loggedIn" @click="logout">ログアウト</button>
        <form @submit.prevent="login" v-else>
            <div>
            <label>Email:</label>
            <input v-model="email" />
            </div>
            <div>
            <label>Password:</label>
            <input v-model="password"/>
            </div>
            <button class="btn btn-primary" type="submit">ログイン</button>
        </form>
    </div>
</template>

動作確認

作成したコードを使ってLaravel AirlockのSPA認証が動作するか確認を行います。コンソールログに表示される内容も一緒に確認していきます。

最初はログインが行われていない状態です。

ログイン画面
ログイン画面

この状態でユーザ情報取得ボタンをクリックします。ログインが完了していないので認証に失敗してエラーが表示されます。

unauthenticadが表示
unauthenticadが表示

次にログインボタンを押してログインを行います。ログインが正常に終了するとログイン画面は消えてログアウトボタンが表示されます。

ログイン完了
ログイン完了

現在はログインしている状態なのでユーザ情報取得ボタンを押すと認証に成功してユーザ情報を取得することができます。一番上の401エラーはログイン前に実行した際のエラーです。

ユーザ情報取得
ユーザ情報取得

ログアウトボタンを押してログアウトし再度ユーザ情報取得ボタンを押すとエラーが表示されます。

ログアウト後にユーザボタンをクリック
ログアウト後にユーザボタンをクリック

次に間違ったユーザ名でログインを押すとエラーメッセージが表示されます。

存在しないユーザでログイン
存在しないユーザでログイン

エラーメッセージはThe given data was invalid(送信したデータが正しくない)です。

ログイン失敗のエラーが表示
ログイン失敗のエラーが表示

ユーザのログイン処理が完了している場合にはアクセス制限のあるリソースにアクセスできログアウトしている状態の場合はアクセス制限のあるリソースにはアクセスできないことが確認できました。

Vue Routerを使ったページ遷移なく本格的はSPAではありませんがLaravel AirlockのSPA認証を使ってどのように設定を行うか理解してもらえたかと思います。