CASL. Permission management in express

Sergii Stotskyi
Jul 25, 2017 · 5 min read
CASL Expressjs API

In this article I will show how to integrate CASL in an expressjs app.
But let’s start from clarifying what is what:

  • Authorization is the process of giving someone permission to do or have something.
  • CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access.
  • Express is a fast, unopinionated, minimalist web framework for Node.js.

If this is first time you’re learning about CASL, please read “What is CASL?”.

Create blog application

I’ve prepared an example of blog application integrated with CASL on github. The app consists of 3 entities (User, Post and Comment) and 4 modules (one module per entity and one module for authentication and authorization logic). All modules can be found in folder. Application uses mongoose models, passport authentication and implements basic REST API interface which allows to:

  • read Posts and Comments for anybody
  • create new Users for anybody
  • manage own Posts for logged in users
  • update and read personal information for logged in users
  • create, update, delete own Comments for logged in users

In order to install this application, just clone it from github, run and . Also you need MongoDB to be up and running, app will connect to . After everything is ready, we can start to play: try to get or create /posts, see errors and results. In order to make this stuff a bit more interesting you can import basic data in your database from folder:

mongorestore ./db

Alternatively you can follow login instructions in project README or use my Postman collection to setup everything. Now you can log in with credentials by sending request. Then put returned in header and now you can write posts and leave comments :)

CASL authorization

I merged authentication and authorization logic into one piece, so that I can define permissions for both logged in and anonymous users. In case if user doesn’t specify token in header, I use lazy generated and empty instance.

Lets check file which exports express middleware and defines abilities for logged in and anonymous users:

Take a look at function which is responsible for ability definition. I created instance using its method what allows me to write the whole function in DSL style. If is passed in, then he will be able to manage own Comments and Posts, otherwise more general rules are applied (and later used as anonymous user’s ability).

Now lets try to update own Post (I have logged in under casl@medium.com):

PATCH http://localhost:3030/posts/597649a88679237e6f411ae6
{
"post": {
"title": "[UPDATED] my post title"
}
}
200 Ok
{
"post": {
"_id": "597649a88679237e6f411ae6",
"updatedAt": "2017-07-24T19:53:09.693Z",
"createdAt": "2017-07-24T19:25:28.766Z",
"title": "[UPDATED] my post title",
"text": "very long and interesting text",
"author": "597648b99d24c87e51aecec3",
"__v": 0
}
}

All good but what will it happen if I try to update somebody else post? :)

PATCH http://localhost:3030/posts/59761ba80203fb638e9bd85c
{
"post": {
"title": "[EVIL ACTION] my post title"
}
}
403 Ok
{
"status": "forbidden",
"message": "Cannot execute \"update\" on \"Post\""
}

Looks like it’s impossible… Cool!

Now lets imagine that we build an admin interface for our writers and on some page, we need to show them only posts which they are allowed to edit. I will try to change abilities to this:

This way all authenticated users will see only what they created and anonymous users will see everything. It seems wrong but lets just try it:

GET http://localhost:3030/posts200 Ok
{
"posts": [
{
"_id": "597649a88679237e6f411ae6",
"updatedAt": "2017-07-24T19:53:09.693Z",
"createdAt": "2017-07-24T19:25:28.766Z",
"title": "[UPDATED] my post title",
"text": "very long and interesting text",
"author": "597648b99d24c87e51aecec3",
"__v": 0
}
]
}

I changed only few lines of code in permission logic and all parts of application reacted to this without additional changes. This is possible thanks to method which CASL provides as part of mongoose plugin. See Database Integration for details.

You can play and try different combinations of abilities, for example, try to give access to create comments for anonymous users. Check Defining Abilities in the official documentation for more details.

Ok, now lets revert everything to how it was and add additional parameter to /posts route. To do this open file, find in method and change it to

Post.accessibleBy(req.ability, req.query.action)

Restart the server (if you don’t use dev mode) and now lets try to send GET /posts?action=update, we should see only posts which are allowed to be udpated:

GET http://localhost:3030/posts?action=update200 Ok
{
"posts": [
{
"_id": "597649a88679237e6f411ae6",
"updatedAt": "2017-07-24T19:53:09.693Z",
"createdAt": "2017-07-24T19:25:28.766Z",
"title": "[UPDATED] my post title",
"text": "very long and interesting text",
"author": "597648b99d24c87e51aecec3",
"__v": 0
}
]
}

And as before returns all posts which can be read by currently logged in user. Exactly what we have been trying to achieve with first change!

Conclusion

I hope it was an interesting journey and now you like CASL as much as I do :). CASL has pretty good documentation, so I believe you will find a lot of useful information there but don’t hesitate to ask questions in gitter chat if there are any.

Looking for more?

Read documentation and other articles about CASL:

If you like that article, please consider to recommend it. 👏

DailyJS

JavaScript news and opinion.

Sergii Stotskyi

Written by

DailyJS

DailyJS

JavaScript news and opinion.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade