Laravel JWT Auth with Vue.js

This post documents using Laravel to build JSON Web Token based authentication with a Vue.js 1.0 user interface. UPDATE: vue.js 2.0 version published.

A fresh install of Laravel 5.2 for local development is required. Head over to the Laravel 5.2 docs to get setup with Composer and Laravel as needed. This post contains some info on how I installed Laravel 5.2 on Windows. Once Laravel 5.2 is installed, the database created and the .env file updated to connect to it, run the migration to create the users table.

php artisan migrate

User Model

One of the first things I like to do is create a Models folder and move the User model into it. To organize models within their own directory, start by creating a folder named Models in the app directory and move the User model into it.

mkdir app/Models

mv app/User.php app/Models

Edit app/Models/User.php, change the App namespace to App\Models.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    ...

Throughout this tutorial you will encounter an ellipsis ... in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, examine the source code.

Edit, config/auth.php. Update the authentication drivers user provider for the App\Models\User namespace change as follows.

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

    ...

Laravel Elixir Vueify

Using NPM, install the dependencies including laravel-elixer and gulp that should already be defined in the package.json file.

npm install

Install the Laravel Elixir and the Browserify Vueify plugin wrapper, laravel-elixir-vueify. This node package also includes vue.js.

For Laravel Elixer 5
npm install laravel-elixir-vueify@1.0.6 --save-dev

Laravel Elixir is a build tool wrapper for Gulp. To use laravel-elixir-vueify, require laravel-elixir-vueify and update the elixir call with mix.browserify in the gulpfile.js as follows.

REPLACE

elixir(function(mix) {
    mix.sass('app.scss');
});

WITH

require('laravel-elixir-vueify');

elixir(function(mix) {
    mix.browserify('app.js');
    mix.sass('app.scss');
});

Create a resources/assets/js folder for the JavaScript.

mkdir resources/assets/js

Create a resources/assets/js/app.js JavaScript file.

touch resources/assets/js/app.js

Test the gulp build. A browserified app.js file that includes an external source map file should get compiled and written to public/js/app.js. The pre-existing app.scss Sass file should also get compiled.

gulp

Default Gulp task output
Default Gulp task output

Vue Router and Components

Install the router for Vue.js.

npm install vue-router@0.7.13 --save-dev

The npm install vue-router command above has been updated to install the version supported by the 1.x version of vue.js. This is the vue.js version installed with laravel-elixir-vueify@1.0.6.

Create a resources/assets/components folder and add an empty App.vue, Dashboard.vue, Home.vue, Register.vue and Signin.vue component.

mkdir resources/assets/components

touch resources/assets/components/App.vue

touch resources/assets/components/Dashboard.vue

touch resources/assets/components/Home.vue

touch resources/assets/components/Register.vue

touch resources/assets/components/Signin.vue

Add the following code to the empty App.vue file which will be the base layout component.

App.vue
<template>
    <div class="panel panel-default">
        <div class="panel-heading">
            <nav>
                <ul class="list-inline">
                    <li><a v-link="{ name: 'home' }">Home</a></li>
                    <li class="pull-right">
                        <a v-link="{ name: 'register' }">Register</a>
                    </li>
                </ul>
            </nav>
        </div>
        <div class="panel-body">
            <router-view></router-view>
        </div>
    </div>
</template>

Add the following code to the empty Home.vue file. The content in this template will populate the App.vue router-view element by default.

Home.vue
<template>
    <h1>Laravel 5</h1>
</template>

Add the following code to the empty Register.vue file for the user account creation form.

Register.vue
<template>
    <div class="alert alert-danger" v-if="error && !success">
        <p>There was an error, unable to complete registration.</p>
    </div>
    <div class="alert alert-success" v-if="success">
        <p>Registration completed. You can now sign in.</p>
    </div>
    <form autocomplete="off" v-on:submit="register" v-if="!success">
        <div class="form-group" v-bind:class="{ 'has-error': error && response.username }">
            <label for="name">Name</label>
            <input type="text" id="name" class="form-control" v-model="name" required>
            <span class="help-block" v-if="error && response.name">{{ response.name }}</span>
        </div>
        <div class="form-group" v-bind:class="{ 'has-error': error && response.email }">
            <label for="email">E-mail</label>
            <input type="email" id="email" class="form-control" placeholder="gavin.belson@hooli.com" v-model="email" required>
            <span class="help-block" v-if="error && response.email">{{ response.email }}</span>
        </div>
        <div class="form-group" v-bind:class="{ 'has-error': error && response.password }">
            <label for="password">Password</label>
            <input type="password" id="password" class="form-control" v-model="password" required>
            <span class="help-block" v-if="error && response.password">{{ response.password }}</span>
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
    </form>
