"Enhance Vue capabilities by dynamically setting permissions upon reload, within the router, and after login, using roles obtained from an asynchronous GET

After spending two days trying to figure it out, I realized that I may be missing something simple.

In my Vue application, I am using casl-vue to control what users can see or do based on their roles.

I want to set the ability in two instances: when the page loads and after a user logs in.

On page load

Everything works fine when I register the abilities synchronously like this:

main.js

import { abilitiesPlugin } from '@casl/vue';
import ability from '../config/ability';
Vue.use(abilitiesPlugin, ability);

ability.js

import { AbilityBuilder, Ability } from '@casl/ability';

export default AbilityBuilder.define((can, cannot) => {
  const role = 'Admin';

  switch (role) {
    case 'Admin':
      can('manage', 'all');
      break;
    case 'Author':
      can('manage', 'all');
      cannot('manage', 'users');
      break;
    case 'Trainer':
      break;
    case 'Participant':
      cannot('manage', 'all');
      break;
    default:
      cannot('manage', 'all');
  };
})

However, when I try to get the role using a function that returns a promise (getCurrentUserRole()) and make the function async like this:

ability.js

export default AbilityBuilder.define(async (can, cannot) => {
  const role = await getCurrentUserRole();

  switch (role) {
    case 'Admin':
      can('manage', 'all');
      break;
    ...
    default:
      cannot('manage', 'all');
  };
})

I encounter the error: "TypeError: ability.on is not a function"

After login

Updating the ability rules after login works using this.$ability.update:

const { role } = await someSignInFunction();
this.$ability.update(defineRulesFor(role));
this.$router.push({ name: 'home' });

Unfortunately, I can't use this function on page load because by the time I call ability.update in App.vue (for example, in beforeCreate() or created()), the page has already loaded, making the update too late.

Vue router

I also want to access the ability can method in my Vue Router instance to control the routes users can visit. But how do I access the ability instance in my router file?

If I haven't explained my problem clearly, please let me know!

Any help with setting up this structure would be greatly appreciated.

Dependencies:

casl/ability: ^3.3.0
casl/vue: ^0.5.1

Answer №1

When you pass an async function inside AbilityBuilder.define, the issue arises as AbilityBuilder.define returns a Promise. It's not possible for it to magically convert async code into synchronous code :)

To address this issue, you should create a separate function called defineRulesFor(role):

// src/config/rules.js

import { AbilityBuilder } from '@casl/ability'

export default function defineRulesFor(role) {
  const { can } = AbilityBuilder.extract()

  switch (role) {
    case 'Admin':
      can('manage', 'all');
      break;
    case 'Author':
      can('manage', 'all');
      cannot('manage', 'users');
      break;
    case 'Trainer':
      break;
    case 'Participant':
      cannot('manage', 'all');
      break;
    default:
      cannot('manage', 'all');
  }
} 

After logging in:

// inside Login.vue
import defineRulesFor from '../config/rules'

export default {
  name: 'LoginPage',
  methods: {
    async login() {
      const response = await axios.post(...)
      const user = response.data
      this.$ability.update(defineRulesFor(user.role))
    }
  }
}

You now have 2 options to handle this:

  1. Store the user role in local storage and restore it on page reload:
// inside App.vue

import defineRulesFor from './config/rules'

