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:
An understanding of Descope flows and how to use the flow builder
Node.js v18.3 or higher and npm installed on your local machine
A code editor and a web browser
Here’s a rough architecture diagram of the application you’ll create:
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:
On the Create project modal, provide the project name (for example, descope-vuejs-auth-rbac
), and click Create:
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:
On the Who uses your application? screen, select Consumers and click Next:
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:
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:
On the We’re now ready to show you Descope in action! page, select Next to generate all the 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:
Currently, this flow allows the user to log in via SMS OTP:
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:
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:
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:
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:
When you click View Available Jobs, you’ll be redirected to the Available Jobs page where all the published jobs are displayed:
Click the Go to Admin Dashboard web page to access the admin dashboard where you can toggle each job’s published status:
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:
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:
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:
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:
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:
An OTP will be sent to the phone number you provide, and upon successful verification, you will be redirected to the Available Jobs page:
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:
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:
When you click the button, you’ll be redirected to the Admin Dashboard page where you can publish and unpublish jobs:
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.