Complete login system with Node.js & Vue.js | Vuex | Part [2/2]

Complete login system with Node.js & Vue.js | Vuex | Part [2/2]

In this article you will learn how to use Vue.js, Vuex and Axios to program the frontend for a complete login system.

This article is the second part of the two-part series Complete Login System with Node.js & Vue.js. We take care of the implementation of a complete login system with Node.js as backend and Vue.js as frontend. The tutorial is divided into two parts, so that you are not bound to a Node.js RestAPI, but can use the Vue.js frontend with another system of your choice, like PHP or Python.

➡️ Part 1: Complete login system with Node.js & Vue.js | RestAPI & JWT

Our goal for part 2: Vue.js frontend with Vuex and Axios

We create a Vue.js frontend where we create a session for the user, store it, and can access it over and over again to check if a user is logged in and has a valid session. We use Vue CLI, Axios, Vuex and the vuex-persistedstate module to temporarily store the session.

At the end you have a web application where users can register and log in. In addition, some pages should only be accessible if the user has a valid session, i.e. is logged in. But now we finally want to start! 🙂

Cup with inscription

1. How Vuex works

Vuex is a kind of “store” in which different states are stored. If this data is adjusted, your Vue Component updates itself automatically. We use this to save the login status of the user to get access to different routes and to display adapted texts, etc.

We also use vuex-persistedstate to keep the data even after reloading the page. But more about this in step 3.

2. Initialize Vue App

At the beginning we have to initialize the Vue App via the Vue CLI. We do this with the following command:

vue create client

We have to select different components and modules that should be installed as standard. Here it makes sense to create your own preset (here “lh-standard”) and use it for future projects. So you should select “Manually select features” here. For this tutorial we absolutely need the modules vue-router and vuex. Everything else is optional.

Vue Create

Once the process is complete, you will receive a folder with this (or similar) structure.

Vue folder structure

3. Install dependencies

The next step is to install additional packages. Among them axios to send HTTP requests and vuex-persistedstate to get the data from Vuex even after reloading the page.

npm install axios vuex-persistedstate

4. Create Routes

Now we’ll plot three routes:

  • …/ => This route should only be accessible for logged in users.
  • …/sign-up => Registration page
  • …/login => Login page

We adapt the standard router file and assign each route its own Vuex View.

// src/router.js

import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import SignUp from "./views/SignUp.vue";
import Login from "./views/Login.vue";

Vue.use(Router);

export default new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/",
      name: "home",
      component: Home
    },
    {
      path: "/sign-up",
      name: "sign-up",
      component: SignUp
    },
    {
      path: "/login",
      name: "login",
      component: Login
    },
  ]
});

5. Set up Vuex

The next step is to set up Vuex. Therefore we integrate the plugin vuex-persistedstate (lines 6 and 21). This helps us to get data even after reloading the page.

To “save” the data we now create the status token and user. We also create actions for login and logout. Very important: In line 48 we set the JWT token of our RestAPI as Authorization Header. Without this line all future requests to the RestAPI would not work!

If you want to know more about Vuex, you can read it here.

// src/store.js

import Vue from 'vue';
import Vuex from 'vuex';
import Axios from 'axios';
import createPersistedState from 'vuex-persistedstate';

import AuthService from '@/services/AuthService.js';

Vue.use(Vuex);

const getDefaultState = () => {
	return {
		token: '',
		user: {}
	};
};

export default new Vuex.Store({
	strict: true,
	plugins: [createPersistedState()],
	state: getDefaultState(),
	getters: {
		isLoggedIn: state => {
			return state.token;
		},
		getUser: state => {
			return state.user;
		}
	},
	mutations: {
		SET_TOKEN: (state, token) => {
			state.token = token;
		},
		SET_USER: (state, user) => {
			state.user = user;
		},
		RESET: state => {
			Object.assign(state, getDefaultState());
		}
	},
	actions: {
		login: ({ commit, dispatch }, { token, user }) => {
			commit('SET_TOKEN', token);
			commit('SET_USER', user);

			// set auth header
			Axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
		},
		logout: ({ commit }) => {
			commit('RESET', '');
		}
	}
});

So that the JWT token is passed also with each call, we must still adapt the main.js as follows.

// src/main.js

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import Axios from 'axios';

Vue.config.productionTip = false;

// set auth header
Axios.defaults.headers.common['Authorization'] = `Bearer ${store.state.token}`;

new Vue({
	router,
	store,
	render: h => h(App)
}).$mount('#app');

6. Services for registration and login

