If you've ever built a non-trivial Vue.js app, you've probably wrestled with access control—deciding who can see or do what, and trying not to scatter permission checks across components, stores, and routes. It starts simple, but as your app grows, so does the complexity. By externalizing authorization logic to a dedicated policy engine like Cerbos, you can keep your Vue codebase clean, maintainable, and scalable, all while enforcing consistent access rules across the app. In this article, we'll show you how to integrate Cerbos into a Vue app and compare it to the traditional DIY approach.
Traditionally, developers implement access control in Vue.js applications using hardcoded roles and permissions. Role-Based Access Control (RBAC) often takes the form of conditionals within components or Vuex store logic. For instance, you might restrict a button's visibility with:
// components/UserManagement.vue
<template>
<div>
<!-- Admin-only user creation button -->
<button
v-if="authStore.userRole === 'admin'"
@click="createUser"
>
Create New User
</button>
<!-- Manager and Admin user edit functionality -->
<user-list>
<template #actions="{ user }">
<button
v-if="['admin', 'manager'].includes(authStore.userRole)"
@click="editUser(user)"
>
Edit User
</button>
<button
v-if="authStore.userRole === 'admin'"
@click="deleteUser(user)"
>
Delete User
</button>
</template>
</user-list>
<!-- Conditional rendering based on permissions -->
<dashboard-settings
v-if="authStore.hasPermission('manage_settings')"
/>
</div>
</template>
While this approach works for small applications, it becomes cumbersome as your app grows in complexity.
When integrating access control into your application, you may encounter several challenges, these are as follows:
Complexity
As your application grows, managing roles and permissions easily become complex due to introduction of new business cases, conditional logic and so forth. For instance, a financial application that requires multiple levels of approval for processing transactions, introducing a new user role would necessitate several adjustments. You must define the new role, account for edge cases associated with it, manage updated permissions, and ensure that your UI components render appropriately based on whether the current user has this newly introduced role.
Maintainability
Say your app now has dozens of components, each containing role-based conditionals. Your company decides to change the permissions—perhaps editors can now delete posts, or reviewers can publish content. You now have to comb through multiple files to update each conditional. This is time-consuming and prone to errors.
Example:
Before: v-if="user.role === 'admin' || user.role === 'editor'"
After: v-if="user.role === 'admin' || user.role === 'editor' || user.role === 'reviewer'"
Failing to update even a single instance could lead to inconsistent user experiences, or worse, unauthorized access due to an overlooked condition.
Security
Hardcoding authorization logic in the frontend means anyone with developer tools can inspect your Vue components and figure out how access control works. Even if you hide UI elements based on roles, an attacker could manipulate requests and attempt unauthorized actions. For instance, a malicious user might notice that the "Delete" button in an application is hidden but discover that the API still processes delete requests if sent manually. Without robust backend verification, this oversight could allow unauthorized users to delete data they should not have access to, posing a significant security risk.
There are a few constraints we face when handling access control in code the traditional way:
Scalability issues
Imagine you're managing access control with simple role checks like v-if="user.role === 'admin'"
. This works fine when you have a handful of roles, but what happens when your app grows? You suddenly need to account for multiple roles, different levels of permissions, and complex rules. Manually updating these across your app leads to a maintenance nightmare.
Auditing challenges
Think about it, if all the access rules are hidden deep inside the code, how do you track who changed what and whether everything is following the right security policies? It's like trying to audit a store's security system when the rules are locked away in a vault instead of being written down somewhere accessible. Every time you need to verify something, you have to dig through the code, which isn't practical for compliance teams.
Limited flexibility
Traditional approaches rely on static role-based conditions, which struggle to accommodate more dynamic access control scenarios. For example, what if you need to allow a user to edit a document only if they created it and it's within a specific time-frame? Implementing such contextual permissions using hardcoded logic results in bloated, hard-to-read code.
No centralized management
If access rules are scattered across multiple files and components, making updates becomes time-consuming and error-prone. For instance, imagine a business policy change requiring managers to approve edits. You now have to manually update multiple conditional checks across your application. A centralized authorization system simplifies this by allowing you to update policies in one place and apply them universally.
With these challenges in mind, let's see how Cerbos offers a better way.
Cerbos is a policy-based, API-driven authorization layer designed to simplify and centralize access control. Instead of embedding authorization logic in your codebase, Cerbos allows you to define policies that govern access decisions, externalizing and decoupling access control from your application. With Cerbos you have a single source of truth where you manage everything as regarding authorization for your server side and client based applications.
Cerbos is particularly useful for:
The Policy Decision Point (PDP) is the core element of Cerbos, responsible for externalizing and evaluating authorization logic. Here are some of the highlights which pertain to our use-case.
The PDP evaluates access requests based on policies, keeping your Vue.js code clean. For example, instead of embedding this logic:
if (user.role === 'admin' && resource.owner === user.id) {
// Allow access
}
You can externalize it into a Cerbos policy…
rules:
- actions: ["view"]
condition:
expr: request.resource.attributes.owner == request.principal.id
… which keeps the logic centralized and consistent.
You can use REST or gRPC APIs to query the PDP. With REST, you send HTTP POST requests to Cerbos' endpoints, providing authorization data such as the principal (user) and resource details. This approach works with libraries like Axios or the browser's Fetch API in Vue.js.
Example REST API usage:
const response = await axios.post('http://localhost:3592/api/check', {
principal: { id: user.id, roles: user.roles },
resource: { kind: resource.kind, id: resource.id },
actions: [action]
});
return response.data.results[action].allow;
Leverage JWT for secure user identity validation. Cerbos can parse JWTs to extract user claims, such as roles and permissions, which are then evaluated against policies. This ensures authentication and authorization logic are integrated without requiring additional effort to decode tokens in your application.
Finally, Cerbos allows you to update policies dynamically without redeploying your application. For instance, if business rules change and you need to introduce a new policy for a specific resource, you simply update the YAML file or API endpoint where policies are defined. This decoupling of policy updates from the application lifecycle ensures faster, less error-prone iterations because you only need to update the policy unlike handling authorization traditionally where you have to dig into your code to make new updates.
There are several ways you can integrate Cerbos into your application but the common approach is to have a basic Policy Decision Point (PDP) that runs locally on your device (as a binary or via Docker) and also ensure you can make requests to check access in your application. For this article we will be using an approach that allows you to manage your Cerbos policies, labels, PDP deployments, and so forth.
In addition, you will be using a Cerbos embedded library that allows you to embed an optimized policy decision build in your application.
Set up a Cerbos account Your Cerbos account can be quickly set up visiting the Cerbos Hub.
Create a workspace Next, you want to create a workspace that allows you to connect your Vue.js application to cerbos and connect the repository in which your application authorization policy is located.
To fully connect your workspace to your repository you need to create a .cerbos-hub.yaml
file at the root directory of your application, this file defines the labels for git references that cerbos detects and uses to compile and test your application policy. Then paste this code in your .cerbos-hub.yaml
apiVersion: api.cerbos.cloud/v1
labels:
latest:
branch: main
Deploy a decision point
In addition, you want to deploy a decision point for fast and effecient decision checks, for this we would be using the docker container and providing all the necessary variables needed in the command, including CERBOS_HUB_BUNDLE
,CERBOS_HUB_WORKSPACE_SECRET
,CERBOS_HUB_CLIENT_ID
,CERBOS_HUB_CLIENT_SECRET
docker run --rm --name cerbos \
-p 3592:3592 -p 3593:3593 \
-e CERBOS_HUB_BUNDLE="latest" \
-e CERBOS_HUB_WORKSPACE_SECRET="..." \
-e CERBOS_HUB_CLIENT_ID="..." \
-e CERBOS_HUB_CLIENT_SECRET="..." \
-v /tmp:/audit_logs \
ghcr.io/cerbos/cerbos:0.40.0 server --set=audit.enabled=true --set=audit.backend=hub --set=audit.hub.storagePath=/audit_logs
This setup ensures you're running the Cerbos authorization service locally and ready for integration with your application.
Add dependencies Install Cerbos embedded library.
npm install @cerbos/embedded
Configuring Cerbos policies:
Create a folder named policies
and then create your application policy file, policy.yaml
, to define your access rules. For example, here is a basic policy that allows an admin to edit and view a document:
# //policies/policy.yaml
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: "default"
resource: "document"
rules:
- actions: ["edit"]
effect: EFFECT_ALLOW
roles: ["admin"]
- actions: ["view"]
effect: EFFECT_ALLOW
roles: ["admin"]
Integrating Cerbos for permission checks
For this we create a folder called services
and in there we create a cerbos.ts
file and write out the logic for connecting to the Cerbos PDP and checking for permissions dynamically.
import {AutoUpdatingLoader, Embedded as Cerbos} from "@cerbos/embedded";
const checkAccess = async (user, resource): Promise<boolean> => {
const cerbos = new Cerbos(
new AutoUpdatingLoader(“https://lite.cerbos.cloud/bundle?workspace=...&label=...“)
);
const checkAccess = async (user, resource): Promise<boolean> => {
try {
const decision = await cerbos.checkResource({
principal: {
roles: user.roles,
id:user.id
},
resource: { kind: resource.kind, id: resource.id},
actions: [‘edit'],
});
return decision?.isAllowed(‘edit');
} catch (e) {
console.error(e);
return false
}
};
export {checkAccess};
Note: Your correct workspace ID and label can be found in your Cerbos Hub dashboard.
Render component based on authorization policy Now we would be rendering our application component based on the policy we've provided. In your Vue component:
//src/app.vue
<template>
<button v-if="canEdit">Edit Document</button>
</template>
<script>
import { ref, onMounted } from 'vue';
import { checkAccess } from './cerbos';
export default {
setup() {
const canEdit = ref(false);
onMounted(async () => {
//sample admin user
const user = { id: '123', roles: ['admin'] };
//sample resource
const resource = { kind: 'document', id: 'doc1' };
canEdit.value = await checkAccess(user, resource, 'edit');
});
return { canEdit };
}
};
</script>
// cerbos check access response
{
"resource": {
"id": "doc1",
"kind": "document",
"policyVersion": "",
"scope": ""
},
"actions": {
"edit": "EFFECT_ALLOW"
},
"validationErrors": [],
"metadata": {
"actions": {
"edit": {
"matchedPolicy": "resource.document.vdefault",
"matchedScope": ""
}
},
"effectiveDerivedRoles": []
},
"outputs": []
}
For dynamic access control, you can advance your policy by adding attribute-based conditions. For instance, adding a condition that allows admin's to only edit document from 8pm onwards. To achieve this we update our policy as follows:
# conditional to enable admin editing only after 20:00
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: "default"
resource: "document"
rules:
- actions:
- edit
effect: EFFECT_ALLOW
roles:
- admin
condition:
match:
expr: request.principal.attr.requestTime >= 17
- actions:
- view
effect: EFFECT_ALLOW
roles:
- admin
And then, test the scenerio by adding the request time to the user's attributes.
import {AutoUpdatingLoader, Embedded as Cerbos} from "@cerbos/embedded";
const checkAccess = async (user, resource, action) => {
const d = new Date();
let hour = d.getHours();
try {
const cerbos = new Cerbos(
new AutoUpdatingLoader("https://lite.cerbos.cloud/bundle?workspace=PZR5W6R2IK58&label=f481a2c9c90ee3ae4deae7b7f656d65d1cd608828f5853d21e9ca383d479223a")
);
const decision = await cerbos.isAllowed({
principal: {
id: user.id,
roles: user.roles,
attr:{
requestTime: hour, // Include user's current time in hours
}
},
action,
resource: { kind: resource.kind, id: resource.id}
});
return decision;
}catch (e) {
console.error(e);
}
};
export {checkAccess};
This only allows the admin to edit the document from 8pm onwards. With this policy update, the logic on your frontend application and your backend application are in sync with this change and can validate the user's action accordingly without you changing multiple files.
For debugging your application, use the Cerbos CLI or the Hub Playground to test and validate policies before deployment, to ensure they meet your application's requirements.
Recommendation: Explore the Cerbos Playground to experience policy-based authorization firsthand. If you want to dive deeper into implementing and managing authorization, join one of our engineering demos or check out our in-depth documentation.
Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team
Join thousands of developers | Features and updates | 1x per month | No spam, just goodies.