avatarJérôme Morlon

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

7024

Abstract

} }] <span class="hljs-string">//</span> <span class="hljs-string">...</span> } } <span class="hljs-string">//</span> <span class="hljs-string">...</span> }</pre></div><p id="3403">Now what if some resource depends on whether the user has access or not to a linked resource ? For exemple let’s say we have a <i>player</i> resource and a <i>game</i> resource. Then we’ll simply add this in our <i>aclRules</i> configuration :</p><div id="0a06"><pre>defaultParams: { ... }, roles: { ... }, dependencies: {player: { on: ‘game’ } }</pre></div><p id="2580">That’s all (provided you only want one-level dependencies, but I can hardly think of use cases where you’d want more to be honest) !</p><p id="4797">So the goal is to rely entirely on such a descriptive configuration file for our whole access control system. <b>How can we do this</b> ?</p><p id="0d91">My approach was to create a domain level entity called AccessControlList which models everything we need. It could go like this :</p><p id="cf00"><i>src/domain/accessControlList.js</i></p><figure id="7261"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*2pD8P825m3hg2nzWaCvW8Q.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-2-js">See Gist</a></figcaption></figure><p id="cb2e">While not trivial, the code is again pretty straightforward when you think about it.</p><p id="9ef2">In our <i>can()</i> method, We are denying access if a role/capability combination does not exist, but we are allowing access if the resource asked match all <i>when</i> conditions (if present) or, based on our entity <i>params </i>property, if any matching exception is found. The whole (very simple) matching algorithm is found is the <i>isIn()</i> method.</p><p id="733e">Finally if there’s no match, we try inherited roles (through <b>recursion</b>), if there’s any inheritance.</p><h2 id="4c24">Protecting a resource</h2><p id="c0e4">So everytime a resource is created, updated or deleted, we will create and persists a new <i>AccessControlList</i> instance. This will occur in our <i>protect()</i> middleware :</p><p id="9a12"><i>src/interfaces/http/auth/accessControl.js</i></p><figure id="b853"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*McYNWI6utg72fxXaS0njcQ.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-3-js">See Gist</a></figcaption></figure><p id="efcf">We will protect a resource when dealing with a POST request. The resource posted would then be in the <i>data</i> field of our <i>ctx.body</i> object. We will then get our <i>resourceReference</i> from <i>data.toString()</i> as our resource is a domain entity with such a method (see <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-2-of-a-5-part-series-8e9d41d1824"><b>Part 2</b></a>).</p><p id="5b75">Now remember that in <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-2-of-a-5-part-series-8e9d41d1824"><b>Part 2</b></a> also, we fetched our current user and placed it in <i>ctx.state.user</i>. This is from where we will get our <i>userReference</i>. We are being careful though to distinguish user creation route (injected as a configuration parameter here), where we have no <i>ctx.state.user</i> as this is a public route.</p><p id="4679">We are also checking if some parameters allowed by our <i>AccessControlList</i> rules are actually present in the data object, and we skip protection for a dependant resource (as we need only to protect the parent).</p><p id="accc">Of course you could break down the above middleware into smaller functions because, I admit, as such it violates the single responsibility principle. But again I didn’t wanted things to be overly complicated.</p><p id="c968">Notice we are using a helper functions here : <i>resolveOperation()</i>, as well as 4 container services : <i>createAcl</i>, <i>getAcl</i>, <i>removeAcl</i> and<i> updateAcl</i>. It’s of course your responsibility to implement such services following the directions provided in <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-1-of-a-5-part-series-3f29a1c414bb"><b>Part 1</b></a>.</p><p id="6014">Let’s see our <i>resolveOperation()</i> code :</p><p id="cfbb"><i>src/interfaces/http/auth/accessControl.js</i></p><figure id="c05a"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*8PKawTqRCKB-OEvhf5SsqA.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-4-js">See Gist</a></figcaption></figure><p id="62c0">The code is pretty easy to understand with some examples. The following routes will thus give :</p><p id="dfd3"><b><i>PATCH</i></b><i> <b>/user/001/update</b></i> : <i>place</i> as resource type, <i>001</i> as resource reference, <i>update</i> as operation</p><p id="4a1f"><b><i>POST</i></b><i> <b>/place/create</b> </i>: <i>place</i> as resource type, <i>create</i> as operation</p><p id="036e"><b><i>GET or DELETE /place/001</i></b><i> </i>:<i> place</i> as resource type, <i>001</i> as resource reference, <i>read</i> as operation</p><p id="cd1b">This requires some discipline when creating routes (see <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-2-of-a-5-part-series-8e9d41d1824"><b>Part 2</b></a><b> </b>for tips) but you’ll need that anyway to have a robust API.</p><h2 id="9dc2">Checking access control</h2><p id="17b5">When on the other hand the resource is queried, the interface is then responsible to check the appropriate <i>AccessControlList</i> instance (if it exists) as well as for its potential dependencies’s own <i>AccessControlList</i> instances. And if no such instance exists, the interface will allow access based on the generic rules, given an operation and a resource type. This will occurs in our <i>accessControl.check</i> middleware :</p><p id="8400"><i>src/interfaces/http/auth/accessControl.js</i></p><figure id="e8ba"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*f4vuRSaiQKZSV_VJ7BXQ_g.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-5-js">See Gist</a></figcaption></figure><p id="db91">We are using two new helper functions here : <i>getParentAcl() </i>and <i>processQueryParametersForAcl()</i>. Let’s start with the second :</p><p id="01ba"><i>src/interfaces/http/auth/accessControl.js</i></p><figure id="944f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*-L2Q1SIqMY7axR7npPqEyQ.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-6-js">See Gist</a></figcaption></figure><p id="56bd">(Our <i>isOwner</i> parameter is hard-coded here — being so universal — but you can choose to make it configurable of course.)</p><p id="20d6">Now our <i>getParentAcl()</i> method is responsible for checkin

Options

g if a resource is dependant of another :</p><p id="bddf"><i>src/interfaces/http/auth/accessControl.js</i></p><figure id="1e52"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*rW5vrN-Ou8aVyH8hXwwefw.png"><figcaption></figcaption></figure><p id="0f05">The code should be self-explanatory (again, I highly encourage you to write readable code with meaningful variables and function names — and I hope you’ll be convinced it pays off when reading the above code — and using comments only when needed but wisely).</p><p id="0b8d">However, notice that the above code expects (otherwise it would break) your repository services to be name with the following pattern :</p><div id="322f"><pre><span class="hljs-string">[resource]</span>Repository</pre></div><p id="a086">(userRepository for instance.)</p><p id="ca8b">You can imagine a more flexible system if you want, but such conventions are good practices anyway so it may not be worth it.</p><p id="37d0">Also notice that we’re making use here of our base repository <i>reload()</i> method (see <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-2-of-a-5-part-series-8e9d41d1824"><b>Part 2</b></a>), silently failing if there’s an error at this level (it will just deny permission at the end) — we’ll see below how to handle errors that need to be handled.</p><p id="47b6">This makes another expectation : that either one of the POST body parameters is the (properly named) parent resource (again, this seems natural and good practice, so I suggest you adhere with this kind of simple, efficient and coherent formalism) or that we are able to get the parent resource from a (properly named) corresponding property — this also seems very logical but requires some discipline : this is the (small) price to pay for a very easy configurable access control system.</p><p id="868e">So we’re at where we wanted to be : we now have a full blown access control mechanism. And<b> if additional control is needed based on more complex logic</b>, it will typically occur at application level.</p><h1 id="2327">How to handle errors properly ?</h1><p id="ce9c">You should catch any error occurring in your code and log most of them. But you only have to present certain errors to the user :</p><ul><li><b>Errors in your own code</b> should crash your application,</li><li><b>Errors due to a bad request</b> should inform the user that his request was… bad (and for what reasons),</li><li><b>Errors due to other services</b> (database connection, cache connection, external APIs) are likely to be hidden as such to the user (but logged precisely), yet they must trigger some error in your application and you should present that error to the user.</li></ul><p id="2ecc">Basically, that’s the gist of it.</p><p id="9a83">For a detailed (and great) explanation on error handling in Node.js, <a href="https://www.joyent.com/node-js/production/design/errors">here’s a mandatory read</a>.</p><p id="35c0">Now how to do that in our different layers ? And when exactly do we throw, catch, return and log ? Answers below.</p><p id="acb2"><b>In our domain layer, we won’t throw or catch or return any error</b>. We certainly could, but it’s not necessary.</p><p id="298a"><b>In our application layer, we will throw custom errors everytime they’re needed</b>. Here’s a very basic example :</p><p id="ecc2"><i>src/app/game/player/create.js</i></p><figure id="0f92"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*qMJWiA_vp8Se_xD6x9nkew.png"><figcaption></figcaption></figure><p id="730d">Here we are creating a player as defined in <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-2-of-a-5-part-series-8e9d41d1824"><b>Part 2</b></a><b> </b>(from a given user and a given game). In our domain Game entity, we’ll have implemented a <i>hasPlayer()</i> method which checks is a user is already a player. We are using it in our app level code to throw a custom error if that’s the case.</p><p id="b2fe"><b>In our infrastructure layer, we will catch and log then re-throw some errors</b>. For instance in our <i>MongooseRepository</i> we could do :</p><p id="1449"><i>src/infra/database/repositories/mongooseRepository.js</i></p><figure id="e7e1"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*llQXQYO_CMt742qMt7kbzA.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-9-js">See Gist</a></figcaption></figure><p id="9a0d">Just beware or <i>try/catch</i> hell when you do that ! You could use a decorator (as the ones we’ll see in <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-5-of-a-5-part-series-8315c291b875"><b>Part 5</b></a>) as a solution though.</p><p id="4a26"><b>Finally, in the presentation layer, we’ll catch errors in a centralized way and present them to the user</b>. In the HTTP interface, This will be the responsibility of our (very simple) <i>errorHandler</i> Koa middleware :</p><p id="5a9f"><i>src/interfaces/http/errors/errorHandler.js</i></p><figure id="e98a"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*FcMB1K4bsMqQ9OFltPV_7Q.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-10-js">See Gist</a></figcaption></figure><p id="b031">We have a nice all-encompassing <i>try/catch</i> block at the top level. This feels satisfying, right ?</p><p id="210b">In production, we display the error message, in test and development mode, we display the error stack trace.</p><p id="a36c">The SERVER_ERROR event allow us to log the error in our <i>server.js</i> code like this :</p><p id="0f43"><i>src/interfaces/http/server.js</i></p><figure id="f13f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*SQKbeb4UBmsxgR-njfJp_Q.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-11-js">See Gist</a></figcaption></figure><p id="bbc9">In our HTTP interface, to be thorough, we’ll also need a <i>notFoundHandler</i> middleware which is, given all what we have set up so far, even more simple :</p><p id="8127"><i>src/interfaces/http/errors/notFoundHandler.js</i></p><figure id="49f8"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*_8VngpYToN7KY1qil2DWBA.png"><figcaption><a href="https://gist.github.com/iperiago/336dfd304dbbf68a09fbf4b9ade60a33#file-part3-12-js">See Gist</a></figcaption></figure><p id="8d68">And now we have our error handling system complete. Such, we can enhance it various ways (a custom <i>Error</i> object, domain errors, etc.). It’s up to you now.</p><p id="1ef5">In <a href="https://readmedium.com/node-js-app-in-the-real-world-what-they-never-really-tell-you-part-4-of-a-5-part-series-10fb131fd08d"><b>Part 4</b></a>, we’ll handle documentation issues, testing and how to interface our app services with CLI commands. See you later guys !</p></article></body>

Node.js app in the real world : authentication, access control, error handling

This is Part 3 of a 5-part series.

Part 0 was the introduction, Part 1 was about structuring your application and coding style, Part 2 was about persisting our domain, setting up the HTTP interface, notably a server, managing routes and controllers. This third part will tackle authentication, access control, and handling errors properly for our REST API written in JavaScript under Node.js with a Koa server, a Mongodb database and the Mongoose ODM.

Let’s handle identity and tackle errors ! Photo by Thư Anh on Unsplash

How to handle authentication ?

Here is the code from our authentication (Koa)middleware :

src/interfaces/http/auth/authenticate.js

See Gist

A lot is actually going on there, but each step is pretty simple :

  • First we are looking at some configuration parameter to see if the requested route is public, in which case we move on without authentication ;
  • Second we are getting back a token either from a secure (httpOnly) cookie sent with the Request or from a Authorization Bearer header : if none is found then we send a 401 error ;
  • Third we decode the token (with some service of your choice implementing a decode() method — incidentally, it will be same service that we’ll use to encode password when creating a new user : remember we skipped this in Part 1, but of course we must do that), get the corresponding user (or send a 401 error if a wrong token has been provided),
  • And finally check if that user has the right permissions for the given route (see below), sending a 403 error if not.

Authentication can of course be managed with a full blown library like Passport.js, but it’s perfectly fine to write your own middleware, don’t forget a few things like (obviously) hashing passwords correctly, a password resetting process, a lot of tests, possibly fake user authentication and so on. This really extends the scope of this series but you’ll find lots of resources on the Web about all this.

Now the most complex part really is the access control, and that part is actually twofold : we need a middleware to check access to a resource, and another to protect a resource.

How to handle access control ?

But we first we need to decide what type of access control we want. I recommand reading this excellent resource to better understand access control. Here I’ve chosen to go for a hierarchy ressource-based one (or HRBAC model). Meaning the configuration file looks like this :

config/app.js

publicRoutes: [
  '/user/create',
  '/user/token/create',
  '/user/exist/email',
  '/user/exist/username',
  '/user/password/reset',
  '/user/logout'
],
aclRules: {
  roles: {
    USER: {
      can: {
        read: [{ resource: 'user' }],
        update: [{ resource: 'user', when: { isOwner: true } }],
        remove: [{ resource: 'user', when: { isOwner: true } }]
      }
    },
    ADMIN: {
      inherits: ['USER'],
      can: { update: [{ resource: 'user' }] }
    },
    SUPER_ADMIN: {
      inherits: ['ADMIN'],
      can: { remove: [{ resource: 'user' }] }
    }
  }
}

There are basically 4 types of operations described above :

  • Creating a user which is public,
  • Getting user information (reading) which requires the role USER,
  • Updating a user which requires the role ADMIN unless we’re updating our own record,
  • Deleting (removing) a user which requires the role SUPER_ADMIN unless we’re updating our own record

Each role inherits the permissions of the role “beneath” it, which greatly simplify the configuration file.

The above code only cares about our user module, but we can extend the configuration file to other resources and add extra conditions (such as, for instance : is the resource published or not ? is it private or public ?). It all amounts to defining such conditions and add them as when parameters.

Here’s an example of how the configuration file would like when doing so :

defaultParams: {
  isPublished: true,
  isPrivate: false
},
roles: {
  USER: {
    can: {
      read: [{ resource: ‘place’, when: {
        isPublished: true,
        isPrivate: false
      }, except: { isOwner: true } }]
      // ...
    }
  }
  // ...
}

Now what if some resource depends on whether the user has access or not to a linked resource ? For exemple let’s say we have a player resource and a game resource. Then we’ll simply add this in our aclRules configuration :

defaultParams: { ... },
roles: { ... },
dependencies: {player: { on: ‘game’ } }

That’s all (provided you only want one-level dependencies, but I can hardly think of use cases where you’d want more to be honest) !

So the goal is to rely entirely on such a descriptive configuration file for our whole access control system. How can we do this ?

My approach was to create a domain level entity called AccessControlList which models everything we need. It could go like this :

src/domain/accessControlList.js

See Gist

While not trivial, the code is again pretty straightforward when you think about it.

In our can() method, We are denying access if a role/capability combination does not exist, but we are allowing access if the resource asked match all when conditions (if present) or, based on our entity params property, if any matching exception is found. The whole (very simple) matching algorithm is found is the isIn() method.

Finally if there’s no match, we try inherited roles (through recursion), if there’s any inheritance.

Protecting a resource

So everytime a resource is created, updated or deleted, we will create and persists a new AccessControlList instance. This will occur in our protect() middleware :

src/interfaces/http/auth/accessControl.js

See Gist

We will protect a resource when dealing with a POST request. The resource posted would then be in the data field of our ctx.body object. We will then get our resourceReference from data.toString() as our resource is a domain entity with such a method (see Part 2).

Now remember that in Part 2 also, we fetched our current user and placed it in ctx.state.user. This is from where we will get our userReference. We are being careful though to distinguish user creation route (injected as a configuration parameter here), where we have no ctx.state.user as this is a public route.

We are also checking if some parameters allowed by our AccessControlList rules are actually present in the data object, and we skip protection for a dependant resource (as we need only to protect the parent).

Of course you could break down the above middleware into smaller functions because, I admit, as such it violates the single responsibility principle. But again I didn’t wanted things to be overly complicated.

Notice we are using a helper functions here : resolveOperation(), as well as 4 container services : createAcl, getAcl, removeAcl and updateAcl. It’s of course your responsibility to implement such services following the directions provided in Part 1.

Let’s see our resolveOperation() code :

src/interfaces/http/auth/accessControl.js

See Gist

The code is pretty easy to understand with some examples. The following routes will thus give :

PATCH /user/001/update : place as resource type, 001 as resource reference, update as operation

POST /place/create : place as resource type, create as operation

GET or DELETE /place/001 : place as resource type, 001 as resource reference, read as operation

This requires some discipline when creating routes (see Part 2 for tips) but you’ll need that anyway to have a robust API.

Checking access control

When on the other hand the resource is queried, the interface is then responsible to check the appropriate AccessControlList instance (if it exists) as well as for its potential dependencies’s own AccessControlList instances. And if no such instance exists, the interface will allow access based on the generic rules, given an operation and a resource type. This will occurs in our accessControl.check middleware :

src/interfaces/http/auth/accessControl.js

See Gist

We are using two new helper functions here : getParentAcl() and processQueryParametersForAcl(). Let’s start with the second :

src/interfaces/http/auth/accessControl.js

See Gist

(Our isOwner parameter is hard-coded here — being so universal — but you can choose to make it configurable of course.)

Now our getParentAcl() method is responsible for checking if a resource is dependant of another :

src/interfaces/http/auth/accessControl.js

The code should be self-explanatory (again, I highly encourage you to write readable code with meaningful variables and function names — and I hope you’ll be convinced it pays off when reading the above code — and using comments only when needed but wisely).

However, notice that the above code expects (otherwise it would break) your repository services to be name with the following pattern :

[resource]Repository

(userRepository for instance.)

You can imagine a more flexible system if you want, but such conventions are good practices anyway so it may not be worth it.

Also notice that we’re making use here of our base repository reload() method (see Part 2), silently failing if there’s an error at this level (it will just deny permission at the end) — we’ll see below how to handle errors that need to be handled.

This makes another expectation : that either one of the POST body parameters is the (properly named) parent resource (again, this seems natural and good practice, so I suggest you adhere with this kind of simple, efficient and coherent formalism) or that we are able to get the parent resource from a (properly named) corresponding property — this also seems very logical but requires some discipline : this is the (small) price to pay for a very easy configurable access control system.

So we’re at where we wanted to be : we now have a full blown access control mechanism. And if additional control is needed based on more complex logic, it will typically occur at application level.

How to handle errors properly ?

You should catch any error occurring in your code and log most of them. But you only have to present certain errors to the user :

  • Errors in your own code should crash your application,
  • Errors due to a bad request should inform the user that his request was… bad (and for what reasons),
  • Errors due to other services (database connection, cache connection, external APIs) are likely to be hidden as such to the user (but logged precisely), yet they must trigger some error in your application and you should present that error to the user.

Basically, that’s the gist of it.

For a detailed (and great) explanation on error handling in Node.js, here’s a mandatory read.

Now how to do that in our different layers ? And when exactly do we throw, catch, return and log ? Answers below.

In our domain layer, we won’t throw or catch or return any error. We certainly could, but it’s not necessary.

In our application layer, we will throw custom errors everytime they’re needed. Here’s a very basic example :

src/app/game/player/create.js

Here we are creating a player as defined in Part 2 (from a given user and a given game). In our domain Game entity, we’ll have implemented a hasPlayer() method which checks is a user is already a player. We are using it in our app level code to throw a custom error if that’s the case.

In our infrastructure layer, we will catch and log then re-throw some errors. For instance in our MongooseRepository we could do :

src/infra/database/repositories/mongooseRepository.js

See Gist

Just beware or try/catch hell when you do that ! You could use a decorator (as the ones we’ll see in Part 5) as a solution though.

Finally, in the presentation layer, we’ll catch errors in a centralized way and present them to the user. In the HTTP interface, This will be the responsibility of our (very simple) errorHandler Koa middleware :

src/interfaces/http/errors/errorHandler.js

See Gist

We have a nice all-encompassing try/catch block at the top level. This feels satisfying, right ?

In production, we display the error message, in test and development mode, we display the error stack trace.

The SERVER_ERROR event allow us to log the error in our server.js code like this :

src/interfaces/http/server.js

See Gist

In our HTTP interface, to be thorough, we’ll also need a notFoundHandler middleware which is, given all what we have set up so far, even more simple :

src/interfaces/http/errors/notFoundHandler.js

See Gist

And now we have our error handling system complete. Such, we can enhance it various ways (a custom Error object, domain errors, etc.). It’s up to you now.

In Part 4, we’ll handle documentation issues, testing and how to interface our app services with CLI commands. See you later guys !

Koa
Access Control
Authentication
Jwt
Error Handling
Recommended from ReadMedium