Skip to content

Instantly share code, notes, and snippets.

@mtvbrianking
Last active July 17, 2025 17:02
Show Gist options
  • Save mtvbrianking/c861ceb40d41a18119978dfe0c98d8cd to your computer and use it in GitHub Desktop.
Save mtvbrianking/c861ceb40d41a18119978dfe0c98d8cd to your computer and use it in GitHub Desktop.
CASL load user permissions from API on login
import { Ability, subject } from "@casl/ability";
const editorRole = { id: 1, name: 'Editor'};
const adminRole = { id: 2, name: 'Admin' };
const user = { id: 1, alias: "jdoe", name: "John Doe", role_id: 1 };
const ability = new Ability();
const rules = [
{
action: "view",
subject: "roles",
conditions: {
id: user.role_id
}
}
];
ability.update(rules);
console.log("can('view roles')", ability.can("view", "roles"));
console.log(
"can('view editorRole')",
ability.can("view", subject("roles", editorRole))
);
console.log(
"can('view adminRole')",
ability.can("view", subject("roles", adminRole))
);
import { Ability, subject } from "@casl/ability";
const user = { id: 1, alias: "jdoe", name: "John Doe" };
const ownPost = { user_id: user.id };
const otherPost = { user_id: 2 };
const ability = new Ability();
// fetched from API
const rules = [
{
action: "view-any",
subject: "posts"
},
{
action: "view",
subject: "posts"
},
{
action: "create",
subject: "posts"
},
{
action: "update",
subject: "posts",
conditions: {
user_id: user.id
}
},
{
action: "delete",
subject: "posts",
conditions: {
user_id: user.id
}
}
];
// update ability with backend permissions
ability.update(rules);
// console.log("can('view-any posts')", ability.can("view-any", "posts"));
// console.log("can('view posts')", ability.can("view", "posts"));
// console.log("can('create posts')", ability.can("create", "posts"));
// console.log("can('update posts')", ability.can("update", "posts"));
console.log("can('delete posts')", ability.can("delete", "posts"));
console.log(
"can('delete ownPost')",
ability.can("delete", subject("posts", ownPost))
);
console.log(
"can('delete otherPost')",
ability.can("delete", subject("posts", otherPost))
);
@mtvbrianking
Copy link
Author

mtvbrianking commented Nov 14, 2021

Limitations

CASL can't work with Laravel Policies i.e,
you can't pass policies conditions from the API to CASL as they are logical statements, not stored in the permissions database.

The API document must mention the available conditions - so that the frontend can hard code them

<div if="$can('view roles')"></div> --- OK
<div if="$can('view roles', subject('role', role))"></div> --- Useless for frontend permissions
<div if="role.id == user.role_id || $can('view roles')"></div> --- Good; if the condition is known before hand

CodeSanbox - https://codesandbox.io/s/casl-permissions-from-backend-g6tob

@mtvbrianking
Copy link
Author

Vue Gates

A simpler implementation with Laravel Spatie permission example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment