How to implement a secure REST API with node.js

tschiela picture tschiela · Mar 19, 2013 · Viewed 113.8k times · Source

I start planning a REST API with node.js ,express and mongodb. The API provides data for a website (public and private area) and maybe later a mobile app. The frontend will be developed with AngularJS.

For some days I read a lot about securing REST APIs, but I don’t get to a final solution. As far as I understand is to use HTTPS to provide a basic security. But how I can protect the API in that use cases:

  • Only visitors/users of the website/app are allowed to get data for the public area of the website/app

  • Only authenticated and authorized users are allowed to get data for private area (and only data, where the user granted permissions)

At the moment I think about to only allow users with a active session to use the API. To authorize the users I will use passport and for permission I need to implement something for myself. All on the top of HTTPS.

Can somebody provide some best practice or experiences? Is there a lack in my “architecture”?

Answer

Gabriel Llamas picture Gabriel Llamas · Mar 19, 2013

I've had the same problem you describe. The web site I'm building can be accessed from a mobile phone and from the browser so I need an api to allow users to signup, login and do some specific tasks. Furthermore, I need to support scalability, the same code running on different processes/machines.

Because users can CREATE resources (aka POST/PUT actions) you need to secure your api. You can use oauth or you can build your own solution but keep in mind that all the solutions can be broken if the password it's really easy to discover. The basic idea is to authenticate users using the username, password and a token, aka the apitoken. This apitoken can be generated using node-uuid and the password can be hashed using pbkdf2

Then, you need to save the session somewhere. If you save it in memory in a plain object, if you kill the server and reboot it again the session will be destroyed. Also, this is not scalable. If you use haproxy to load balance between machines or if you simply use workers, this session state will be stored in a single process so if the same user is redirected to another process/machine it will need to authenticate again. Therefore you need to store the session in a common place. This is typically done using redis.

When the user is authenticated (username+password+apitoken) generate another token for the session, aka accesstoken. Again, with node-uuid. Send to the user the accesstoken and the userid. The userid (key) and the accesstoken (value) are stored in redis with and expire time, e.g. 1h.

Now, every time the user does any operation using the rest api it will need to send the userid and the accesstoken.

If you allow the users to signup using the rest api, you'll need to create an admin account with an admin apitoken and store them in the mobile app (encrypt username+password+apitoken) because new users won't have an apitoken when they sign up.

The web also uses this api but you don't need to use apitokens. You can use express with a redis store or use the same technique described above but bypassing the apitoken check and returning to the user the userid+accesstoken in a cookie.

If you have private areas compare the username with the allowed users when they authenticate. You can also apply roles to the users.

Summary:

sequence diagram

An alternative without apitoken would be to use HTTPS and to send the username and password in the Authorization header and cache the username in redis.