Step 01: npm installing casl packages casl vue and casl abilities
npm install @casl/vue @casl/ability
Step 02: Defining service abilities inside resources/js/services/ability.js to use it inside app.js
import { AbilityBuilder, Ability } from '@casl/ability' const { can, cannot, build } = new AbilityBuilder(Ability); export default build();
Step 03: Now importing ability and abilitiesPlugin from services to app.js
// at the top import { abilitiesPlugin } from '@casl/vue'; import ability from './services/ability'; createApp({ setup() { const { getUser } = useAuth() onMounted(getUser) } }) .use(router) .use(VueSweetalert2) .use(abilitiesPlugin, ability) <= services being used in vue .mount('#app')
Step 04: Now, building the /abilities API route inside routes/api.php
Route::group(['middleware' => 'auth:sanctum'], function() { ...... Route::get('abilities', function(Request $request) { return $request->user()->roles()->with('permissions') ->get() ->pluck('permissions') ->flatten() ->pluck('name') ->unique() ->values() ->toArray(); }); });
Code above creates unique permissions list in array for a particular logged in user.
Step 05: Using this api call creating getAbilities() method from auth.js composable
// importing casl vue packages and injecting ABILITY_TOKEN to ability variable // at the top ...... import { AbilityBuilder, Ability } from '@casl/ability'; import { ABILITY_TOKEN } from '@casl/vue'; export default function useAuth() { ..... const ability = inject(ABILITY_TOKEN) // adding getAbilities() and returning it to component const getAbilities = async() => { axios.get('/api/abilities') .then(response => { const permissions = response.data const { can, rules } = new AbilityBuilder(Ability) can(permissions) ability.update(rules) }) }
Code above, after a successful HTTP GET request, assigning all the permissions to a variable from the response. Then adding all the permissions into a can method and updating the abilities.
Step 06: Calling getAbilities() method from loginUser() method using await and async
// changing loginUser() from below const loginUser = (response) => { user.name = response.data.name user.email = response.data.email localStorage.setItem('loggedIn', JSON.stringify(true)) router.push({ name: 'issues.list' }) } // to below const loginUser = async (response) => { user.name = response.data.name user.email = response.data.email localStorage.setItem('loggedIn', JSON.stringify(true)) await getAbilities() await router.push({ name: 'issues.list' }) }
Step 07: Now with casl/vue wrapper with global instances of permissions set for logged in user and using can() method changing edit/delete links to hide/show inside Index.vue of issues
<script setup> ... import { useAbility } from '@casl/vue' .... const { can } = useAbility() // inside template <td> <router-link v-if="can('issues.update')" :to="{ name: 'issues.edit', params: { id: issue.id } }">Edit</router-link> <a href="#" v-if="can('issues.delete')" @click.prevent="deleteIssue(issue.id)" class="ml-2">Delete</a> </td>
Now, for editor role, the Delete action will not be shown anymore.