export default {
  name: 'App',
  created() {
    this.$ability.update(defineRulesFor(localStorage.getItem('userRole'))
  }
}
  1. Retrieve information about the user again on page reload
// inside App.vue

import defineRulesFor from './config/rules'

export default {
  name: 'App',
  async created() {
    const role = await getCurrentUserRole()
    this.$ability.update(defineRulesFor(role))
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to prevent a form from being submitted and conduct validation using JavaScript?

Encountered a form that is unchangeable in appearance: <form id="mx_locator" name="mx_locator" method="post" action="search-results"> <!-- form elements --> <span><input type="image" src="/images/search.png" onclick="loader()"> ...

Leveraging random attributes in Next.js without encountering the "server/client mismatch" issue

Is there a way to generate unique IDs for form inputs dynamically, in order to avoid conflicts with other elements that share the same ID? If multiple login forms are present on a single page, each with an 'email' field, setting the id property b ...

Switch from using jQuery to vanilla JavaScript for handling POST requests

I am looking to eliminate the jQuery dependency and use plain JavaScript instead in the code below for a post request. <script type="text/javascript> $(function() { $.post("test_post.php", { name: "John Doe", ...

Dealing with Class and Instance Problems in Mocha / Sinon Unit Testing for JavaScript

Currently, I am working on unit testing an express middleware that relies on custom classes I have developed. Middleware.js const MyClass = require('../../lib/MyClass'); const myClassInstance = new MyClass(); function someMiddleware(req, ...

What is the best way to implement client-side shopping cart calculations using jQuery?

https://i.stack.imgur.com/aOajr.png Here is the content of my shopping cart. Below, I will explain the flow. [product image clicked] -> ajax request fetches details from database for that particular product id and appends (product name,id and price ...

Unable to load routes from ./app.js in the file ./src/routes/index.js

Just dipping my toes into the world of nodejs. I recently moved all my routes from app.js to a separate file located at PROJECT_DIR/src/routes/index.js. However, when I try to open the page in my browser, it displays "Cannot GET /wines". Below are snippets ...

Link a function to a button in a 3rd party library

Whenever I click a button, there is a possibility of an alertify alert window appearing randomly. The alertify alert popup serves as a more aesthetically pleasing alternative to the traditional javascript Alert. Alertify library Below is a snapshot depic ...

Analyze two objects and eliminate any duplicate keys present between them

I am currently conducting an experiment on objects where my goal is to remove keys from object1 that are present in object2. Here is an example of what I am trying to achieve: var original = { a: 1, b: 2, c: 3, e: { tester: 0, ...

Learn how to dynamically highlight a table row when any changes are made to it using jQuery

Is there a way to automatically highlight the current table row when any input text or image is changed within that specific row using jQuery? In the given code snippet below, with two rows provided, how can one of the rows be highlighted upon modifying ...

Issue with Jquery animation

I'm facing a strange issue. I've created a jQuery function that animates the result bars of a poll. function displayResults() { $(".q_answers1 div").each(function(){ var percentage = $(this).next().text(); $(this).css({widt ...

What is the best way to set up the login page and logged-in homepage using Node, Express, and Passport with the "/" route?

I am currently in the process of developing an application using Node.js, Express.js, and Passport.js. My goal is to create a seamless user experience on the homepage where if you are not logged in, you will see a login form (with potential for additional ...

Manipulating visibility of an input tag using JQuery

Having a simple input tag: <input id="DAhour" type="number" style="width:50px; font-size: xx-small; visibility:hidden"> Initially, the input tag is set to be hidden. Upon changing a combobox to a specific index, it should become visible. Despite su ...

How will the presence of @types/react impact the higher-level files in my project?

https://i.sstatic.net/TfsLf.png https://i.sstatic.net/RqmMS.png Here is the structure of my directories vue node_modules src react_app node_modules @types/react package.json ...other file package.json Why does the presenc ...

Using Leaflet to beautify categorical json information

As a beginner in coding, I must apologize if a similar question has already been asked. I've spent days searching but haven't found the right combination of terms to find examples for my scenario. I am exploring various small use cases of differ ...

Bring in a function by its name from the ts-nameof package that is not declared in the d.ts export

Recently, I came across a captivating package that caught my interest and I would love to incorporate it into my TypeScript application: https://github.com/dsherret/ts-nameof However, upon attempting to import the nameof function, I realized it was not be ...

Inventive website concept (far from ordinary, and not a plea for coding assistance)

Once again, I want to clarify that I am not asking for someone to code this idea for me. I am seeking advice from experienced web developers on whether or not my concept is achievable, as it involves some complex issues (at least in my opinion). If this po ...

Phase 2 "Loading" visual backdrop

I'm attempting to include a loading animation GIF in my Cycle 2 plugin. Is there a way to ensure that the GIF loads before the images? Right now, I have set my loading.gif as a background image. The issue is that the loading.gif isn't displaying ...

Showing Local Website Content on iOS in React Native: A Step-by-Step Guide

Struggling to Display Local Web Code on iOS with React Native I've attempted various solutions but haven't had success. ...

Encountering difficulties with displaying error messages on the Material UI datepicker

I am currently utilizing React Material UI There is a need to display an error based on certain conditions that will be calculated on the backend. Although I have implemented Material UI date picker, I am facing issues with displaying errors. import * as ...

A guide to exporting a PDF in A4 size landscape mode with jspdf

As a novice in UI development, I am currently trying to export HTML content to PDF using the JSPDF library. However, I have encountered difficulties in generating the PDF in A4 size landscape mode. The HTML code includes data with charts (canvasjs/chartjs) ...