In modern web applications, controlling what each user can see and do is crucial for both security and usability. Whether you’re building a SaaS dashboard or an internal tool, role-based and permission-based access control is a must-have feature
In this blog, I’ll walk you through how I implemented permission-based access control in a Vue 3 application using:
- JWT tokens from the backend
- Custom directives for disabling UI
- Route guards for blocking unauthorized navigation
This setup provides robust control without cluttering your components.
The Goal to:
- Decode user permissions from a JWT token
- Enable/disable UI actions based on those permissions
- Show tooltips when features are disabled
- Hide restricted features entirely
- Block unauthorised routes via router guards
Permissions Design
The backend sends a JWT token with a permissions object embedded:
Each permission can be:
- full – full access
- readonly – can view, not modify
- denied – cannot access at all
1. Decoding JWT & Managing Permissions
Store the permissions in a reactive ref:
The getAllPermissions() function should be called during app initialization (e.g., in main.ts or after login).
2. Vue Directive: v-permission
We created a custom Vue directive to apply permission logic declaratively to components.
Usage in Components
Here’s how you’d use it in your template:
If the user lacks permission, the button will be:
- Disabled
- Semi-transparent
- Unclickable
- Showing a tooltip
3. Conditionally Hiding UI with v-if
For fully denied permissions, you can use:
This hides the button completely if access is explicitly denied.
4. Securing Routes with Router Guards
To prevent users from manually navigating to protected routes, we add permission metadata and guard logic.
Sample usage:
Global Navigation Guard
Access Denied Route
ResultsÂ
With this setup:
- Unauthorized users can’t see or interact with restricted UI
- Routes are blocked at the navigation layer
- Users get clear feedback via tooltips or redirection
- Templates stay clean and declarative
Final Thoughts
This approach strikes a great balance between security, usability, and maintainability. You can easily extend it further to support:
- Role-based logic
- Multi-tenant permissions
- Asynchronous permission loading
Action buttons look like:
