CASL and Cancan. Permissions sharing between UI and API.
With growing amount of devices, applications were split into 2 separate services: front-end (UI, what user sees and interacts with) and back-end (API, implies business rules). These 2 pieces may be written in the same language (e.g., JavaScript) or in different (e.g., JavaScript on UI and Ruby on API). That approach allows to write business rules ones and implement separate User Interfaces for each device type (e.g., Web, iOS, Android).
In authenticated apps, we often want to change what users can do depending on their role. For example, a guest user might be able to see a post, but only a registered user or an admin sees a button to delete that post.
Managing these permissions may become a nightmare for applications which are split into UI and API. Moreover if there’re 2 different teams that implement these parts, they very likely will end up creating logic which handles permissions on UI and API separately. So, changes in back-end permission logic will require changes on front-end and vise-verse.
This increases delivery time and makes applications to be prune to bugs. So, what is the solution for this?
Share Permissions
Whenever possible, define permissions on server side and share them with client side. This approach works very nice if you use Node.js because you can use CASL on both sides and easily transfer rules using JWT token as an example.
Also it works seamlessly with Ruby’s cancan gem (unsupported, community created a fork called cancancan). Actually this gem was a big motivator for me to create CASL :)
First time you’ve heard about CASL? You may want to read “What is CASL?”.
Update: CASL 4.0 is released, see “CASL 4.0. What’s inside?”
Integration Example
I created an example which shows how to integrate Rails 5 based API with Vue based UI. This is a simple blog application which allows to manage articles, login into app and logout.
For those, who are interesting in the end result, check Vue app and Rails API repos
REST API
There is not much to explain in server app. This is just a basic Rails application which renders JSON in response to requests. All authorization logic was done using load_and_authorize_resource
controller helper function.
Session management is stateless, based on JWT token. That token contains only user_id
field. To login, client needs to send POST request to /api/session
with email
and password
. In case of successful response, server returns JWT token and a list of rules compatible with CASL format.
This is the response for user with role “member”:
This response tells that user can read everything (i.e., read all
), can manage Article
where author_id
equals 2
and can read and update User
with id = 2
. You probably already understood that 2
is an id of logged in user. So, in simpler words he can manage own articles, update own profile and read everything else.
Cancan stores rules differently from CASL, that’s why I added to_list
method into Ability
which creates an array of rules that CASL can consume.
Vue app
CASL is shipped together with complementary package for Vue. This package adds $can
method into all Vue components and that makes possible to check permissions easily in templates. For example:
This app uses Vuex to manage local state. All requests to REST API a made through actions in Vuex.Store
. Also there are few plugins and modules which you can find in src/store
folder but the most interesting for us is src/store/ability.js
.
That plugin fills Ability
instance with rules when user login and clears it on logout.
In the code above, I create and export an empty Ability
instance (this instance will be used later as well). Then I create a Vuex plugin which subscribes to store changes. When createSession
(i.e., user login) action is committed, ability is updated with provided array of rules and on destroySession
(i.e., user logout), ability is reset to read-only mode.
Later this plugin is connected into store via plugins
property and that ability is re-exported and passed into Vue’s abilitiesPlugin
.
And that’s it!
Now client side and server side permissions system are connected and when you change rules on API, you will not need to change code on UI.
Looking for more?
Read documentation and other articles about CASL:
- Official Documentation
- Vue ACL with CASL
- Managing user permissions in React app
- Permissions in Aurelia app
- Authorization in Expressjs app
- Easy API Authorization with CASL and Feathers
If you like CASL, ⭐️ Star it on GitHub and share this article with your friends.