This tutorial was written by Kevin Kimani, a software developer and technical writer. Connect with him on his GitHub or X to see more of his work!


Authentication and access control are important parts of any web application that needs to protect access to specific resources and functionalities. However, managing user roles and permissions can be very complex, especially as your application scales and requires you to implement granular permissions. Role-based access control (RBAC) allows you to define specific roles and assign permissions to them. By associating users with roles, you can easily control what they see in the application and the resources they can interact with.

Descope simplifies the process of adding authentication and RBAC to your Vue application through a unique flow-based approach. Descope flows are no-code visual workflows that abstract away the implementation details of authentication methods, session management, and all other aspects related to customer identity management. Whether you are building a small application or a large enterprise application, Descope offers you all the tools you need to implement authentication and authorization, which allows you to focus on the core functionality of your application.

In this article, you’ll learn how to integrate Descope authentication into a basic Vue application along with RBAC.

Prerequisites

To complete this tutorial, you need to have the following:

Here’s a rough architecture diagram of the application you’ll create:

Vue.js tutorial architecture diagram
Fig: Rough architecture diagram

Creating a project on Descope

Start by creating a Descope project.

Navigate to your Descope console, select the current project drop-down, and click + Project to create a new project:

Creating a new project on Descope
Fig: Creating a new project on Descope

On the Create project modal, provide the project name (for example, descope-vuejs-auth-rbac), and click Create:

Providing the new project details
Fig: Providing the new project details

Once the project has been created, you will be redirected to the project’s home page. Click the Get Started button to configure your users’ authentication process in a few steps:

Project home page
Fig: Project home page

On the Who uses your application? screen, select Consumers and click Next:

Who uses your application
Fig: Who uses your application

In the Vue application, you will use email one-time password (OTP) as the authentication method and SMS OTP as the multifactor authentication (MFA). In the Which authentication methods do you want to use? screen, select One-time Password as your primary authentication method and click Next:

Choosing the primary authentication method
Fig: Choosing the primary authentication method

Since you cannot select OTP again, just configure MFA later in the flow builder and, for now, skip the MFA step by clicking Go ahead without MFA:

Skipping the MFA step
Fig: Skipping the MFA step

On the We’re now ready to show you Descope in action! page, select Next to generate all the flows:

Generating all flows
Fig: Generating all flows

In just a few steps, you have configured your users’ authentication process. You can also customize it to fit our project’s needs.

On your Descope console sidebar, select Flows and click the sign-up-or-in flow to open it in the flow builder:

Opening the sign-up-or-in flow in the flow builder
Fig: Opening the sign-up-or-in flow in the flow builder

Currently, this flow allows the user to log in via SMS OTP:

Current flow configuration
Fig: Current flow configuration

You need to customize this to use email OTP as the authentication method and SMS OTP as the MFA. You can configure this in the flow builder, but to make it easier to follow along, this example uses a prepared configured flow with the desired functionality.

Download the raw flow file from GitHub by clicking the Download button:

Downloading the flow raw file
Fig: Downloading the flow raw file

Next, in the flow builder, click the Import flow / Export button, select Import flow, and upload the flow you just downloaded in the previous step:

Importing a flow
Fig: Importing a flow

The flow you just imported presents a user with the Welcome screen where they can fill out their email address. Descope then sends an OTP to the provided email address, and once the user inputs the correct OTP, the flow checks if the user is new or returning. If new, the flow requests the user’s full name and phone number, updates this info in the Descope data store, sends an OTP to verify the user’s phone number, and finally, logs in the user. If the user is returning, an OTP is sent to their registered phone number, and once verified, they are logged in.

Save the flow by clicking the Save button:

Saving the flow
Fig: Saving the flow

Apart from using the default Descope SMS connector, you also can customize your own messaging connector and use it in the Descope console to send messages. You can use the Amazon Web Services Simple Notification Service (AWS SNS), Twilio, or Twilio Verify.

Lastly, select Project from the sidebar and copy your Project ID from the Project page. You will use it in the next section.

Cloning the starter project

To make it easy for you to follow along, this tutorial uses a premade starter template. To clone it to your local machine, execute the following command in your terminal:

git clone --single-branch -b starter-template https://github.com/kimanikevin254/descope-vuejs-auth-rbac-demo.git

Next, cd into the application folder and install the dependencies:

cd descope-vuejs-auth-rbac-demo && npm i

The starter template is a project built on the concept of a job board. Users can view all the posted jobs and publish or unpublish jobs. It uses json-server to mock the backend functionality. There are a few predefined jobs in the json-server/db.json file that are displayed in the Vue application.

To run the application, start by running the json-server by executing the command npm run server, and in a separate terminal, execute the command npm run dev and navigate to http://localhost:5173 in your browser:

Home page
Fig: Home page

When you click View Available Jobs, you’ll be redirected to the Available Jobs page where all the published jobs are displayed:

Available jobs
Fig: Available jobs

Click the Go to Admin Dashboard web page to access the admin dashboard where you can toggle each job’s published status:

Admin dashboard
Fig: Admin dashboard

Currently, anyone can view the available jobs and access the admin dashboard. In the next step, you’ll learn how to limit who can view these.

Adding authentication to the application

You’ll implement authentication and RBAC in the Vue application using Descope so that only authenticated users will be able to view the available jobs and only users with the Admin Dashboard Access permission will be able to access the dashboard.

To do this, you initially have to install the Vue SDK using the following command:

npm i --save @descope/vue-sdk

Next, add the Descope plugin to the application by replacing the code in your src/main.js application with the following:

import "./assets/main.css";

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import descope from "@descope/vue-sdk";

const app = createApp(App);

app.use(descope, {
 projectId: import.meta.env.VITE_DESCOPE_PROJECT_ID,
});

app.use(router);

app.mount("#app");

This code imports the Descope plugin and registers it in your application. This plugin requires the Descope project ID that you obtained earlier. Create a .env file in the project root folder and add the following:

VITE_DESCOPE_PROJECT_ID=<YOUR-DESCOPE-PROJECT-ID>

Remember to replace the placeholder value with your actual project ID.

Next, you need to modify the src/views/LoginView.vue file to allow Descope to handle authentication. Open the file and replace the existing code with the following:

<script setup>
import { Descope, useSession } from "@descope/vue-sdk";
import { ref, watch } from "vue";
import { useRouter } from "vue-router";

const router = useRouter();
const { isLoading, isAuthenticated } = useSession();

// Redirect if isAuthenticated is true
watch(isAuthenticated, (newValue) => {
 console.log("New auth status", newValue);
 if (newValue === true) {
 router.replace({ name: "jobs" });
 }
});
</script>

<template>
 <div class="h-screen max-w-xl mt-4 mx-auto">
 <p v-if="isLoading">Loading...</p>
 <Descope v-if="!isAuthenticated" :flowId="'sign-up-or-in'" />
 </div>
</template>

This code uses the useSession() hook from the Descope SDK to track the user’s authentication status (isAuthenticated) and the session loading state (isLoading). If the user session is loading, the template renders a loading message, and once the session is loaded, it uses the Descope component to render the sign-up-or-in flow you modified earlier if the user is not authenticated. The watch function monitors changes to isAuthenticated, and if the user becomes authenticated, it automatically redirects them to the Jobs view using Vue Router’s replace() method.

Next, you need to use Vue Router navigation guards to protect the routes that only an authenticated user can access. There are several ways to do this, but this guide uses global before guards.

When you open the src/router/index.js file, you’ll see that most route objects have a meta object with a requiresAuth key-value pair attached to them. The requiresAuth key-value pair is used to mark all the routes that require the user to be authenticated.

To add the navigation guard, add the following code just before export default router:

router.beforeEach(async (to, from, next) => {
 const isAuthenticated = await routeGuard();

 if (to.meta.requiresAuth && !isAuthenticated) {
 next({ name: "login" });
 } else if (to.fullPath.includes("login") && isAuthenticated) {
 next({ name: "home" });
 } else {
 next();
 }
});

This navigation guard checks the user’s authentication status before allowing access to certain routes. It uses an async function called routeGuard() (from Descope) to determine if the user is authenticated. If the route requires authentication (indicated by to.meta.requiresAuth) and the user is not authenticated, the guard redirects them to the login page. If the user is already authenticated and tries to access the login page, they are redirected to the home page. If neither condition applies, the navigation proceeds normally.

Remember to add the routeGuard method import statement to the list of existing imports in the same file:

import { routeGuard } from "@descope/vue-sdk";

Lastly, in the navigation bar, you need to show the correct buttons based on the user’s authentication status and allow the user to log out. To do this, open the src/components/NavBar.vue file and replace the existing code with the following:

<script setup>
import { useDescope, useUser } from "@descope/vue-sdk";
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router";

const router = useRouter();
const route = useRoute();

const { user } = useUser();
const { logout } = useDescope();

const login = () => {
 router.push({ name: "login" });
};

const handleLogout = () => {
 logout();
 router.replace({ name: "login" });
};
</script>

<template>
 <nav class="border-b p-4">
 <div class="container mx-auto flex items-center justify-between">
 <RouterLink to="/" class="text-2xl font-bold">
 JobBoard Pro
 </RouterLink>

 <div class="flex items-center space-x-4">
 <button
 v-if="!user && !route?.fullPath?.includes('login')"
 @click="login"
 class="bg-blue-600 text-white px-4 py-2 rounded-md shadow hover:bg-blue-500 transition duration-300 ease-in-out"
 >
 Get Started
 </button>

 <button
 v-if="user"
 @click="handleLogout"
 class="bg-gray-600 text-white px-4 py-2 rounded-md shadow hover:bg-gray-500 transition duration-300 ease-in-out"
 >
 Log Out
 </button>
 </div>
 </div>
 </nav>
</template>

Authentication is now implemented for your Vue application.

Adding RBAC to the application

