Best Laravel-Vue.js Authentication using JWT Auth: In Today’s world, Authentication is a primary concern for any web application because it will secure your web application from unauthorised users. In this tutorial, you will learn about Vue authentication using Laravel JWT package. I have also used Vuex as global state management.
Here, you can see a live demo on Laravel + Vue Authentication
Here, I am going to show you the process of implementing authentication for Vue Single Page Application with Laravel API.
Best Vue.js Authentication using Laravel – JWT Auth
Laravel is the most popular PHP framework for developing a robust and secure web application, and Vue.js is a lightweight front-end framework. Laravel framework provides built-in support for Vue.js to build a great app without refreshing a webpage.
I am going to give you a demo of authentication in
Vue – Laravel using JWT Auth.
Dependencies
- Node.js
- Laravel 7.9
- JWT Auth
- NPM 6.4.1
- Vue.js 2.0
- Vue-router
- Vue-axios
- Vuex
First of all, You need to create a new laravel project using composer. Make sure you have installed composer in your machine.
#1: Create a new Laravel Project
1 |
composer create-project --prefer-dist laravel/laravel laravel-vue-auth |
After Creating Laravel Project, Go to the project directory by running the following command.
1 |
cd laravel-vue-auth |
and install NPM for Vue.js Dependencies.
1 |
npm install |
#2: Configure Database
Now, Next step is to create a database using phpmyadmin or any other tools and change some configuration in your .env file to use the database by providing the database name and credential as shown below.
1 2 3 4 5 6 |
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel_vue_auth DB_USERNAME=root DB_PASSWORD= |
After, Configure the database in the .env file type the following command to run the migration.
1 |
php artisan migrate |
#3: Install Vue Libraries
Install some necessary Vue libraries such as Vue-Router, Vue-axios, and Vuex.
1 |
npm install vue-router vue-axios vuex --save |
You need to import and use those dependencies in the app.js file which located inside resources >> js folder.
1 2 3 4 5 6 7 8 |
import Vue from 'vue'; import VueRouter from 'vue-router'; import axios from 'axios'; import VueAxios from 'vue-axios'; import App from './App.vue'; Vue.use(VueRouter); Vue.use(VueAxios, axios); |
#4: Create Vue Components
Create a file called App.vue in resources >> assets >> js folder and write below code in App.vue file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<template> <div> <nav> <div class="container"> <div class="nav-wrapper"> <ul id="nav-mobile" class="left hide-on-med-and-down"> <li> <router-link :to="{ name: 'home' }">Home</router-link> </li> </ul> <ul id="nav-mobile2" class="right"> <li v-if="!this.$store.state.isLoggedIn"> <router-link :to="{ name: 'login' }">Login</router-link> </li> <li v-if="!this.$store.state.isLoggedIn"> <router-link :to="{ name: 'register' }">Register</router-link> </li> <li v-if="this.$store.state.isLoggedIn"> <a href="#" class="" @click="logout()">Logout</a> </li> <li> <a href="https://www.google.com" target="_blank" class="github"><i class="fa fa-github"></i></a> </li> </ul> </div> </div> </nav> <transition name="fade" mode="out-in"> <router-view></router-view> </transition> <footer class="page-footer"> <div class="footer-copyright"> <div class="container"> <span>© 2018</span> <a href="https://artixun.com" target="_blank" style="color:white;text-decoration:none">Artixun Softwares.</a> </div> </div> </footer> </div> </template> |
Create another file called Home.vue inside resources >> assets >> js >> components folder and write the following code.
1 2 3 |
<template> <h4>Laravel 7.9 Vue SPA Authentication</h4> </template> |
Then, write the below code in the app.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import Vue from 'vue'; import VueRouter from 'vue-router'; import axios from 'axios'; import VueAxios from 'vue-axios'; import App from './App.vue'; import Home from './components/Home.vue'; Vue.use(VueRouter); Vue.use(VueAxios, axios); const router = new VueRouter({ mode: 'history', routes: [ { path: '/', name: 'home', component: Home }, }); new Vue({ router, el: '#app', render: h => h(App) }); |
In this tutorial, I have used materialize CSS framework. I have put CSS CDN in the welcome.blade.php file which found inside resources >> views directory. You can also include more external CSS and JS file inside the same data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> </head> <body style="background: #f5f5f5;"> <div id="app"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script> <script src="/js/app.js"></script> </body> </html> |
Then, Run the following command in your terminal. Every time it will compile the latest and existing CSS and JS files code when changes occur.
1 |
npm run watch |
#4.1: Create a Register Component
Create a file called Register.vue file inside resources >> assets >> js >> components folder and put the following code in Register.vue file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
<template> <div class="row" style="margin:2% auto; width:420px; margin-bottom:90px;"> <div class="container col s12"> <div class="heading"> <center><h5>Sign Up</h5></center> </div> <div class="progress" v-if="isProgress"> <div class="indeterminate"></div> </div> <div class="login-form"> <div class="row"> <div class="input-field col s12"> <input id="name" type="text" class="validate" v-model="name"> <label for="name">Name</label> <span class="text text-danger" v-if="error && errors.name">{{ errors.name[0] }}</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input id="email" type="text" class="validate" v-model="email"> <label for="email">Email</label> <span class="text text-danger" v-if="error && errors.email">{{ errors.email[0] }}</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input id="password" type="password" class="validate" v-model="password"> <label for="password">Password</label> <span class="text text-danger" v-if="error && errors.password">{{ errors.password[0] }}</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input id="confirm_password" type="password" class="validate" v-model="confirmPassword"> <label for="confirm_password">Confirm Password</label> <span class="text text-danger" v-if="error && errors.confirmPassword">{{ errors.confirmPassword[0] }}</span> </div> </div> <button class="btn btn-block waves-effect waves-light submit" type="button" name="action" @click.prevent="register()">Register</button> </div> </div> </div> </template> <script> export default { data(){ return { name: '', email: '', password: '', confirmPassword:'', error: false, errors: {}, success: false, isProgress: false }; }, methods: { register(){ this.axios.post('api/auth/register', { name: this.name, email: this.email, password: this.password, confirmPassword: this.confirmPassword }).then(response => { this.isProgress = true; if(response.data.success == true) { setTimeout(() => { this.isProgress = false; this.$router.push({ name: 'login'}) this.$toaster.success('Sign up successfully...') }, 2000) } }).catch(error => { this.isProgress = false; this.error = true; this.errors = error.response.data.errors }); } } } </script> |

#4.2: Create a Login Component
Create another file called Login.vue inside the same directory and put the below code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
<template> <div class="row" style="margin:2% auto; width:420px;margin-bottom:30px;"> <div class="container col s12"> <div class="heading"> <center><h5>Sign In</h5></center> </div> <div class="progress" v-if="isProgress"> <div class="indeterminate"></div> </div> <div class="alert alert-danger" v-if="loginError && errors.message"> <span>{{ errors.message[0] }}</span> </div> <div class="login-form"> <div class="row"> <div class="input-field col s12"> <input id="email" type="text" class="validate" v-model="email"> <label for="email">Email</label> <span class="text text-danger" v-if="loginError && errors.email">{{ errors.email[0] }}</span> </div> </div> <div class="row"> <div class="input-field col s12"> <input id="password" type="password" class="validate" v-model="password"> <label for="password">Password</label> <span class="text text-danger" v-if="loginError && errors.password">{{ errors.password[0] }}</span> </div> </div> <button class="btn btn-block waves-effect waves-light submit" type="button" name="action" @click="login()">Login</button> </div> </div> </div> </template> <script> import store from '../store' export default { data() { return { email: '', password: '', loginError: false, errors: {}, isProgress: false, } }, methods: { login() { this.loginError = false; this.axios.post('api/auth/login', { email: this.email, password: this.password }).then(response => { this.isProgress = true; if(response.data.success == true) { setTimeout(() => { this.isProgress = false; store.commit('LoginUser', response.data); this.$router.push({name: 'dashboard'}) },2000); } else { this.isProgress = true; setTimeout(() => { this.isProgress = false; this.loginError = true; this.errors = response.data.errors },1000); } }).catch(error => { this.isProgress = false; this.loginError = true; this.errors = error.response.data.errors }); }, } } </script> |
#4.3: Create a Dashboard Component
Make a Dashboard.vue file inside the same directory and write the below code in it.
1 2 3 |
<template> <h4>Welcome to Dashboard</h4> </template> |
Here, I have also used Vuex as state management which will store JWT token in local storage and manage it for the overall application.
To use Vuex, Create a new file called store.js inside resources >> js and put the following code inside it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { isLoggedIn: !!localStorage.getItem('token'), token: localStorage.getItem('token') }, mutations: { LoginUser (state, data) { state.isLoggedIn = true; let token = data.access_token; state.token = token; localStorage.setItem('token', token) }, LogoutUser (state) { state.isLoggedIn = false; state.token = localStorage.removeItem('token') }, tokenStored (state) { state.token = localStorage.getItem('token') } } }) |
Then, Replace the content of the app.js file which was created before.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import Vue from 'vue'; import VueRouter from 'vue-router'; import axios from 'axios'; import VueAxios from 'vue-axios'; import App from './App.vue'; import Dashboard from './components/Dashboard.vue'; import Home from './components/Home.vue'; import Register from './components/Register.vue'; import Login from './components/Login.vue'; import store from './store'; Vue.use(VueRouter); Vue.use(VueAxios, axios); //vue-toaster import Toaster from 'v-toaster'; import 'v-toaster/dist/v-toaster.css'; Vue.use(Toaster, {timeout: 2000}); const router = new VueRouter({ mode: 'history', routes: [ { path: '/', name: 'home', component: Home }, { path: '/register', name: 'register', component: Register, meta: { auth: false } }, { path: '/login', name: 'login', component: Login, meta: { auth: false } }, { path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { requiresAuth: true } }] }); router.beforeEach((to, from, next) => { // check if the route requires authentication and user is not logged in if (to.matched.some(route => route.meta.requiresAuth) && !store.state.isLoggedIn) { // redirect to login page next({ name: 'login' }) return } // if logged in redirect to dashboard if(to.path === '/login' && store.state.isLoggedIn) { next({ name: 'dashboard' }) return } next() }); // new Vue(App).$mount('#app'); new Vue({ router, store, el: '#app', render: h => h(App) }); |
In the above code, I have added register component, login component, and dashboard component routers with meta option and auth property, which will determine which router needs authentication.
Here, I have given requiresAuth meta option true to dashboard route which indicates that route will not be able to access without login.
I have also handled a method called router.beforeEach() which will check each route for authentication.
If requireAuth and isLogging are false then it will automatically redirect to the login page. Otherwise, it will redirect to the dashboard route.
#5: Install jwt-auth library for Laravel
Now, I am starting the server side implementation of our authentication. First of all, You need to install jwt-auth library for your laravel application.
1 |
composer require tymon/jwt-auth |
This library is used to handle token for authentication over api. You can know more about jwt-auth library.
Then, Publish the configuration file by typing the following command for versions 5.5 or above.
1 |
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" |
Next, after installing the library you need to generate a secret key using the following command which will update your .env file with JWT_SECRET key.
1 |
php artisan jwt:secret |
Make sure to use jwt guard authentication in config >> auth.php file like below.
1 2 3 4 |
'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], |
#6: Register jwt-auth Middleware
Now, Make sure to register the middleware, which comes up with JWT Auth package in Kernel.php file located inside app >> Http folder like below.
1 2 3 4 |
protected $routeMiddleware = [ ... 'auth.jwt' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class ]; |
#7 Update User Model
Here, I am going to update the User Model by implementing the Tymon\JWTAuth\Contracts\JWTSubject interface.
This interface requires implementing two methods getJWTIdentifier and getJWTCustomClaims like below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class User extends Authenticatable implements JWTSubject { use Notifiable; public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; } protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; } |
#8: Create API Routes
Now, Create api routes for authentication system in routes >> api.php file
1 2 3 4 5 6 7 |
Route::post('/auth/register', 'AuthController@postRegister'); Route::post('/auth/login', 'AuthController@postLogin'); Route::group(['middleware' => ['auth.jwt']], function () { Route::get('/auth/logout', 'AuthController@logout'); }); |
#9: Create a Controller
Above, I have added api routes for register and login page. Now, I am implementing a controller for that routes.
So, Create a AuthController using below command.
1 |
php artisan make:controller AuthController |
This command will create a new file under app >> Http >> Controllers folder. Add the below code inside AuthController file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?php namespace App\Http\Controllers; use App\Http\Requests\LoginRequest; use App\Http\Requests\RegisterRequest; use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class AuthController extends Controller { public function postRegister(RegisterRequest $request) { $user = new User(); $user->name = $request->get('name'); $user->email = $request->get('email'); $user->password = bcrypt($request->get('password')); $user->save(); return response()->json([ 'success' => true ]); } public function postLogin(LoginRequest $request) { $credentials = $request->only('email', 'password'); $token = null; try { if (!$token = \JWTAuth::attempt($credentials)) { return response()->json([ 'success' => false, 'errors' => [ 'message' => [ 'Either Email or Password Invalid' ] ], ]); } } catch (\JWTAuthException $e) { return response()->json([ 'success' => false, 'errors' => [ 'message' => [ 'Either Email or Password Invalid' ] ], ]); } return $this->respondWithToken($token); } protected function respondWithToken($token) { $user = Auth::user(); return response()->json([ 'success' => true, 'access_token' => $token, 'user' => $user, 'token_type' => 'bearer', ]); } public function logout() { \JWTAuth::invalidate(\JWTAuth::getToken()); return response()->json([ 'success' => true ]); } } |
Finally, I have completed Laravel + Vue Authentication Practice Using JWT Auth tutorial with example. I hope you have learned from this tutorial and will implement JWT Auth in your next application. Thank you.
Hi,
second step: data base
php artisan migrate =>Nothing to migrate
There is no make:migration. what about the user table??
Hi,
For the second step, the user’s table migration is already provided by the Laravel, so we dont need to write that make:migration for the user’s table.
Let me know if you got any other query.
Hello, Can you Provide ta laravel code part ?
https://github.com/Codefrontback/laravel-vue-auth-demo here you can find the code 🙂
!$token = \JWTAuth::attempt($credentials)
typo.
Hey @Alan Spurlock,
Thank you so much for notifying me. I am updating it.
Really really appreciated, do you know how hard is to find a good tutorial AND which is working as per current node and laravel version. Thank you again, It’s all working fine, some of the things were missing from tutorial compare to git repo like logout function is missing from the tutorial. Git Repo is very much helpful, I already gave a star. 🙂
Hi @Vipul,
Thank you so much for the feedback. I will add the logout feature :).
Very nice tutorial!!, thanks!!
hey man, thanks nice tutorial, but if i want to register with username and not email, how can i make it?