David Atanda I'm a frontend developer who is passionate about technology and startups in Africa. I believe technology has the power to transform Africa into a first-world continent.

Handling authentication in Vue.js using Vuex, Vue Router, and Auth0

7 min read 2161

Vue authentication using Vuex, vue-router, and Auth0

Authentication is an essential feature critical to the security that most modern web apps need. If breached, it can pose security risks to your users by exposing their data to the wrong folks. Instead of handling authentication from scratch, many modern web apps use Auth0, an easy-to-implement, adaptable authentication and authorization platform.

Auth0 currently provides a SPA SDK to handle authentication within Vue. Then, a Vue plugin already available in the documentation wraps the functionality of the SDK, which in turn provides a high-level API abstraction that handles a lot of the smaller details of authentication implementation.

In this tutorial, instead of using the plugin and SDK as explained above, you’ll learn how to implement authentication using Auth0, traditional Vuex, and Vue Router. In the end, you’ll build a functional authentication service using Vue and Auth0, and implement authentication in any web app of your choice using these same processes. Let’s get started!

Installing Vue CLI

We will be using the Vue CLI to kickstart the project. If you already have Vue CLI installed on your system, you can skip to the next section.

First, you need to have the Vue CLI installed on your system. You can install it globally with this command:

npm install -g @vue/cli

We can now use the Vue CLI to create our project. Create a new project using this command:

vue create vue-sample-auth0

You will be asked to pick a preset. Choose Manually select features and then select Babel, Vuex, Router, and Linter/Formatter.

You will also be asked if you want to use History mode for Router — choose Yes. You can select any Linter you want, but for this tutorial I will be using ESLint and Prettier.

After the project has been created, navigate to the new project folder:

cd vue-sample-auth0

Then start the server:

We made a custom demo for .
No really. Click here to check it out.

npm run serve

Once the server is started, you can open your browser to view it. You should see this:

Vue welcome screen

Setting up Auth0

We’ll now install Auth0’s library to handle authentication on the client side:

npm i auth0-js

To get started with Auth0, you’ll need to sign up, create a new tenant, and select your region. Then you can proceed through these steps:

  1. On your dashboard, click on Applications.
    Application menu in Auth0
  2. Click on Create Application.
    Create your application in Auth0
  3. This modal should pop up, and you should select Single Page Web Applications.
    Click on single-page web applications
  4. We can then select Vue from the options.
    Select Vue in Auth0

Configuring your Auth0 settings

Now that you’ve created the application, it’s time to configure the settings.

Configuring your Auth0 Settings

You will see your Domain and Client ID under the Basic Information section. We’ll come back to this later on; first, we need to store these values as environmental variables for our application to work.

Under the Application URLs section, we will need to define these values:

  • Allowed callback URLs
  • Allowed logout URLs
  • Allowed web origins
  • Allowed origins (CORS)

Note that you’ll need to use your own local URL to test your application locally.

Allowed callback URLs

These are the only URLs you need to redirect your users after they successfully log in. We’ll use the local URL:

http://localhost:8080/authfinalize

I’ll explain why we’re sending the user to the authfinalize route later in the article.

Allowed logout URLs

These are the URLs that Auth0 can use to redirect your users after they log out.

http://localhost:8080

Allowed logout URLs screen in Auth0

Allowed web origins

These are the only URLs allowed to send requests to Auth0. You need to add this URL to prevent a cross-origin resource sharing (CORS) error. For now, we’ll add the local URL:

http://localhost:8080

Allowed web origins in Auth0

Allowed origins (CORS)

These are the only URLs that are allowed to make any requests to the server.

http://localhost:8080

Here, we’ll have to set our id_token expiration in seconds. We’ll use 36,000s (10 hours) for this sample site:

id_token expiration set in sample site

Now, let’s go back to our project folder and create a .env file for our local development. Add these environment variables:

VUE_APP_AUTH0_CONFIG_RESPONSETYPE=token id_token
VUE_APP_AUTH0_CONFIG_SCOPE=openid profile email
VUE_APP_AUTH0_CONFIG_DOMAIN={{ Domain }}
VUE_APP_AUTH0_CONFIG_DOMAINURL=https://{{ Domain }}
VUE_APP_AUTH0_CONFIG_CLIENTID={{ ClientID }}
VUE_APP_DOMAINURL={{Local host URL}}
VUE_APP_DOMAINURL_REDIRECT={{ Allowed Callback URL }}
VUE_APP_EXPIRY=36000

