Laravel JWT Auth with Vue.js - Page 2

API

For form validation, create a FormRequest class. Use the command line and the php artisan make command to generate the class as follows.

php artisan make:request RegisterFormRequest

Edit app/Http/Requests/RegisterFormRequest.php. First, return true instead of false in the authorize function since using JWT for that. Then add the validation rules in the array that is returned in the rules function for the new User as follows.

RegisterFormRequest.php
public function authorize()
{
    return true;
}

...

public function rules()
{
    return [
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required',
    ];
}

Edit the pre-existing app/Http/Controllers/Auth/AuthController.php. Replace everything below the namespace declaration with this code.

AuthController.php
...

use App\Models\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\RegisterFormRequest;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function register(RegisterFormRequest $request)
    {
        User::create([
            'name' => $request->json('name'),
            'email' => $request->json('email'),
            'password' => bcrypt($request->json('password')),
        ]);
    }
}

Test the form and API by registering a new user. The users table of the database should contain a new user record. Try registering another user with the same e-mail address to test the validation.

JWT Auth

For Laravel JSON web token authentication, install jwt-auth using composer require.

composer require tymon/jwt-auth

Edit config\app.php adding the service JWTAuthServiceProvider to the providers array and under the aliases array, add the JWTAuth facade.

app.php
...

'providers' => [
    ...

    Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
]

...

'aliases' => [
    ...

    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
]

Publish the configuration.

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"

Generate a key in the published configuration.

php artisan jwt:generate

Edit the published configration config\jwt.php file by updating 'user' => 'App\User' since the User model namespace has been changed from the default.

jwt.php
...

'user' => App\Models\User::class,

User Endpoint

Create the User controller.

php artisan make:controller UserController

Edit the app\Http\Controllers\UserController.php adding the index method to return the user data.

UserController.php
...

use Illuminate\Http\Request;

use App\Http\Requests;

class UserController extends Controller
{
    public function index(Request $request)
    {
        $data = [];
        $data['name'] = $request->user()->name;
        $data['email'] = $request->user()->email;
        return response()->json([
            'data' => $data,
        ]);
    }
}

Add the signin method to the AuthController class and update the use statements for the external classes as follows.

AuthController.php
...

use App\Models\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\RegisterFormRequest;
use Carbon\Carbon;
use JWTAuth;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;

class AuthController extends Controller
{
    public function register(RegisterFormRequest $request)
    {
        User::create([
            'name' => $request->json('name'),
            'email' => $request->json('email'),
            'password' => bcrypt($request->json('password')),
        ]);
    }

    public function signin(Request $request)
    {
        try {
            $token = JWTAuth::attempt($request->only('email', 'password'), [
                'exp' => Carbon::now()->addWeek()->timestamp,
            ]);
        } catch (JWTException $e) {
            return response()->json([
                'error' => 'Could not authenticate',
            ], 500);
        }

        if (!$token) {
            return response()->json([
                'error' => 'Could not authenticate',
            ], 401);
        } else {
            $data = [];
            $meta = [];

            $data['name'] = $request->user()->name;
            $meta['token'] = $token;

            return response()->json([
                'data' => $data,
                'meta' => $meta
            ]);
        }
    }
}

Edit app\Http\Kernel.php adding jwt.auth to the application’s route middleware array.

kernel.php
...

protected $routeMiddleware = [
    ...

    'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
];

Edit app\Http\routes.php adding the signin and jwt.auth route group to authorize access to the user endpoint.

routes.php
...

Route::group(['middleware' => ['api']], function () {
    Route::post('/api/register', [
        'uses' => 'Auth\AuthController@register',
    ]);

    Route::post('/api/signin', [
        'uses' => 'Auth\AuthController@signin',
    ]);

    Route::group(['middleware' => 'jwt.auth'], function () {
        Route::get('/user', [
            'uses' => 'UserController@index',
        ]);
    });

});

Remaining Front End Components

Add the following code to the empty Signin.vue file.

Signin.vue
<template>
    <div class="alert alert-danger" v-if="error">
        <p>There was an error, unable to sign in with those credentials.</p>
    </div>
    <form autocomplete="off" v-on:submit="signin">
        <div class="form-group">
            <label for="email">E-mail</label>
            <input type="email" id="email" class="form-control" placeholder="gavin.belson@hooli.com" v-model="email" required>
        </div>
        <div class="form-group">
            <label for="password">Password</label>
            <input type="password" id="password" class="form-control" v-model="password" required>
        </div>
        <button type="submit" class="btn btn-default">Sign in</button>
    </form>
</template>
<script>
import auth from '../js/auth.js';

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

Add the following code to the empty Dashboard.vue file. The content in this template will populate the App.vue router-view element when the user is authenticated.

Dashboard.vue
<template>
    <h1>Laravel 5 - Dashboard</h1>
</template>

Update auth.js as follows to handle all of the authorization functions.

auth.js
...

export default {
    user: {
        authenticated: false,
        profile: null
    },
    check() {
        if (localStorage.getItem('id_token') !== null) {
            Vue.http.get(
                'api/user',
            ).then(response => {
                this.user.authenticated = true
                this.user.profile = response.data.data
            })
        }
    },
    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
        })
    },
    signin(context, email, password) {
        Vue.http.post(
            'api/signin',
            {
                email: email,
                password: password
            }
        ).then(response => {
            context.error = false
            localStorage.setItem('id_token', response.data.meta.token)
            Vue.http.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('id_token')

            this.user.authenticated = true
            this.user.profile = response.data.data

            router.go({
                name: 'dashboard'
            })
        }, response => {
            context.error = true
        })
    },
    signout() {
        localStorage.removeItem('id_token')
        this.user.authenticated = false
        this.user.profile = null

        router.go({
            name: 'home'
        })
    }
}

Update the router.map in app.js to include the Dashboard and Signin components.

app.js
...

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

1. Update the App.vue navigation inside the nav so the Register link is only available when applicable. Add a Sign in and Sign out link along with a list item to show the users name when signed in.

2. At the bottom of the App.vue file add a script to bind the auth.js methods.

App.vue
...

<nav>
    <ul class="list-inline">
        <li><a v-link="{ name: 'home' }">Home</a></li>
        <li class="pull-right" v-if="!auth.user.authenticated">
            <a v-link="{ name: 'register' }">Register</a>
        </li>
        <li class="pull-right" v-if="!auth.user.authenticated">
            <a v-link="{ name: 'signin' }">Sign in</a>
        </li>
        <li class="pull-right" v-if="auth.user.authenticated">
            <a href="#" v-on:click="signout">Sign out</a>
        </li>
        <li class="pull-right" v-if="auth.user.authenticated">
            Hi, {{ auth.user.profile.name }}
        </li>
    </ul>
</nav>

...

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

export default {
    data() {
            return {
                auth: auth
            }
        },
        methods: {
            signout() {
                auth.signout()
            }
        },
        ready() {
            auth.check()
        }
}
</script>

That’s It!

Rebuild the app with the gulp command and give it a test drive.

gulp

Versions that were used in this tutorial.

  • Laravel 5.2
  • Laravel Elixir Vueify v1.0.6
  • JWT Auth v0.5.9
  • Vue v1.0.26
  • Vue Resource v1.0.1
  • Vue Router v0.7.13

Source Code
comments powered by Disqus