Currently, every authenticated user can access the dashboard. This is not ideal as every authenticated user shouldn’t have access to your application’s admin functionalities. To fix this, you need to set up RBAC to allow only users with the Admin Dashboard Access permission to access the admin dashboard.

Go back to your Descope console, select Authorization from the sidebar, select the Permissions tab on the Authorization page, and click the + Permission button to create new permission:

Creating new permission
Fig: Creating new permission

In the Add Permission modal, provide “Admin Dashboard Access” as the permission name, “Can access the admin dashboard” as the description, and click Add to save the permission.

Next, you need to create a role and assign this permission to it. Select the Roles tab and click the + Role button to create a new role in the Add Role modal. Provide “admin” as the name of the role, use “Can perform admin functions” as the description, and assign the Admin Dashboard Access permission:

Creating a new role
Fig: Creating a new role

Repeat the same process and create a new role with the name “user” and “Can view published jobs” as the description. Do not assign any permissions to this role.

Your Authorization page should now look like this:

Authorization page
Fig: Authorization page

You now need to protect the dashboard to make sure that only users with the admin role can access it.

Go back to the src/routes/index.js file and see that the route definition for the dashboard page has a requiresAdminDashboardAccessPermission key-value pair in the meta object. You’re using this key-value pair to denote that this route requires the user to have the necessary permissions to access it.

Next, replace the navigation guard you created in the previous step with the following:

router.beforeEach(async (to, from, next) => {
 const isAuthenticated = await routeGuard();
 const permissions = isAuthenticated && getJwtPermissions()

 if (to.meta.requiresAuth && !isAuthenticated) {
     next({ name: "login" });
 } else if (to.fullPath.includes("login") && isAuthenticated) {
     next({ name: "home" });
 } else if (to.meta.requiresAdminDashboardAccessPermission && !permissions.includes("Admin Dashboard Access")) {
     next(from.path);
 } else {
     next();
 }
});

In addition to the functionality you had defined earlier, the updated code retrieves the role permissions of the authenticated user using the getJwtPermissions() method (from the Descope SDK) and adds an if condition that checks if the user trying to access the admin route has the Admin Dashboard Access permission. This ensures that the admin dashboard can be accessed only by authenticated users with a role that has the appropriate permission.

Make sure you update the Descope imports in this file:

import { getJwtPermissions, routeGuard } from "@descope/vue-sdk";

Next, in the src/views/JobsView.vue file, you are displaying a link to the admin dashboard. You need to make sure that this link is displayed only to users with the Admin Dashboard Access permission. Open this file and add this code to the script to import the useUser() hook and access the details of the authenticated user:

import { getJwtPermissions } from "@descope/vue-sdk";

const permissions = getJwtPermissions();

Lastly, replace the Router Link in the template to make sure the link is displayed only to users with the appropriate permission:

<RouterLink
 
 v-if="permissions?.includes('Admin Dashboard Access')"
 to="/admin"

 class="inline-block px-6 py-2 bg-blue-600 text-white font-semibold rounded-md shadow hover:bg-blue-500 transition duration-300 ease-in-out"
>
 Go to Admin Dashboard
</RouterLink>

RBAC is now fully configured in your application. You can go ahead and test the application.

Testing the application

To test the application, ensure that both the Vue Vite server and the json-server are running. If not, run the Vue server using the command npm run dev and the json-server using the command npm run server.

Next, navigate to http://localhost:5173 on your browser and click the View Available Jobs button. Since you’re not authenticated, you will be redirected to the login page:

Login page
Fig: Login page

After you have provided your email address and verified using the OTP sent to your email address, you’ll be requested to provide additional information since you’re a new user:

Additional info
Fig: Additional info

An OTP will be sent to the phone number you provide, and upon successful verification, you will be redirected to the Available Jobs page:

Available jobs
Fig: Available jobs

Since you do not have an admin role, you’ll see that the page does not display the Go to Admin Dashboard button. If you try to input the http://localhost:5173/admin URL manually, you will be redirected back to the dashboard.

To test if a user with the admin role can access the admin dashboard, go to the Descope console, select Users from the sidebar, select the actions button on the user who just signed up to edit their details, and on the user details modal, select Add Tenant / Role. Add the admin role you created previously to the user:

Editing user details
Fig: Editing user details

Go back to the Vue application and refresh the Available Jobs page. Since the user now has an admin role, you will be able to see the Go to Admin Dashboard button:

Available Jobs page as an admin
Fig: Available Jobs page as an admin

When you click the button, you’ll be redirected to the Admin Dashboard page where you can publish and unpublish jobs:

Admin dashboard
Fig: Admin dashboard

This confirms that the authentication and RBAC you added to your Vue application by integrating with Descope is working as expected.

You can access the full project code on GitHub.

Conclusion

As you have seen in this guide, adding authentication and RBAC to the Vue application with Descope is a straightforward process. Descope offers a powerful Vue SDK that makes the integration process very easy for developers of all levels. Using Descope, you can add robust security features with minimal effort, allowing you to focus on the core features of your application.

Sign up for Descope today to meet your application’s security needs.