These variables will be used within our project as we go on. The access token and id_token will be returned as response after authentication, which can be used to verify the identity of the authenticated user.

Domain, ClientID, and Allowed Callback URL can all be found on the Settings page. The only difference between our VUE_APP_AUTH0_CONFIG_DOMAIN and VUE_APP_AUTH0_CONFIG_DOMAINURL is that we need to add https:// in front of the domain. All environmental variables in Vue must have a prefix of VUE_APP.

Setting up Vuex

We’ll consider Vuex as the single source of truth for our application, and so it’ll be used to initialize Auth0 within our application. We’ll also use it to handle data within our application.

First, we need to import auth0-js into our application and set up our state. Within our state, we’ll initialize Auth0 and add our application details from the environmental variables as discussed above.

import Vuex from "vuex";
import Vue from "vue";
import actions from "./auth/actions";
import getters from "./auth/getters";
import mutations from "./auth/mutations";
import auth0 from "auth0-js";
Vue.use(Vuex);
const state = () => ({
  userIsAuthorized: false,
  auth0: new auth0.WebAuth({
    domain: process.env.VUE_APP_AUTH0_CONFIG_DOMAIN,
    clientID: process.env.VUE_APP_AUTH0_CONFIG_CLIENTID,
    redirectUri: process.env.VUE_APP_DOMAINURL_REDIRECT,
    responseType: process.env.VUE_APP_AUTH0_CONFIG_RESPONSETYPE,
    scope: process.env.VUE_APP_AUTH0_CONFIG_SCOPE,
  }),
});
export default new Vuex.Store({
  namespaced: true,
  state,
  actions,
  getters,
  mutations,
});

We add userIsAuthorized to our state to accept boolean. We’ll use it as an indicator to know whether the user is logged in. Note that on the initial load of the application, this will register as false.

Vuex actions

login

Authenticating the user with Auth0 involves sending the user to an Auth0 portal outside our application. After that, we return them to our application. This login action will be called within our component to trigger this redirection.

//login.js

const login = ({state}) => {
    state.auth0.authorize()

}
export default login;
//logging in within the component

<template>
  <div class="home">
    <button @click="login">Login</button>
    <button @click="signup">Signup</button>
  </div>
</template>
<script>

export default {
  name: "Home",
  methods: {
    login() {
      this.$store.dispatch('login')
    },
    signup() {
      this.$store.dispatch('login')
    }
  }
};
</script>

authHandleAuthentication

When the user is returned to our application, the access token and id_token are sent back to us as a response. This action sets the expiration time for the token, and then stores access token, id_token, and expiration time locally before sending the user to the secured page, or the dashboard.

The function uses the parseHash from the Auth0 object inside our state to check if the user has the access token and id_token, and then it saves them locally before sending the user to the dashboard. On the other hand, if there’s an error, the user sees an error and gets redirected back to the homepage.

//auth0HandleAuthentication.js

import router from '../../../router/index'
const auth0HandleAuthentication = (context) => {
  context.state.auth0.parseHash((err, authResult) => {
    if (authResult && authResult.accessToken && authResult.idToken) {
      let expiresAt = JSON.stringify(
        (process.env.VUE_APP_EXPIRY * 1000) + new Date().getTime()
      );
      // save the tokens locally
      localStorage.setItem("access_token", authResult.accessToken);
      localStorage.setItem("id_token", authResult.idToken);
      localStorage.setItem("expires_at", expiresAt);
      router.push({ name : "dashboard"})
    }
    else if (err) {
      alert("There's is an issue with signing up", err)
      router.replace({ name: "home" })
    }
  })
};
export default auth0HandleAuthentication;

logout

We use the logout button to sign out the user. It takes the user back to Auth0’s site to log them out of our application completely. Then, it sends them back to our application using the Allowed Logout URLs we previously added.

//logout.js

const logout = () => {
  localStorage.removeItem("access_token");
  localStorage.removeItem("id_token");
  localStorage.removeItem("expires_at");
  window.location.href =
    process.env.VUE_APP_AUTH0_CONFIG_DOMAINURL +
    "/v2/logout?returnTo=" +
    process.env.VUE_APP_DOMAINURL +
    "/&client_id=" +
    process.env.VUE_APP_AUTH0_CONFIG_CLIENTID;
};
export default logout;
//logging out on the dashboard page