We create a new file AuthService.js to make requests to our server (RestAPI). For this we need a function login(credentials) for the login and the function signUp(credentials) to register. Here you need our package axios, which we already installed in step 3.

The third function getSecretContent() returns the secret content, which is only displayed if you are logged in.

// src/services/AuthService.js

import axios from 'axios';

const url = 'http://localhost:3000/api/';

export default {
	login(credentials) {
		return axios
			.post(url + 'login/', credentials)
			.then(response => response.data);
	},
	signUp(credentials) {
		return axios
			.post(url + 'sign-up/', credentials)
			.then(response => response.data);
	},
	getSecretContent() {
		return axios.get(url + 'secret-route/').then(response => response.data);
	}
};

7. Sign Up page

The probably most important step: The registration of new users. We have already created a route in the router and are now creating the appropriate Vue View. Here we have a simple form with user name, password, password repetition and a button to register.

When clicking, we call the signUp(credentials)function from the AuthService and pass the entered user data. Additionally we have a field in which error and success messages are displayed automatically: msg.

// src/views/SignUp.vue

<template>
	<div>
		<h1>Sign Up</h1>
		<input type="text" placeholder="Username" v-model="username" />
		<input type="text" placeholder="Password" v-model="password" />
		<input
			type="text"
			placeholder="Password (repeat)"
			v-model="password_repeat"
		/>
		<input type="button" @click="signUp" value="Sign Up" />
		<p v-if="msg">{{ msg }}</p>
	</div>
</template>
<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			username: '',
			password: '',
			password_repeat: '',
			msg: ''
		};
	},
	methods: {
		async signUp() {
			try {
				const credentials = {
					username: this.username,
					password: this.password,
					password_repeat: this.password_repeat
				};
				const response = await AuthService.signUp(credentials);
				this.msg = response.msg;
			} catch (error) {
				this.msg = error.response.data.msg;
			}
		}
	}
};
</script>

Optionally you can forward line 38 to the login page. For this you can use this code:

this.$router.push('/');

If we call this page we will get this result with full functionality:

Vue Sign Up Demo

8. Login page

At login we have a similar structure and this time we call the login(credentials) function.

It is important to call Vuex in line 36. We pass the user object user and the token token from the HTTP response and write the data in Vuex.

After successful login, line 38 will forward you to the “protected route”, which should only be accessible after login.

// src/views/Login.vue

<template>
	<div>
		<h1>Login</h1>
		<input type="text" placeholder="Username" v-model="username" />
		<input type="text" placeholder="Password" v-model="password" />
		<input type="button" @click="login" value="Login" />
		<p v-if="msg">{{ msg }}</p>
	</div>
</template>
<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			username: '',
			password: '',
			msg: ''
		};
	},
	methods: {
		async login() {
			try {
				const credentials = {
					username: this.username,
					password: this.password
				};
				const response = await AuthService.login(credentials);
				this.msg = response.msg;

				const token = response.token;
				const user = response.user;

				this.$store.dispatch('login', { token, user });

				this.$router.push('/');
			} catch (error) {
				this.msg = error.response.data.msg;
			}
		}
	}
};
</script>

And this is what the route looks like in action:

Vue Login Demo

9. Protect routes

Registration and login is complete. Now comes the part where we want to protect our route / and make it accessible only with valid user data. We go to the corresponding view and add a few lines to check if the user is logged in. If not, he will be redirected to the login page.

// src/views/Home.vue

<template>
	<div>
		<h1>Hi {{ username }}</h1>
		<p>{{ secretMessage }}</p>
		<input type="button" value="Logout" @click="logout" />
	</div>
</template>

<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			secretMessage: '',
			username: ''
		};
	},
	async created() {
		if (!this.$store.getters.isLoggedIn) {
			this.$router.push('/login');
		}

		this.username = this.$store.getters.getUser.username;

		this.secretMessage = await AuthService.getSecretContent();
	},
	methods: {
		logout() {
			this.$store.dispatch('logout');
			this.$router.push('/login');
		}
	}
};
</script>

We have also added a logout button to end the current session and the username will also be displayed. Of course everything is extendable and customizable. Our login system looks like this:

Vue Login System

10. Conclusion

That?s it! In the second part we implemented the Vue.js frontend with the help of Vuex. We created a registration page, a login page, “normal” pages and pages protected by a login. If you haven’t read part 1: Creating the RestAPI with Node.js and JWT yet, you can do it directly to provide your login system with a backend component. With this we have implemented a complete login system, which you can use for your web applications. Thanks for reading! 🙂

Join the Conversation

1 Comment

Your email address will not be published. Required fields are marked *