In this tutorial, we’re gonna build a Vue.js with Vuex and Vue Router Application that supports JWT Authentication. I will show you:
- JWT Authentication Flow for User Signup & User Login
- Project Structure for Vue.js Authentication with Vuex & Vue Router
- How to define Vuex Authentication module
- Creating Vue Authentication Components with Vuex Store & VeeValidate
- Vue Components for accessing protected Resources
- How to add a dynamic Navigation Bar to Vue App
Let’s explore together.
Typescript version: Vue/Vuex Typescript example: JWT Authentication
Vue 3 version: Vue 3 Authentication with JWT, Vuex, Axios and Vue Router
Related Post:
– In-depth Introduction to JWT-JSON Web Token
– Vue.js CRUD Application with Vue Router & Axios
– Vue File Upload example using Axios
Fullstack:
– Spring Boot + Vue.js: Authentication with JWT & Spring Security Example
– Node.js Express + Vue.js: JWT Authentication & Authorization example
Contents
- Overview of Vue JWT Authentication example
- Flow for User Registration and User Login
- Vue App Component Diagram with Vuex & Vue Router
- Technology
- Project Structure
- Setup Vue App modules
- Create Services
- Define Vuex Authentication module
- Create Vue Authentication Components
- Create Vue Components for accessing Resources
- Define Routes for Vue Router
- Add Navigation Bar to Vue App
- Handle Unauthorized Access
- Conclusion
- Further Reading
Overview of Vue JWT Authentication example
We will build a Vue application in that:
- There are Login/Logout, Signup pages.
- Form data will be validated by front-end before being sent to back-end.
- Depending on User’s roles (admin, moderator, user), Navigation Bar changes its items automatically.
Screenshots
– Signup Page:
– Login Page & Profile Page (for successful Login):
– Navigation Bar for Admin account:
You also need to add Refresh Token, more details at:
Vue Refresh Token with Axios and JWT example
Demo
This is full Vue JWT Authentication App demo (with form validation, check signup username/email duplicates, test authorization with 3 roles: Admin, Moderator, User). In the video, we use Spring Boot for back-end REST APIs.
Flow for User Registration and User Login
For JWT Authentication, we’re gonna call 2 endpoints:
- POST
api/auth/signup
for User Registration - POST
api/auth/signin
for User Login
You can take a look at following flow to have an overview of Requests and Responses Vue Client will make or receive.
Vue Client must add a JWT to HTTP Authorization Header before sending request to protected resources.
You can find step by step to implement these back-end servers in following tutorial:
- Spring Boot JWT with Spring Security (MySQL/PostgreSQL)
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node.js JWT Authentication & Authorization with MySQL
- Node.js JWT Authentication & Authorization with MongoDB
- Node.js JWT Authentication & Authorization with PostgreSQL
Vue App Component Diagram with Vuex & Vue Router
Now look at the diagram below.
Let’s think about it.
– The App
component is a container with Router
. It gets app state from Vuex store/auth
. Then the navbar now can display based on the state. App
component also passes state to its child components.
– Login
& Register
components have form for submission data (with support of vee-validate
). We call Vuex store dispatch()
function to make login/register actions.
– Our Vuex actions call auth.service
methods which use axios
to make HTTP requests. We also store or get JWT from Browser Local Storage inside these methods.
– Home
component is public for all visitor.
– Profile
component get user
data from its parent component and display user information.
– BoardUser
, BoardModerator
, BoardAdmin
components will be displayed by Vuex state user.roles
. In these components, we use user.service
to get protected resources from API.
– user.service
uses auth-header()
helper function to add JWT to HTTP Authorization header. auth-header()
returns an object containing the JWT of the currently logged in user from Local Storage.
Technology
We will use these modules:
- vue: 2.6.10
- vue-router: 3.0.3
- vuex: 3.0.1
- axios: 0.19.0
- vee-validate: 2.2.15
- bootstrap: 4.3.1
- vue-fontawesome: 0.1.7
Project Structure
This is folders & files structure for our Vue application:
With the explaination in diagram above, you can understand the project structure easily.
Setup Vue App modules
Run following command to install neccessary modules:
npm install vue-router
npm install vuex
npm install [email protected]
npm install axios
npm install bootstrap jquery popper.js
npm install @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/vue-fontawesome
After the installation is done, you can check dependencies
in package.json file.
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/vue-fontawesome": "^0.1.7",
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"core-js": "^2.6.5",
"jquery": "^3.4.1",
"popper.js": "^1.15.0",
"vee-validate": "^2.2.15",
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"vuex": "^3.0.1"
},
Open src/main.js, add code below:
import Vue from 'vue';
import App from './App.vue';
import { router } from './router';
import store from './store';
import 'bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import VeeValidate from 'vee-validate';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import {
faHome,
faUser,
faUserPlus,
faSignInAlt,
faSignOutAlt
} from '@fortawesome/free-solid-svg-icons';
library.add(faHome, faUser, faUserPlus, faSignInAlt, faSignOutAlt);
Vue.config.productionTip = false;
Vue.use(VeeValidate);
Vue.component('font-awesome-icon', FontAwesomeIcon);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
You can see that we import and apply in Vue
object:
– store
for Vuex (implemented later in src/store)
– router
for Vue Router (implemented later in src/router.js)
– bootstrap
with CSS
– vee-validate
– vue-fontawesome
for icons (used later in nav
)
Create Services
We create two services in src/services folder:
services
auth-header.js
auth.service.js (Authentication service)
user.service.js (Data service)
Authentication service
The service provides three important methods with the help of axios for HTTP requests & reponses:
login()
: POST {username, password} & saveJWT
to Local Storagelogout()
: removeJWT
from Local Storageregister()
: POST {username, email, password}
import axios from 'axios';
const API_URL = 'http://localhost:8080/api/auth/';
class AuthService {
login(user) {
return axios
.post(API_URL + 'signin', {
username: user.username,
password: user.password
})
.then(response => {
if (response.data.accessToken) {
localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem('user');
}
register(user) {
return axios.post(API_URL + 'signup', {
username: user.username,
email: user.email,
password: user.password
});
}
}
export default new AuthService();
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Data service
We also have methods for retrieving data from server. In the case we access protected resources, the HTTP request needs Authorization header.
Let’s create a helper function called authHeader()
inside auth-header.js:
export default function authHeader() {
let user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}
It checks Local Storage for user
item.
If there is a logged in user
with accessToken
(JWT), return HTTP Authorization header. Otherwise, return an empty object.
Note: For Node.js Express back-end, please use x-access-token header like this:
export default function authHeader() {
let user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// for Node.js Express back-end
return { 'x-access-token': user.accessToken };
} else {
return {};
}
}
Now we define a service for accessing data in user.service.js:
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/api/test/';
class UserService {
getPublicContent() {
return axios.get(API_URL + 'all');
}
getUserBoard() {
return axios.get(API_URL + 'user', { headers: authHeader() });
}
getModeratorBoard() {
return axios.get(API_URL + 'mod', { headers: authHeader() });
}
getAdminBoard() {
return axios.get(API_URL + 'admin', { headers: authHeader() });
}
}
export default new UserService();
You can see that we add a HTTP header with the help of authHeader()
function when requesting authorized resource.
Define Vuex Authentication module
We put Vuex module for authentication in src/store folder.
store
auth.module.js (authentication module)
index.js (Vuex Store that contains all modules)
Now open index.js file, import auth.module
to main Vuex Store here.
import Vue from 'vue';
import Vuex from 'vuex';
import { auth } from './auth.module';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
auth
}
});
Then we start to define Vuex Authentication module that contains:
- state: { status, user }
- actions: { login, logout, register }
- mutations: { loginSuccess, loginFailure, logout, registerSuccess, registerFailure }
We use AuthService
which is defined above to make authentication requests.
auth.module.js
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, user) {
return AuthService.login(user).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
},
register({ commit }, user) {
return AuthService.register(user).then(
response => {
commit('registerSuccess');
return Promise.resolve(response.data);
},
error => {
commit('registerFailure');
return Promise.reject(error);
}
);
}
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
registerSuccess(state) {
state.status.loggedIn = false;
},
registerFailure(state) {
state.status.loggedIn = false;
}
}
};
You can find more details about Vuex at Vuex Guide.
Create Vue Authentication Components
Define User model
To make code clear and easy to read, we define the User
model first.
Under src/models folder, create user.js like this.
export default class User {
constructor(username, email, password) {
this.username = username;
this.email = email;
this.password = password;
}
}
Let’s continue with Authentication Components.
Instead of using axios or AuthService
directly, these Components should work with Vuex Store:
– getting status with this.$store.state.auth
– making request by dispatching an action: this.$store.dispatch()
views
Login.vue
Register.vue
Profile.vue
Vue Login Page
In src/views folder, create Login.vue file with following code:
<template>
<div class="col-md-12">
<div class="card card-container">
<img
id="profile-img"
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
class="profile-img-card"
/>
<form name="form" @submit.prevent="handleLogin">
<div class="form-group">
<label for="username">Username</label>
<input
v-model="user.username"
v-validate="'required'"
type="text"
class="form-control"
name="username"
/>
<div
v-if="errors.has('username')"
class="alert alert-danger"
role="alert"
>Username is required!</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
v-model="user.password"
v-validate="'required'"
type="password"
class="form-control"
name="password"
/>
<div
v-if="errors.has('password')"
class="alert alert-danger"
role="alert"
>Password is required!</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" :disabled="loading">
<span v-show="loading" class="spinner-border spinner-border-sm"></span>
<span>Login</span>
</button>
</div>
<div class="form-group">
<div v-if="message" class="alert alert-danger" role="alert">{{message}}</div>
</div>
</form>
</div>
</div>
</template>
<script>
import User from '../models/user';
export default {
name: 'Login',
data() {
return {
user: new User('', ''),
loading: false,
message: ''
};
},
computed: {
loggedIn() {
return this.$store.state.auth.status.loggedIn;
}
},
created() {
if (this.loggedIn) {
this.$router.push('/profile');
}
},
methods: {
handleLogin() {
this.loading = true;
this.$validator.validateAll().then(isValid => {
if (!isValid) {
this.loading = false;
return;
}
if (this.user.username && this.user.password) {
this.$store.dispatch('auth/login', this.user).then(
() => {
this.$router.push('/profile');
},
error => {
this.loading = false;
this.message =
(error.response && error.response.data) ||
error.message ||
error.toString();
}
);
}
});
}
}
};
</script>
<style scoped>
label {
display: block;
margin-top: 10px;
}
.card-container.card {
max-width: 350px !important;
padding: 40px 40px;
}
.card {
background-color: #f7f7f7;
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}
.profile-img-card {
width: 96px;
height: 96px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
</style>
This page has a Form with username
& password
. We use VeeValidate 2.x to validate input before submitting the form. If there is an invalid field, we show the error message.
We check user logged in status using Vuex Store: this.$store.state.auth.status.loggedIn
. If the status is true
, we use Vue Router to direct user to Profile Page:
created() {
if (this.loggedIn) {
this.$router.push('/profile');
}
},
In the handleLogin()
function, we dispatch 'auth/login'
Action to Vuex Store. If the login is successful, go to Profile Page, otherwise, show error message.
Vue Register Page
This page is similar to Login Page.
For form validation, we have some more details:
username
: required|min:3|max:20email
: required|email|max:50password
: required|min:6|max:40
For form submission, we dispatch 'auth/register'
Vuex Action.
src/views/Register.vue
<template>
<div class="col-md-12">
<div class="card card-container">
<img
id="profile-img"
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
class="profile-img-card"
/>
<form name="form" @submit.prevent="handleRegister">
<div v-if="!successful">
<div class="form-group">
<label for="username">Username</label>
<input
v-model="user.username"
v-validate="'required|min:3|max:20'"
type="text"
class="form-control"
name="username"
/>
<div
v-if="submitted && errors.has('username')"
class="alert-danger"
>{{errors.first('username')}}</div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
v-model="user.email"
v-validate="'required|email|max:50'"
type="email"
class="form-control"
name="email"
/>
<div
v-if="submitted && errors.has('email')"
class="alert-danger"
>{{errors.first('email')}}</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
v-model="user.password"
v-validate="'required|min:6|max:40'"
type="password"
class="form-control"
name="password"
/>
<div
v-if="submitted && errors.has('password')"
class="alert-danger"
>{{errors.first('password')}}</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block">Sign Up</button>
</div>
</div>
</form>
<div
v-if="message"
class="alert"
:class="successful ? 'alert-success' : 'alert-danger'"
>{{message}}</div>
</div>
</div>
</template>
<script>
import User from '../models/user';
export default {
name: 'Register',
data() {
return {
user: new User('', '', ''),
submitted: false,
successful: false,
message: ''
};
},
computed: {
loggedIn() {
return this.$store.state.auth.status.loggedIn;
}
},
mounted() {
if (this.loggedIn) {
this.$router.push('/profile');
}
},
methods: {
handleRegister() {
this.message = '';
this.submitted = true;
this.$validator.validate().then(isValid => {
if (isValid) {
this.$store.dispatch('auth/register', this.user).then(
data => {
this.message = data.message;
this.successful = true;
},
error => {
this.message =
(error.response && error.response.data) ||
error.message ||
error.toString();
this.successful = false;
}
);
}
});
}
}
};
</script>
<style scoped>
label {
display: block;
margin-top: 10px;
}
.card-container.card {
max-width: 350px !important;
padding: 40px 40px;
}
.card {
background-color: #f7f7f7;
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}
.profile-img-card {
width: 96px;
height: 96px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
</style>
Profile Page
This page gets current User from Vuex Store and show information. If the User is not logged in, it directs to Login Page.
src/views/Profile.vue
<template>
<div class="container">
<header class="jumbotron">
<h3>
<strong>{{currentUser.username}}</strong> Profile
</h3>
</header>
<p>
<strong>Token:</strong>
{{currentUser.accessToken.substring(0, 20)}} ... {{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}}
</p>
<p>
<strong>Id:</strong>
{{currentUser.id}}
</p>
<p>
<strong>Email:</strong>
{{currentUser.email}}
</p>
<strong>Authorities:</strong>
<ul>
<li v-for="(role,index) in currentUser.roles" :key="index">{{role}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Profile',
computed: {
currentUser() {
return this.$store.state.auth.user;
}
},
mounted() {
if (!this.currentUser) {
this.$router.push('/login');
}
}
};
</script>
Create Vue Components for accessing Resources
These components will use UserService
to request data.
views
Home.vue
BoardAdmin.vue
BoardModerator.vue
BoardUser.vue
Home Page
This is a public page.
src/views/Home.vue
<template>
<div class="container">
<header class="jumbotron">
<h3>{{content}}</h3>
</header>
</div>
</template>
<script>
import UserService from '../services/user.service';
export default {
name: 'Home',
data() {
return {
content: ''
};
},
mounted() {
UserService.getPublicContent().then(
response => {
this.content = response.data;
},
error => {
this.content =
(error.response && error.response.data) ||
error.message ||
error.toString();
}
);
}
};
</script>
Role-based Pages
We have 3 pages for accessing protected data:
- BoardUser page calls
UserService.getUserBoard()
- BoardModerator page calls
UserService.getModeratorBoard()
- BoardAdmin page calls
UserService.getAdminBoard()
This is an example, other Page are similar to this Page.
src/views/BoardUser.vue
<template>
<div class="container">
<header class="jumbotron">
<h3>{{content}}</h3>
</header>
</div>
</template>
<script>
import UserService from '../services/user.service';
export default {
name: 'User',
data() {
return {
content: ''
};
},
mounted() {
UserService.getUserBoard().then(
response => {
this.content = response.data;
},
error => {
this.content =
(error.response && error.response.data) ||
error.message ||
error.toString();
}
);
}
};
</script>
Define Routes for Vue Router
Now we define all routes for our Vue Application.
src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
Vue.use(Router);
export const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/home',
component: Home
},
{
path: '/login',
component: Login
},
{
path: '/register',
component: Register
},
{
path: '/profile',
name: 'profile',
// lazy-loaded
component: () => import('./views/Profile.vue')
},
{
path: '/admin',
name: 'admin',
// lazy-loaded
component: () => import('./views/BoardAdmin.vue')
},
{
path: '/mod',
name: 'moderator',
// lazy-loaded
component: () => import('./views/BoardModerator.vue')
},
{
path: '/user',
name: 'user',
// lazy-loaded
component: () => import('./views/BoardUser.vue')
}
]
});
This is the root container for our application that contains navigation bar. We will add router-view
here.
src/App.vue
<template>
<div id="app">
<nav class="navbar navbar-expand navbar-dark bg-dark">
<a href class="navbar-brand" @click.prevent>bezKoder</a>
<div class="navbar-nav mr-auto">
<li class="nav-item">
<router-link to="/home" class="nav-link">
<font-awesome-icon icon="home" />Home
</router-link>
</li>
<li v-if="showAdminBoard" class="nav-item">
<router-link to="/admin" class="nav-link">Admin Board</router-link>
</li>
<li v-if="showModeratorBoard" class="nav-item">
<router-link to="/mod" class="nav-link">Moderator Board</router-link>
</li>
<li class="nav-item">
<router-link v-if="currentUser" to="/user" class="nav-link">User</router-link>
</li>
</div>
<div v-if="!currentUser" class="navbar-nav ml-auto">
<li class="nav-item">
<router-link to="/register" class="nav-link">
<font-awesome-icon icon="user-plus" />Sign Up
</router-link>
</li>
<li class="nav-item">
<router-link to="/login" class="nav-link">
<font-awesome-icon icon="sign-in-alt" />Login
</router-link>
</li>
</div>
<div v-if="currentUser" class="navbar-nav ml-auto">
<li class="nav-item">
<router-link to="/profile" class="nav-link">
<font-awesome-icon icon="user" />
{{ currentUser.username }}
</router-link>
</li>
<li class="nav-item">
<a class="nav-link" href @click.prevent="logOut">
<font-awesome-icon icon="sign-out-alt" />LogOut
</a>
</li>
</div>
</nav>
<div class="container">
<router-view />
</div>
</div>
</template>
<script>
export default {
computed: {
currentUser() {
return this.$store.state.auth.user;
},
showAdminBoard() {
if (this.currentUser && this.currentUser.roles) {
return this.currentUser.roles.includes('ROLE_ADMIN');
}
return false;
},
showModeratorBoard() {
if (this.currentUser && this.currentUser.roles) {
return this.currentUser.roles.includes('ROLE_MODERATOR');
}
return false;
}
},
methods: {
logOut() {
this.$store.dispatch('auth/logout');
this.$router.push('/login');
}
}
};
</script>
Our navbar looks more professional when using font-awesome-icon
.
We also make the navbar dynamically change by current User’s roles
which are retrieved from Vuex Store state
.
If you want to check Authorized status everytime a navigating action is trigger, just add router.beforeEach()
at the end of src/router.js like this:
router.beforeEach((to, from, next) => {
const publicPages = ['/login', '/register', '/home'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
// trying to access a restricted page + not logged in
// redirect to login page
if (authRequired && !loggedIn) {
next('/login');
} else {
next();
}
});
Configure Port for Vue App
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, so we also need to configure port for our App.
In project root folder, create vue.config.js file with following content:
module.exports = {
devServer: {
port: 8081
}
}
We’ve set our app running at port 8081
.
Conclusion
Congratulation!
Today we’ve done so many interesting things. I hope you understand the overall layers of our Vue application, and apply it in your project at ease. Now you can build a front-end app that supports JWT Authentication with Vue.js, Vuex and Vue Router.
You should add Refresh Token, more details at:
Vue Refresh Token with Axios and JWT example
If you want Typescript version of this project, please visit:
Vue/Vuex Typescript example: JWT Authentication
You will need to make this client work with one of following Servers:
- Spring Boot JWT with Spring Security (MySQL/PostgreSQL)
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node.js JWT Authentication & Authorization with MySQL
- Node.js JWT Authentication & Authorization with MongoDB
- Node.js JWT Authentication & Authorization with PostgreSQL
Happy learning, see you again!
Further Reading
- Vue Router Guide
- Vuex Guide
- VeeValidate 2.x
- In-depth Introduction to JWT-JSON Web Token
- Node.js Express + Vue.js: JWT Authentication & Authorization example
- Spring Boot + Vue: Authentication with JWT & Spring Security Example
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Fullstack CRUD App:
- Vue.js + Node.js + Express + MySQL
- Vue.js + Node.js + Express + PostgreSQL
- Vue.js + Node.js + Express + MongoDB
- Vue.js + Spring Boot + MySQL/PostgreSQL
- Vue.js + Spring Boot + MongoDB
- Vue.js + Django Rest Framework
Integration:
– Integrate Vue.js with Spring Boot
– Integrate Vue App with Node.js Express
Source Code
You can find the complete source code for this tutorial on Github.
Vue 3 version: Vue 3 Authentication with JWT, Vuex, Axios and Vue Router
Definitely believe that this is one of the best Vue auth tutorial.
Will likely be back to get more. Thanks!
That is really interesting Vuex tutorial. I have shared your site in my social networks. Thanks!
Thanks for the tutorial everything works, but I have a question.
Right now a normal user can visit every page even the Admin pages. How should I fix this in the routing? Or is there any way I can do this so that the user can just visit a few pages.
Hi, you can check the user role and redirect him to another page 🙂
good tutorial ❤☺
I enjoy reading through your Vue tutorial. Thanks!
Thanks
Thanks a lot for this cool and helpful tutorial.
I am trying to figure out one more thing: When the browser is ideal for quiet a while and after that I refresh my browser, it appears as the loggedIn function is still returning true (since in the localStorage there is still the user item), but the token has expired. When I now make a backend request, it of course fails, but without any action. How can I intercept that the token is invalid and redirect the user to the login page?
Thank you!
Hi, you can continue to read following tutorial:
Vue Refresh Token with Axios and JWT example
Ꭲhanks a lot!
Thank you for this nice tutorial. It is possibile to set an expiration to the token? I would like that the client require to the user to login again after a while-
Hi, first we need to implement Refresh Token on server side. Then we add some code to this client to check expired token and send new refresh token request.
You can read following tutorials for backend:
– Spring Boot Refresh Token with JWT example
– JWT Refresh Token in Node.js example
– JWT Refresh Token in Node.js and MongoDB
how can i get the user’s password? i tried currentUser.password but its not showing.
I want to have a different navbars base on different roles when the user prompt to login. May I ask if you have any idea about it? Thanks😊
Thanks!
Congratulations on the tutorial.
I have frontend and backend on the same server, when I connect directly through the server everything works fine. when I connect from a remote machine the frontend opens but I get the following “Network Error”. How can I solve? Thank you.
Thanks a lot for this tutorials it’s very clear and very helpful!
You take time to show the structure project, it ‘s very professional
Thanks for the awesome tutorial!
I am busy integrating with my own app.
Can you make a tutorial on how to integrate Google ReCaptcha with Spring boot and Vue?
One of the best Auth tutorial for Vue Client.
Awesome tutorial. Thank you bezkoder.
Hi,
You made it very simple, great stuff
Great tutorial. How to integrate the vue frontend with the backend?
Hi, just visit the tutorial for backend servers that I mentioned in the tutorial. 🙂
This is very clear, great stuff
excellent tutorial!!!
Bless you kind sir!
Realy appreciate all your hard work in providing such a super duper example with the souce code in it!
Saved my skin!
reading throught your tutorial I’ve came to understand more and better working with sequelize and vue js.
Wow, amazing Vue Auth tutorial!
you made the work look easy. The overall look of your web site is excellent, as well as the content.
Hello, excellent tutorial.
Is there a way to put the Authorization headers in vue.config.js so as not to put it on every request. I tried importing auth-header in the vue.config.js but it gives me error.
Thanks
Great tutorial! Truly a well put together project. Do you have any plans to update this tutorial for Vue 3?
Yeah, I will do it when having time 🙂
Thank you! Looking forward to it 🙂
Bonjour
Excellent tuto. bien compréhensible.
Est il possible d’ajouter le téléchargeant d’un fichier image au formulaire d’inscription.
Vraiment c’est un super tutorial.
Merci
Great tutorial! Thanks
Fortunately found your Vue tutorial! It helps me a lot.
Hey, about auth.module.js, why in the actions > login after then and catch , You return Promise.resolve and Promise.reject ?
Best regards
Hi, because we need to use Promise (then, catch) in Login/Register component 🙂
Hi bezkoder,
thank you very much for the detailed tutorial!
How did you create the flow chart of the requests and messages between Vue App and Backend under “Flow for User Registration and User Login”? Is there a specific tool you used or maybe at least a template to create this graphic? I want to create something like this for my application as well. Thank you for your help.
Greetings from Vienna!
Hello,
thank you for this great tutorial,
i wonder about all the «computed» values that looks if user is logged In.
it looks to me that they should be method as a change in vuex store would not change the result of the computed value because the store is not a «reactive dependency» see doc here https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods
once i changed all these computed to methods, it all went well.
Still i’m very surprised you get it wrong on something as trivial as that, so it bother me that i must be missing something important
Your tutorials are priceless!
For some weird reason I cannot APIs like ‘/api/test/all’, ‘/api/auth/signin’ etc. work on my server.
They work perfectly fine on my local machine. There are also other APIs which I build following your other tutorial and they work fine on the server.
The kind of error I get is: “Cannot GET /api/test/all”.
Any ideas why it doesn’t work?
Thank you!
I’ve solved it. I’ve just installed all the necessary packages that weren’t on the server and rebooted the server.
Hi! Thank you. It’s a great tutorial. I like the approache with services.
How about to use “this.$store.getters.isLoggedIn” instead “this.$store.state.auth.status.loggedIn”?
This authentication needs AccessToken + RefreshToken and verification them lifetime in App.vue -> Created hook using interceptors.
Hi! Thank you very much for your tutorials, really helpful! I followed the tutorial but I can’t quite understand how do you sign-up as an admin or a moderator. Could you help me with an explanation? Thank you
Hi, you can use a HTTP Client App to send POST signup request (with role in payload) to the server.
I love your content. Very helpful….
Thank you, I appreciate you taking the time to cater for the development community
Hi,
I implemented the same code in a Nuxt app and it doesn’t seem to work. It throws many issues and one being localStroage is not defined.
Please help!!!
Great series on jwt. One comment – I got a little held up on how the login action was being called. Specifically I was confused on the ‘auth/login’ string passed into the dispatch function (it initially looked like a route to me). Adding a comment about how namespacing is used here may be worth considering. Thank you, Jake
Hi, it is just a string and depends on how you want to classify actions 🙂
Hi! You are amazing. Thank you so much! All tutorials are pure gold, no unnecessary junk, so much appreciated!
Is it safe to pass the user to localStorage?
Hi, there is no completely safe way to store data in localStorage or Cookies.
You can read this stackoverflow question:
https://stackoverflow.com/questions/34817617/should-jwt-be-stored-in-localstorage-or-cookie
Thank you very much for this great tutorial!
As I understood, we store the token and the role in the localStorage, then use these information to prevent user to access the admin page. However, if the user modifies this role in the localStorage, he can visit the admin page normally (of course, he cannot use any API calls because on the backend, we checked his roles again).
Do you know if there is anyway to completely prevent user to access the admin page?
Thank you in advance.
Hi, you can send request to Auth API to check Authorization before rendering the UI.
Great and helpful tutorial!
One question I had was you mentioned “Instead of using axios or AuthService directly, these Components should work with Vuex Store”
Could you help me understand why this is the recommended design pattern? Or link to a resource that can?
Thanks!
Hi, let me explain the sentence briefly.
– “using axios or AuthService directly”: call axios method for HTTP requests such as post/get or
AuthService.auth()
method…– “these Components should work with Vuex Store”: the store container holds your application state – think about it as single source of truth. We use Vuex because login state, or loggedin user information should be checked and accessed from almost components.
You can read more details about the reason to use Vuex at the post:
https://vuejsdevelopers.com/2017/05/15/vue-js-what-is-vuex/
Hello thank you for this very good tutorial,
Right now, only the resources are protected, but users can still visit the admin page. Is there a way to just unauthorized users from the page completely?
Thank you
thanks app recharge
Thank you for this Vue Auth tutorial. I’m looking forward to seeing more tutorials in your website.
Hello and thank you for this great tutorial.
But i think persist your jwt in session or local storage seems to be a bad practice.
HTML5 Web Storage is vulnerable to XSS, has a larger attack surface area, and can impact all application users on a successful attack.
We should consider store our jwt in cookies for web applications because of the additional security they provide, and the simplicity of protecting against CSRF (which is the cookie method’s weakness).
great tutorial.
sessionStorage can be used instead of localStorage ?
Yeah, you can use Session Storage instead of Local Storage.
Very Nice.
How add reset password with email ?
Hi, I will write the tutorial for reset password when having time 🙂
i got a netwrok error. how can i fix it
Hi, you need to run one of the backend servers listed in the tutorial, then this frontend Vue App will work well 🙂
I am new to Vue / frontend in general. Your tutorials have been a fantastic resource to learn from. Keep up the good work!
Typescript version would be nice too!
Hi, I’ve just written Typescript version for this tutorial at:
Vue/Vuex Typescript example: JWT Authentication
Hello and thanks for this detailed tutorial.
Is there a way to migrate this solution to TypeScript?
Thanks
Hi, I’ll write the tutorial when having time 🙂
Hope to see your Typescript tutorial soon!
Hi, I’ve just written Typescript version for this tutorial at:
Vue/Vuex Typescript example: JWT Authentication
Hi, I’ve just written Typescript version for this tutorial at:
Vue/Vuex Typescript example: JWT Authentication
Ok the problem above got resolved through the 3 INSERT’s I have to do.
I was wondering where in the front end is the authentification check on every page? Because the router.beforeEach function you have commented out, so where is the authentication happening?
Hi, look at user.service.js, you can see we add
{ headers: authHeader() }
for each request to authorized resource.authHeader()
returns x-access-token header or Authorization header with JWT token.Hi, thanks a lot for the tutorial. At signup, I get error:
{ “timestamp”: “2020-02-15T06:34:30.014+0000”, “status”: 500, “error”: “Internal Server Error”, “message”: “Error: Role is not found.”, “path”: “/api/auth/signup” }
in the front end you are not passing role value as attribute for user
Hi Faraz,
This is a late reply but I thought I would try and answer it anyway.
You shouldn’t send the user role to the API as this is a security risk. One can then send post to the registration API and manually set the role to admin which is bad.
You should set the role on the API side.
Yes the backend is on a live server. The api are created on laravel. I’m doing only login because i should take the created users from backend to do the login. Maybe i should use the auth library websanova.
Hello!
Great Tutorial, thank u very much.
I am facing this problem when trying to login: gives the error: { “error”: “Unauthorized” }. How can i fix that?
Hi, how did you cause the issue? Did you run backend first? Also check database so that you can make sure registration is ok before logging in.
Hi bezkoder,
thank you for your great tutorial, it was the best and cleanest I found about JWT Authentication.
Really inspiring, truly! I based my new projects on your code 🙂
I still found some little things that could be “fixed” or improved in your code, maybe I could send you some patch on github if you’re interested in it.
Yeah, please send me your patch. I always want to learn everything from everyone. Thank you so much 🙂
Ok great 🙂 Here is my repo: https://github.com/tbl0605/vue-vuex-jwt-auth
Mostly bug fixes and some little improvements.
I’ll send a pull request on your repo, so you can comment or discuss my changes if you want 😉
Thank you so much. It looks great. I will take time to run and test your code next week. If everything runs, I will update this tutorial with your code.
Once again, thank you so much for your help. 🙂
You’re welcome 🙂 And thank you again for your great tutorial!
hi,
thanks for this nice tutorial, do you upload this section of tutorial to Git?
Hi, you can find the source code on Github.
thanks bezkoder,
your tutorial was best starter for my project.
i look forward to your new tutorials.
Thanks for your interest 🙂
Nice… But somehow I am not able to change model. Can you push your github repo with this part of fullstack project? I would like to compare it with my project.
Here you are: https://github.com/bezkoder/vue-vuex-jwt-auth
This is one of the best Vue.js tutorial for Authentication that combines many things inside: HTTP client, Vuex, JWT, Form validation.
Thank you so much for your effort.
Hey Bezkoder, nice Project and really helpfull.
Would it be possible to upload the sourcecode to github? This would help to check some files directly without searching for it.
Thank you very much!
Here you are: https://github.com/bezkoder/vue-vuex-jwt-auth
Hi Bezkoder, thanks for sharing this vue authentication project. I’ve searched many sites and this is the best.
Hi,
thank you very much for uploading the source code! Could you also provide the “user.js” file in the models folder?
Hi,
I’ve just added #Define_User_model to the post.
Thank you for your comment.