<template>
  <div class="hello">
    <h2>Here's the dashboard</h2>
    <button @click="logout">Logout</button>
  </div>
</template>
<script>
export default {
  name: 'Dashboard',
  methods: {
    logout() {
      this.$store.dispatch('logout')
    }
  }
}
</script>

Vuex mutations

We will only have one mutation that’ll be used to trigger our userIsAuthorized indicator within our state.

 const setUserIsAuthenticated = (state, replacement) => {
    state.userIsAuthorized = replacement;
}

vue-router

This is where the actions will be triggered and users will be routed, based on certain conditions in our route guards.

The user flow is direct:

  1. The user attempts login
  2. We send the user to the Auth0 login portal for authentication
  3. They’re then sent back to our application (/authfinalize page specifically) with the access token, id_token, etc.
  4. We save these tokens locally with the expiration time and redirect the user to the secured page

While setting up our routes, we will add the meta object for each route. This will allow us to identify the routes that are opened to everyone using requiresGuest, and the protected routes using requiresAuth.

These meta properties can be accessed on the route location and navigation guards.

import Vue from "vue";
import VueRouter from "vue-router";
import Store from "../store"
import Home from "../views/Home.vue";
import Authfinalize from "../views/Authfinalize"
import Dashboard from "../views/Dashboard"
Vue.use(VueRouter);
const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
    meta : {
      requiresGuest : true,
    }
  },
  {
    path: "/dashboard",
    name: "dashboard",
    component: Dashboard,
    meta : {
      requiresAuth : true
    }
  },
  {
    path: "/authfinalize",
    name: "Authfinalize",
    component: Authfinalize,
  },
];
const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});
router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some((x) => x.meta.requiresAuth);
  const requiresGuest = to.matched.some((x) => x.meta.requiresGuest);
  const auth0callback = to.matched.some(record=>record.path == "/authfinalize");
  let routerAuthCheck = false;  
  if(auth0callback) {
    Store.dispatch('auth0HandleAuthentication');
    next(false);
  }
  if( localStorage.getItem('access_token') && localStorage.getItem('id_token') && localStorage.getItem('expires_at') ){
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    routerAuthCheck = new Date().getTime() < expiresAt;  
  }
  Store.commit('setUserIsAuthenticated', routerAuthCheck); 
  if (requiresAuth) {  
    if(routerAuthCheck){
      next();
    }
    else{
      Store.dispatch('logout');
    }
  }

  if (requiresGuest && routerAuthCheck) {  
      next({ name: "dashboard" }); 
  } 
  next(); 
})

export default router;

In our beforeEach router guard, we should check which meta-object the intended route has and assign it accordingly to either a requiresAuth or requiresGuest variable. We can also create an auth0callback variable if we are navigating to the /authfinalize path.

Then, we create a variable named routerAuthCheck and assign it to false. We will use this variable to trigger the mutation that’ll change the indicator within our Vuex that we talked about earlier.

The first if block checks if we’re navigating to the /authfinalize route, and then calls the auth0HandleAuthentication. Note that the only time we will navigate to this route is when we’re being redirected back from Auth0.

The second if block checks if the user is authenticated and then it makes a commit, which switches our indicator to true when logged in and vice-versa. Basically, it checks the local storage to see if the tokens and expiration date are still are there.

It then goes further to check if the expiration date is greater than the current time. routerAuthCheck will reflect whether it’s true or false, and then it’ll commit to the setUserIsAuthenticated mutation, which switches our app indicator.

The third if block checks if the intended route requires authentication (requiresAuth), then checks if the routerAuthCheck is true or false. If true, the user goes on to the dashboard. If false, the user is logged out.

The last if block checks if an authenticated user is routing to the homepage (requiresGuest). Then, it automatically sends them to the dashboard instead.

Deploying your app

To deploy the app, you need to add your live URL to each of the settings demonstrated earlier. Separate each of the URLs with a comma.

Deploying your app with one URL

Deploying your app with multiple URLs

Remember that you’ll have to create a new .env file and set the URL for the live version within your codebase or the platform you’re deploying to, depending on what platform you’re using for deployment.

Conclusion

While there are other methods of handling authentication, tools like Auth0 can give you the advantage of time while you’re still sure of the security of your application. You can check out our codebase on GitHub and find the demo site here. You can also find out more from Auth0’s documentation.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

David Atanda I'm a frontend developer who is passionate about technology and startups in Africa. I believe technology has the power to transform Africa into a first-world continent.

Leave a Reply