How to use Vue.js Navigation Guards to Restrict Access to Routes

In this tutorial, we’re going to see how to use Vuex with Vue.js navigation guards to protect certain pages within the application from unauthorized access.

Create a New Vue.js Project with the Vue CLI

To keep this tutorial simple and easy to understand, we’re going to start with a fresh project generated with the Vue CLI. As of right now, I’m using 3.9.3 of the Vue CLI.

From the Terminal or Command Prompt, execute the following command to create a new project:

vue create navguard-project

While we’re going to be using the Vue Router, don’t choose to enable it with the CLI. Instead choose the default settings when prompted and complete the wizard for project generation.

Once the project is created, navigate into the project and execute the following:

npm install vuex --save
npm install vue-router --save

We’ll be using Vuex to store our authentication state throughout the application. In a realistic scenario, the JSON Web Token (JWT) would be stored, but this is a basic example with no connected APIs or databases. We’re using the Vue Router to navigate between routes or components.


You may also like: Vue Authentication And Route Handling Using Vue-router


Define the Restricted Route as a Vue.js Component

Before we worry about our navigation guards or routing logic, let’s first create the component that we wish to secure. In the project’s src/components directory, create a Secure.vue file with the following code:

<template>
    <div>
        <h1>Secure Area</h1>
        <p>
            This is a secure area
        </p>
    </div>
</template>

<script>
    export default {
        name: "Secure"
    }
</script>

<style scoped></style>

The component isn’t going to get any more complex than what you see above. Remember, the point of this project is to make it so the user can only access this particular component if we allow them to.

The next step is to create the unprotected login page.

Creating the Login Component

While the Secure.vue component will have restrictions, we’re going to create a Login.vue component that will be readily available whenever requested.

Within the project’s src/components directory, create a Login.vue file with the following code:

<template>
    <div>
        <h1>Login</h1>
        <input type="text" name="username" v-model="input.username" placeholder="Username" />
        <input type="password" name="password" v-model="input.password" placeholder="Password" />
        <button type="button" v-on:click="login()">Login</button>
    </div>
</template>

<script>
    export default {
        name: "Login",
        data() {
            return {
                input: {
                    username: "",
                    password: ""
                }
            }
        },
        methods: {
            login() {
                if(this.input.username == "admin" && this.input.password == "pass") {
                    // Additional logic here...
                } else {
                    console.log("The username and / or password is incorrect");
                }
            }
        }
    }
</script>

<style scoped></style>

While very basic, the above code will change slightly as we progress. For now, the purpose is to show a simple form with bound variables. When trying to submit the form, the login method is called and we validate the username and password information that was provided.

I cannot stress this enough, but this is a simple mock data example. In a production scenario, you’ll likely make an HTTP request to your server to validate the username and password that the user provides with the username and password found in your database. This is outside the scope of what we want to accomplish.

Storing the Authorization Status in the Vuex Data Store

With the two components, which will eventually be routes, out of the way, we can focus on our data store. This is not to be confused with a database as this data store is just a global storage mechanism to be used throughout the lifespan of the users session in the application.

To configure the Vuex store, we need to make some changes to the project’s src/main.js file. Open this file and add the following:

import Vue from 'vue'
import Vuex from "vuex"
import App from './App.vue'

Vue.config.productionTip = false;

Vue.use(Vuex);

const store = new Vuex.Store(
    {
        state: {
            authenticated: false
        },
        mutations: {
            setAuthentication(state, status) {
                state.authenticated = status;
            }
        }
    }
);

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

Notice that we’ve imported Vuex, defined a new store, and added it to the main Vue constructor.

In the store itself, we have one variable that we wish to use. The authenticated variable is a boolean that is defaulted to false. The idea behind this is that when false, we shouldn’t be able to access the secure page, but when true, we should be able to. Because we cannot change data directly in the variable, we have to create a mutation. The purpose of the mutation is to set the variable between true and false.

Now that we have Vuex configured, we can make a small change to the Login.vue component:

methods: {
    login() {
        if(this.input.username == "admin" && this.input.password == "pass") {
            this.$store.commit("setAuthentication", true);
        } else {
            console.log("The username and / or password is incorrect");
        }
    }
}

In the login method, we are now leveraging the mutation through a commit command. If the login is successful, we say that our authentication status is now true. We’ll be revisiting this login method again, but at least now we can change the authentication status throughout the application.

This leads us to the actual navigation.

Defining Navigation Routes and Guarding Access

As it stands, we have two components, but they don’t really behave like pages that you can navigate between. This means we have to change a few things to turn them into routes.

Open the project’s src/main.js file and make the following changes:

import Vue from 'vue'
import VueRouter from "vue-router"
import Vuex from "vuex"
import App from './App.vue'
import Login from "./components/Login.vue"
import Secure from "./components/Secure.vue"

Vue.config.productionTip = false;

Vue.use(VueRouter);
Vue.use(Vuex);

const store = new Vuex.Store(
    {
        state: {
            authenticated: false
        },
        mutations: {
            setAuthentication(state, status) {
                state.authenticated = status;
            }
        }
    }
);

const router = new VueRouter({
    routes: [
        {
            path: '/',
            redirect: {
                name: "login"
            }
        },
        {
            path: "/login",
            name: "login",
            component: Login
        },
        {
            path: "/secure",
            name: "secure",
            component: Secure,
            beforeEnter: (to, from, next) => {
                if(store.state.authenticated == false) {
                    next(false);
                } else {
                    next();
                }
            }
        }
    ]
});

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

Notice that this time, we’ve also imported the Vue Router, defined some routes, and added it to the Vue constructor method.

There are two routes in our router, one for login and one for a secure page. Since the login route is not at the root path, if someone tries to access the root path, we just redirect them to the login route. What matters the most to us for this example is the beforeEnter method:

beforeEnter: (to, from, next) => {
    if(store.state.authenticated == false) {
        next(false);
    } else {
        next();
    }
}

Whenever we try to access the Secure.vue component, the beforeEnter method triggers. We check the Vuex store for the authentication status and if it says we are not authenticated, we stop what we’re doing and discontinue the navigation into the page. Otherwise, we proceed into the route.

So let’s make some final revisions.

Open the project’s src/App.vue file and make it look like the following:

<template>
    <div>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {
        name: 'app',
        components: {}
    }
</script>

<style></style>

We’ve removed a lot of stuff, but the most important thing we have now is the <router-view> tags. This acts as an outlet to our routes. The templates inside each component will render inside these tags.

Now that we have our components being rendered as routes, we don’t actually perform a navigation yet.

Open the project’s src/components/Login.vue file and make the following change to the login method:

methods: {
    login() {
        if(this.input.username == "admin" && this.input.password == "pass") {
            this.$store.commit("setAuthentication", true);
            this.$router.replace({ name: "secure" });
        } else {
            console.log("The username and / or password is incorrect");
        }
    }
}

After we set the authentication status, we attempt to route to the secure area. Remember, if you try to route to the secure area before updating the authentication status, the navigation will not happen because the navigation guard will prevent it.

Conclusion

You just saw how to use navigation guards in a Vue.js application to restrict access to particular routes. This is useful for many things, such as user login within your application, but it is also very useful for many other scenarios.

Learn More

Vue Router Tutorial - Navigation Guards

#vue-js #javascript

How to use Vue.js Navigation Guards to Restrict Access to Routes
5 Likes121.65 GEEK