VueJS Route Security and Authentication

Part 35 of 48 in API Driven Development With Laravel and VueJS
Dan Pastori avatar
Dan Pastori January 4th, 2018
⚡️ Updated content is available We learned a lot since we originally wrote this article. We now have this updated for Laravel 10, Vue 3/NuxtJS 3, and Capacitor 3.

In our last tutorial https://serversideup.net/public-private-api-laravel/, we converted the Laravel API Backend to be accessible for both an authenticated user and an unauthenticated user. This tutorial will go through the process of securing pieces of data that we don’t want the user to to view if they are not authenticated.

Before we make any crazy claims it’s very possible for users to just inject Javascript and tweak all sorts of things. We don’t want to rely 100% on the security of front end JS to secure our data. We just want to block actions from users. Our server and API will handle the hardcore security like preventing users from inserting or updating data.

Step 1: Add Redirect so Layout is Never in View

We are using the / route in VueJS to act as a global layout for all of our other routes. However, this is still a navigable route. We want to redirect this to our cafes home page.

Open up the /resources/assets/js/routes.js file and add the redirect key to your routes object underneath the path we are using for layout:

{
  path: '/',
  redirect: { name: 'home' },
  name: 'layout',
  component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
  children: [

Now whenever anyone visits this page, they are redirected to the home screen.

Step 2: Create Modal for Logging In

Since we removed the login route from the PHP side in the last tutorial https://serversideup.net/public-private-api-laravel/, we are now allowing our social login through a modal that we can call from anywhere. This way every thing is contained in our single page application.

First, let’s create the /resources/assets/js/components/global/LoginModal.vue component and enter the basic component stub:

<style lang="scss">

</style>

<template>

</template>

<script>
  import { EventBus } from '../../event-bus.js';

  export default {

  }
</script>

This component will essentially just provide links to our /login/{social} url. The structure, we will have copied from our now deleted login.blade.php. First, let’s fill out the template with:

<template>
  <div id="login-modal" v-show="show" v-on:click="show = false">
    <div class="login-box">
            <a href="/login/google" v-on:click.stop="">
                <img src="/img/google-login.svg"/>
            </a>

            <a href="/login/twitter" v-on:click.stop="">
                <img src="/img/twitter-login.svg"/>
            </a>

            <a href="/login/facebook" v-on:click.stop="">
                <img src="/img/facebook-login.svg"/>
            </a>
        </div>
  </div>
</template>

What this does is wrap a login-box element with a login-modal element. Each link in the login box allows the user to log in with whatever social network that they want similar to what we had before. One thing to note about those links is they have an attribute of v-on:click.stop="". What this does is stop the propagation so the event stops after the user clicks. This is because the login-modal will close on a click and we don’t want the modal to close on a child click.

Now let’s add our functionality:

<script>
  import { EventBus } from '../../event-bus.js';

  export default {
    data(){
      return {
        show: false
      }
    },

    mounted(){
      EventBus.$on('prompt-login', function(){
        this.show = true;
      }.bind(this));
    }
  }
</script>

The two important features of the functionality are 1. The show variable in the data() and 2, the prompt-login event we listen to. The show data allows us to toggle the show/hide of the modal. When set to true the modal is shown. When set to false the modal is hidden.

The prompt-login event we listen to toggles the show to either true or false to show the login.

Now to style the form, we will use some of the same styles as we did on the login.blade.php . Add these styles to the <style> tag:

<style lang="scss">
  div#login-modal{
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba( 0, 0, 0, .6 );

    div.login-box{
      max-width: 370px;
      min-width: 320px;
      padding: 0 10px;
      background-color: #fff;
      border: 1px solid #ddd;
      -webkit-box-shadow: 0 1px 3px rgba(50,50,50,0.08);
      box-shadow: 0 1px 3px rgba(50,50,50,0.08);
      -webkit-border-radius: 4px;
      border-radius: 4px;
      font-size: 16px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);

      a{
        display: block;
        margin: auto;
        width: 230px;
        margin-top: 10px;
        margin-bottom: 10px;
      }
    }
  }
</style>

Step 3: Set Up LoginModal.vue

Now that we got our login modal created we need to set it up. First open up your /resources/assets/js/pages/Layout.vue file. We will be importing our login modal here so add to the top of the component script:

import LoginModal from '../components/global/LoginModal.vue';

Then we will need to register it in our components object:

components: {
  Navigation,
  AppFooter,
  LoginModal
},

and place it right before the <app-footer></app-footer> component in the template:

<template>
  <div id="app-layout">
    <navigation></navigation>

    <router-view></router-view>

    <login-modal></login-modal>

    <app-footer></app-footer>
  </div>
</template>

We placed it in the Layout.vue template so we can call the login screen from any part of the app.

Now we need to open the Navigation.vue component. In this component, we will display a login link if the user isn’t logged in. In the template of this component, adjust the code for the right side navigation:

<img class="avatar" v-if="user != '' && userLoadStatus == 2" :src="user.avatar" v-show="userLoadStatus == 2"/>
<span class="logout" v-if="user != '' && userLoadStatus == 2" v-on:click="logout()">Logout</span>
<span class="login" v-if="user == ''" v-on:click="login()">Login</span>

What this does is determine if we should display the avatar if the user is logged in and a logout button or if we should display the login button.

We also need to import the EventBus:

import { EventBus } from '../../event-bus.js';

This way when the user clicks the login() link, we can emit the prompt-login method which opens our login modal.

Our login() method looks like:

login(){
  EventBus.$emit('prompt-login');
},

We also added a logout method that dispatches an action to our Vuex state to clear any data referring to the user. Our logout method looks like:

logout(){
  this.$store.dispatch('logoutUser');

  window.location = '/logout';
}

When we visit the /logout route our Laravel side of the application will log out the user and redirect them back to the application. We can also listen for the logoutUser event in any Vuex module and clear any data we don’t want saved after the user has logged in.

I added the action to /resources/assets/js/modules/users.js:

/*
  Logs out a user and clears the status and user pieces of
  state.
*/
logoutUser( { commit } ){
  commit( 'setUserLoadStatus', 0 );
  commit( 'setUser', {} );
}

Step 4: Adjust Views Based On Authentication

There are a few views we have to adjust based on whether the user is authenticated or not. We need to hide the add button on the cafes homepage, the edit but on the individual cafe page and the like button on the individual cafe page.

First, let’s open our Home.vue component. At the top of the page, adjust the template to look like:

<router-link :to="{ name: 'newcafe' }" v-if="user != '' && userLoadStatus == 2"  class="add-cafe-button">+ Add Cafe</router-link>
<a class="add-cafe-text" v-if="user == '' && userLoadStatus == 2" v-on:click="login()">Want to add a cafe? Create a profile and add your favorite cafe!</a>

What this does is hide the add cafe button if the user is not authenticated. Now this doesn’t prevent the user from typing in the URL and visiting the page. We will be dealing with Vue Router Navigation Guards in the next tutorial.

Now let’s open the Cafe.vue page. I wrapped the Edit link in a container and hid it if the user is not logged in:

<div class="edit-container" v-if="this.user != '' && this.userLoadStatus == 2">
  <router-link :to="{ name: 'editcafe', params: { id: cafe.id } }">Edit</router-link>
</div>

I also added a toggle for the like button if the user is not logged in. It prompts the user to log in by dispatching the event to login to the Login modal. Our template update should look like:

<toggle-like v-if="user != '' && userLoadStatus == 2"></toggle-like>
<a class="prompt-log-in" v-if="user == '' && userLoadStatus == 2" v-on:click="login()">Did you know you can "like" this cafe and save it to your profile? Just log in!</a>

and we should add the login() method to the methods object on the page:

/*
  Defines the methods used by the component.
*/
methods: {
  login(){
    EventBus.$emit('prompt-login');
  }
}

This way we will be able to show the login modal when we click the link to log in.

Conclusion

We now have cleaned up some of the VueJS front end and accounted for both public and private routes, we will add some navigation guards in the next tutorial. This will make sure that users do not navigate to pages they don’t have access to when they are not authenticated. This combined with the changes we’ve made on the Laravel side of things make sure no data is leaked. Even if they were to inject javascript to view a page to add a cafe, we already block the API from adding the cafe if submitted.

Be sure to check out all of the code here: GitHub – serversideup/roastandbrew: Helping the coffee enthusiast find their next cup of coffee. Also, helping aspiring web and mobile app developers build a single page app and converting it to a mobile hybrid. All tutorials can be found at https://serversideup.net

Keep Reading
View the course View the Course API Driven Development With Laravel and VueJS
Up Next → Vue Router Navigation Guards with Vuex

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.