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