</template>

Add the following code to the empty app.js file to require vue, the vue-router, import vue components, setup the router. Export Vue and the router to allow modules to import them.

app.js
var Vue = require('vue');
var VueRouter = require('vue-router');

import App from '../components/App.vue';
import Dashboard from '../components/Dashboard.vue';
import Home from '../components/Home.vue';
import Register from '../components/Register.vue';
import Signin from '../components/Signin.vue';

Vue.use(VueRouter);

export default Vue;
export var router = new VueRouter

router.map({
    '/': {
        name: 'home',
        component: Home
    },
    '/register': {
        name: 'register',
        component: Register
    }
});

router.start(App, '#app');

Web Page

Edit the resources/views/welcome.blade.php template.

Replace the entire contents of the file with this markup.

welcome.blade.php
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{!! csrf_token() !!}">
    <title>Laravel</title>

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

</head>
<body>
    <div class="container">
        <div id="app"></div>
    </div>
    <script src="/js/app.js"></script>
</body>
</html>

Now is a good time to test that everything builds and you have a Bootstrap styled page with with links to Home and Register in a horizontal navigation bar and a content panel under it with “Laravel 5” heading text. Select the Register link to diplay the form in the content panel.

gulp

Laravel JWT Auth Vuejs User Registration Form
New User Registration Form

Next, the registration form needs to be setup to post the data.

Vue Resource

The Vue.js resource plugin provides services for making web requests and handling responses. Install the resource plugin for Vue.js

npm install vue-resource --save-dev

Update resources/js/app.js to require the vue-resource plugin after vue and the vue-router. Also add the Vue.http.headers and Vue.http.options.root url. Laravel VerifyCsrfToken middleware will check for the X-CSRF-TOKEN request header. To make this token available to the client side script, it is being stored in a meta tag via resources/views/welcome.blade.php. The Authorization header is needed for JWT auth which will be added with the sign-in form later.

app.js
var Vue = require('vue');
var VueRouter = require('vue-router');
var VueResource = require('vue-resource');

Vue.http.headers.common['X-CSRF-TOKEN'] = document.getElementsByName('csrf-token')[0].getAttribute('content');
Vue.http.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('id_token');
Vue.http.options.root = 'http://laravel.dev:8080';

...

Create a resources/js/auth.js JavaScript file.

touch resources/assets/js/auth.js

Add the following code to auth.js to handle the API request when the register form is submitted.

auth.js
import Vue from './app.js';
import {router} from './app.js';

export default {
    user: {
        authenticated: false,
        profile: null
    },
    register(context, name, email, password) {
        Vue.http.post(
            'api/register',
            {
                name: name,
                email: email,
                password: password
            }
        ).then(response => {
            context.success = true
        }, response => {
            context.response = response.data
            context.error = true
        })
    }
}

Add the following script to the bottom of the Register.vue file to handle the register form submit.

Register.vue
...

</template>

<script>
import auth from '../js/auth.js';

export default {
    data() {
        return {
            name: null,
            email: null,
            password: null,
            success: false,
            error: false,
            response: null
        }
    },
    methods: {
        register(event) {
            event.preventDefault()
            auth.register(this, this.name, this.email, this.password)
        }
    }
}
</script>

Run gulp then fill out and submit the new user registration form. You should get a error message since the api/register route and respective endpoint has not yet been created.

Laravel JWT Auth Vuejs User Registration Form Error
New User Registration Form Error

The next page covers Form Request Validation, API authorization with JWT Auth, User endpoint and Sign in.

comments powered by Disqus