Architecting a Secure RESTful Node.js app

Tweet about this on TwitterShare on LinkedIn48Share on Google+0Share on Reddit0Buffer this pageFlattr the authorEmail this to someonePrint this page

Architecting a Secure RESTful Node.js app

In this post, we will take a look at architecting a Node.js app that will act as a REST API server for all of our clients. We will be implementing a Token Based Authentication to authenticate and authorize the client to access the application data.

This application design is targeted at systems that would like to implement its own REST API with a custom authentication. To showcase how one can consume the REST API with authentication, we will be writing an Angular.js client.

This post is inspired by Authentication with AngularJS and a Node.js REST api.

You can find the completed code here.

So, let us get started.

REST API

If you are new to REST API, I would highly recommend watching the below video

Also do checkout restapitutorial.com and HTTP Status codes for more info.

Security

If you are new to implementing security or want to gain a fair idea, I would recommend watching the below video

 

Architecture

The key difference between a traditional application server and a RESTful server is that the RESTful server dispatches only data for an end point and not a web page. This is very advantageous when you want to have a mobile app or a tablet app built on top of your existing application data. This way the time to market your app idea is very low. You build your application logic only once and then consume it from any RESTful client.

Now the question which arises is how do we know if the client accessing our REST API is genuine? Here is one of the possible solution.

I have used the below architecture with a couple of my projects and it seemed to be pretty sturdy.

minimal

As you can see from the above diagram, the architecture is pretty flexible and can be used by any client that can consume a REST API.

Any client which would like to access the data from our system should first fire a request to a /login route (or endpoint) with a valid username and a password. We then check if the credentials are valid. If they are valid, we would send an user object back to the client along with an access token.

This token has an expiration time associated with it. And the client is expected to send this token to the server everytime a request is made to fetch the data. This is our custom way of authenticating a client.

For your app, you can either issue a username/password for your client (an Android app or Angular app) to connect and make API calls or you can use your customer’s credentials to make the call too.

As soon as a user makes a call to one of our API endpoints, we redirect the request to the authentication middleware. This middleware is responsible for authenticating the client by first processing the token. If the token is not present/invalid or expired, it will throw a 401 HTTP status code. If the request has a valid token and is not yet expired, we will invoke the next middleware in the stack.

If you want, you can also add another layer of security. You can request the client to send the logged in user’s username along with the token. This way, first we will authenticate the token and then the client. This is authentication and authorization.  This approach will also help you to check if a given user is allowed to access a route and its data.

Simple and easy right? We will implement the same now to get a feel of the architecture.

Setup the REST Server

Create a new folder named myRESTApp. Inside that folder, create a folder named server. We will first create a new node project. Open a new terminal/prompt from inside the server folder and run

npm init

Fill it up as applicable. Now, we will add a few dependencies. Run

npm install --save express body-parser morgan jwt-simple

We are going to use Express as our server side framework, jwt-simple module to generate JSON Web Tokens. These tokens will be used as access tokens between the server and client.

Our final package.json would look like

Now, we will setup our server. At the root of server folder, create a file named server.js. This will be the entry point into our node application.

Update server.js as below

Things to notice

Line 28 : The middleware that we are going to write to authenticate and authorize the request.

Line 30 : The list of routes for our application. We will create this file soon.

Line 42 : Start the server

Line 11 : Enable Cross Origin Resource Sharing

Remaining config is pretty standard Express stuff.

[Important] : You should consider adding HTTPS support to you app. Not only for login, but for every request. You can find more info about setting up HTTPS here. Thanks to Ward Bell for pointing it out.

Now, we will work on the routes. For the purpose of this post, we will create 3 types of routes

  1. Routes that do not need authentication or authorization like the login. Or an About Us, Contact details etc.
  2. Routes that need authentication to access the underlying resource. Like a list of products or any data that would be specific to the user.
  3. Routes that need authentication as well as authorization. Like deleting users or updating configs etc. (like an admin task). For this authentication alone would not suffice, you need to be authorized to access that resource.

Create a new folder named routes inside the server folder. Inside the routes folder create a new file named index.js. This file will hold all the routes needed for our app.

Updated index.js  as below

Things to notice

Lines 4 – 6 : Externalized the business logic for each route

Line 11: This is the login route. A client that would like to interact with our data, should first hit this URL, get the token and then only will be authorized to access the remaining routes

Lines 16 – 20 : A dummy set of API methods to access a product. This can be any resource you would like to expose to your clients. Do notice the /api/v1/ at the beginning of the route. This is cue for the authentication middleware to validate the request. If you do not provide the /api/v1/, the validation will be skipped. As you can see from line 11.

Line 25 – 29 : A dummy set of Admin API methods to manage a user. Do notice the /api/v1/. 

Now, we will build the logic for each of the 3 routes. First Login. Create a new file named auth.js inside the routes folder. This file will contain the logic to authenticate the user and generate an access token when the login is successful.

Update auth.js as below

Things to notice

Line 10 : If the username or password is invalid/not present, we will throw a 401

Line 20 : At this point, we will fire a call to DB and check if the credentials are valid. To keep this example simple, I have spoofed the response as a success & built a dummy DB response.

Line 23 : If the authentication fails, we send back a 401

Line 36 : If everything is fine so far, we respond with a token

Line 65 : This method is responsible for generating the JSON response that is expected from the login endpoint.

Line 67 : Generates the token with the provided expiration date and a app secret. We will create the app secret in a minute. Do note that the app secret is the only hook with which the decoding happens. If you change the secret after you dispatch a token, it may not be valid when it comes back from a client.

Now, we will create the secret. Create a new folder named config inside the server folder. Inside the config folder create a new file named secret.js and update it as

Now, we will create the dummy API for products and users. Create a file named products.js inside the routes folder and update it as below

And create another file named users.js and update it as below

Now, we will create the validation middleware. Create a new folder named middleware  inside the server folder. Inside the middleware folder create a new file named validateRequest.js. This will be the validation layer for the API requests.

Update validateRequest.js as below

Things to notice

Line 13,14 : We search for a token/key and in the request. This is the access token we have issued post successful login and key is the current logged in user’s username.

Line 18 : We decode the token and check its validity on line 20

Line 31 : We hit the DB to check if the current user is a valid user and also fetch his role.

Line 35 : We validate role vs url accessed and allow or deny accordingly.

All the other else cases would dispatch appropriate HTTP status codes.

That is all we need to build a secure REST API.

Testing the REST API

The process is pretty simple. First, we will fire a request to the login end point with dummy credentials. The response will contain a user object and the access token. Since we have spoofed the response to be a success, you will get a success response always. In your actual app, update this login to valid authentication.

Using that access token, we will make further request to our API.

I will be using CURL to fire the REST requests, you can use any REST client to do so.

Start our server by running

node server.js  or  nodemon server.js

Brute Force

Without any token, let us fire a request to get all the products

URL : curl http://localhost:3000/api/v1/products

Screen Shot 2014-09-21 at 11.25.11 pm

As expected you will get a 401. You can also dispatch a 403 unauthorized here.

Login

Now, let us try logging in.

URL : curl –data “{\”username\” : \”[email protected]\”, \”password\” : \”pass123\”}” -H “content-type:application/json” http://localhost:3000/login

The username and password above are dummy. Do notice the header content-type. Without this, Bodyparser will not kick in. And the response would consist of a token and the user object.

Screen Shot 2014-09-21 at 11.30.01 pm

Now, we will make a copy of the token and we need to send this in the headers from now on.

Fetch Products

To fetch all products, you would run

URL : curl -H “content-type:application/json” -H “x-access-token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MTE5MjcyMDAzNjl9.cuUKFKsf2qhQJHToP-zBmObhMwi84rhnrhH03OdyzSA” -H “x-key:[email protected]” http://localhost:3000/api/v1/products

Do notice that I have updated the token in the above URL. Make sure you update the token before firing the request.

Do notice that we have another header named key. This will consist of the current logged in user’s username.

And we would get the response back as

Screen Shot 2014-09-21 at 11.35.18 pmAdmin resources

Since we have hardcoded the role for the current user to be admin, we will be able to access the admin URL’s too. You can fire a request like

URL : curl -H “content-type:application/json” -H “x-access-token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MTE5MjcyMDAzNjl9.cuUKFKsf2qhQJHToP-zBmObhMwi84rhnrhH03OdyzSA” -H “x-key:[email protected]” http://localhost:3000/api/v1/admin/users

And you should see the response like

Screen Shot 2014-09-21 at 11.37.08 pmNow, let us test the authorization part. Navigate to routes/auth.js line 56, set the role to ‘user’ and restart the server

Now, fire the request to get all users

URL : curl -H “content-type:application/json” -H “x-access-token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MTE5MjcyMDAzNjl9.cuUKFKsf2qhQJHToP-zBmObhMwi84rhnrhH03OdyzSA” -H “x-key:[email protected]” http://localhost:3000/api/v1/admin/users

and you should see a “Not Authorized” error as expected.Screen Shot 2014-09-21 at 11.40.59 pmSimple and easy right!!

Angular.js Client

Now, we will build an Angular.js client that will consume the above API. Inside the myRESTApp folder, create another folder named ngClient. Open a new terminal/prompt here.

We will use a slush generator named slush-ng (written by me) to scaffold a minimal Angular.js app. For that we will install gulp, slush and slush-ng globally. Execute

Windows Mac/*nix
npm i -g gulp slush slush-ng    sudo npm i -g gulp slush slush-ng

Once this is done, from inside the ngClient folder, run

slush ng

You can provide any name to your project. And slush will scaffold a minimal angular app and download the required dependencies.

To run the Angular.js app as is, we will run

gulp

This will start a new server on port 2772. Now you can navigate to  http://localhost:2772 to see the scaffolded app in action.

This app is configured with routes, a sample directive a sample filter and an integration with the service layer. All you need to start off with a minimal Angular.js app.

Authentication Layer

The first thing we are going to do is setup an authentication factory on the client. Create a new folder named auth inside the js folder. Inside this folder, create a new file named auth.factory.js.

Update auth.factory.js as below

Things to notice

Line 1 : AuthenticationFactory. This Factory is responsible for checking the user status on the client side.

Line 17 : UserAuthFactory. This factory is responsible for contacting the login endpoint and validating the user. And also logging out the user.

Line 44 : TokenInterceptor. This factory is responsible for sending in the access token and the key along with each request to the server.

Next, we will create a controller to manage the login. Create a file named auth.controller.js inside the auth folder. Update it as below

Things to notice

Line 3 : We hard code a user, for easy testing (lazy)

Line 14 : We fire a request to the Login endpoint

Line 16 – 22 : We set client side session variables. We store the values in the AuthenticationFactory as well as sessionStorage. This way, if the user refreshes, we will still have the session on the client side.

Line 24 : Upon successful login, we will redirect the user to the home page.

On the client side, we will create a partial for Login. Inside the partials folder, create a new file named login.html and update it as below

This partial will be associated with a Login Controller, which we created in auth.controller.js.

Now, we will setup our app. Open js/app.js and update it as below

Things to notice

Line 5 : We have added the TokenInterceptor.

Line 8 : New route /login

Line 12,18,24,30,36 : Setting if the current route needs authentication.

Line 43 : We set up a few session variables to keep a track of user’s session. These variables are stored both in memory as well as browser’s session storage (in case of a full page refresh we do not want the user to login again).

Now, we will add the logout link. Open partials/header.html and update it as below

Things to notice

Line 4,12showMenu expression will be true when the user is logged in. This way, we do not show the menu to the user if not logged in. This value will be updated in app.js line 58.

Line 21 : The logout link

Now, the functionality for the logout link. Open controllers.js and update the HeaderCtrl as below

The code so far will take care of the login. Now we will make a request to get all products after successful login.

We will create a new dataFactory to interact with the REST API. Open factory.js and update it as below

A sample route to demonstrate the access of API methods post login.

Now in controllers.js update the Page3Ctrl as below

Pretty straight forward. Access the Factory, get the products and finally update the UI. Open partials/page3.html and update it as below

Finally the index.html. Update it as below with the newly created js references

That is it. Back to browser (with gulp command running in the background) and navigate to  http://localhost:2772. You should see a login page

Click on Submit and you should see the home page. Now click on logout and you should be logged out. Log back in and click on Page 3 (Sample Factory) link in the menu. Now, you should see a list of products from the REST endpoint.

Screen Shot 2014-09-22 at 9.02.40 am

Simple, easy, secure and scalable architecture for your awesome apps!


Thanks for reading! Do comment.
@arvindr21

Tweet about this on TwitterShare on LinkedIn48Share on Google+0Share on Reddit0Buffer this pageFlattr the authorEmail this to someonePrint this page
  • prateek

    Hi Arvind,

    First of all thank you for providing a detailed conceptual insight into creating a secure Restful Nodejs application. It is really helpful for beginners like me.

    I would really appreciate if you could help me resolve my issue:

    So in my middleware validateRequest.js, I check if my token is expired, if it has expired I return a new oUser object with a fresh token. Problem is oUser always returns ‘undefined’ inspite of the fact that ‘checkToken’ gets called and a new token is generated in the db. :

    module.exports = function(req, res, next) {
    if (req.body.token) {
    var decoded = jwt.decode(req.body.token, require(‘../config/secret’)());
    if( token_is_expired ) { //token expire logic
    var oUser = checkToken(decoded); //call to routes/authToken.js
    if(oUser){
    res.json({success: true, response:oUser});
    }else{
    res.json({success: false, response:{msg:”There was some problem”}});
    return;
    }
    }else{
    next();
    }
    } else {
    res.json({success: false, response:{msg:”Invalid Token”}});return;
    }
    }

    Has this got anything to do with aysnc or non-blocking call? Appreciate any help.

    • prateek

      I was able to solve this. Instead of :
      var oUser = checkToken(decoded);
      I used a function callback as below:
      checkToken(decoded, function(err, res){
      console.log(res);
      });
      Once again thanks for the great article.

  • johaness vix

    are you passing user and password in plain? how to avoid doing that?

  • John

    Hi Arvind,

    How to handle user log out at server side? when user logs out what we need to do with the valid token?

    • / Arvind Ravulavaru

      Hello John,

      Our server is RESTful, that means it is stateless. You need not do anything with the server (with the above architecture). On the client side when the user clicks on logout, we clear the local persistence of the token. Like

  • Patrick R

    Any reason I could be hitting the following errors:

    [18:53:33] Server started http://localhost:2772
    [18:53:33] LiveReload started on port 35729
    Error: Forbidden
    at createError (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-index/index.js:188:13)
    at Object.directory [as handle] (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-index/index.js:100:46)
    at next (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/lib/proto.js:194:15)
    at SendStream.error (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-static/index.js:86:37)
    at SendStream.emit (events.js:107:17)
    at SendStream.error (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-static/node_modules/send/lib/send.js:172:51)
    at SendStream.onStatError (/apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-static/node_modules/send/lib/send.js:285:48)
    at /apps/backend/projects/myRESTApp-master/ngClient/node_modules/gulp-connect/node_modules/connect/node_modules/serve-static/node_modules/send/lib/send.js:378:26
    at FSReqWrap.oncomplete (fs.js:95:15)

    • Mark Terrey

      did you work this one out?

      • Daniel Abrão

        change this snippet on server.js:

        app.all(‘/*’, function(req, res, next) {

        // CORS headers

        res.header(“Access-Control-Allow-Origin”, “*”); // restrict it to the required domain

        res.header(‘Access-Control-Allow-Methods’, ‘GET,PUT,POST,DELETE,OPTIONS’);

        // Set custom headers for CORS

        res.header(‘Access-Control-Allow-Headers’, ‘Content-type,Accept,X-Access-Token,X-Key’);

        if (req.method == ‘OPTIONS’) {

        res.status(200).end();

        } else {

        next();

        }

        });

        • Gaurav Arora

          I get the error despite the changes you mentioned.

  • Patrick R

    Am on linux (RHEL)…I get the following. What’s missing?

    curl –data “{“username” : “[email protected]”, “password” : “pass123″}” -H “content-type:application/json” http://localhost:3000/login

    curl: (6) Could not resolve host: xn--data-z86a; Name or service not known

    [1/2]: “username” : “[email protected]” –>

    –_curl_–“username” : “[email protected]

    curl: (6) Could not resolve host: myapp.com”; Name or service not known

    [2/2]: “password” : “pass123″ –>

    –_curl_– “password” : “pass123″

    curl: (6) Could not resolve host: “password” ; Name or service not known

    Error: Not Found    at /apps/backend/projects/myRESTApp-master/server/server.js:34:13    at Layer.handle [as handle_request] (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/layer.js:82:5)    at trim_prefix (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:302:13)    at /apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:270:7    at Function.proto.process_params (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:321:12)    at next (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:261:10)    at /apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:603:15    at next (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:246:14)    at Function.proto.handle (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:166:3)    at router (/apps/backend/projects/myRESTApp-master/server/node_modules/express/lib/router/index.js:35:12)

    • Kev

      Patrick R, please try this

      curl -d '{"username":"[email protected]", "password": "pass123"}' -H 'Content-type: application/json' http://localhost:3000/login

      • Patrick R

        thank you! worked

  • Brooks Wood

    Excellent article and thank you!

    I have connected this example to my database and I am abel to successfully log in. When I try to get a list of “readings” in this case instead of “products” in your example my return is empty. Any thoughts on the best way to return a JSON get request from the an API endpoint/db? I have attached a few screenshots of my revised coders

    • / Arvind Ravulavaru

      Hello Brooks, Thanks!

      Quick question, when you navigate to http://localhost:3000/readings in the browser what happens?

      • Brooks Wood

        Hi Arvind, thanks for the response.

        As long as I use postman and customer headers with JWT and username I get this JSON Response:

        [

        {

        “_id” : “55501da53506aaa9494d0fd6″,

        “coreid” : “55555”,

        “__v” : 0,

        “temp” : “98dF”,

        “alert” : “none”

        }

        ]

        • / Arvind Ravulavaru

          I noticed that your ng-repeat is reading in readings but you are using r.coreid and r.temp to display the values. Shouldn’t this be reading.coreid or reading.temp ? Could that be the issue?

          • Brooks Wood

            I have tried several variations and just tried again as ‘reading.coreid’ and ‘reading.temp’ the outcome is the same. Blank result.

          • / Arvind Ravulavaru

            Hmm.. can you try console logging the response in your controller (data.data) and see what is the value?

          • Brooks Wood

            Here is what I added but I am not familiar with getting console.log output results while using gulp. Do you have any thoughts?

          • Brooks Wood

            IMG Attached

          • / Arvind Ravulavaru

            Try this

            Save the file and refresh the page. Next, open developer tools and you should see something logged in the console tab.

          • Brooks Wood

            Here is the console output – attached is an image of my controller and factory

            ——————–

            [Error] Error: Can’t find variable: readFactory

            http://localhost:2772/js/controllers.js:40:16

            [email protected]://localhost:2772/libs/angular/angular.min.js:35:41

            [email protected]://localhost:2772/libs/angular/angular.min.js:35:166

            http://localhost:2772/libs/angular/angular.min.js:67:562

            [email protected]://localhost:2772/libs/angular-route/angular-route.min.js:7:249

            [email protected]://localhost:2772/libs/angular/angular.min.js:54:374

            [email protected]://localhost:2772/libs/angular/angular.min.js:47:257

            http://localhost:2772/libs/angular/angular.min.js:46:378

            http://localhost:2772/libs/angular/angular.min.js:48:218

            [email protected]://localhost:2772/libs/angular/angular.min.js:52:30

            [email protected]://localhost:2772/libs/angular-route/angular-route.min.js:6:498

            [email protected]://localhost:2772/libs/angular/angular.min.js:115:310

            http://localhost:2772/libs/angular-route/angular-route.min.js:11:189

            [email protected]://localhost:2772/libs/angular/angular.min.js:101:96

            [email protected]://localhost:2772/libs/angular/angular.min.js:101:96

            http://localhost:2772/libs/angular/angular.min.js:102:260

            [email protected]://localhost:2772/libs/angular/angular.min.js:113:32

            [email protected]://localhost:2772/libs/angular/angular.min.js:110:122

            [email protected]://localhost:2772/libs/angular/angular.min.js:113:369

            [email protected]://localhost:2772/libs/angular/angular.min.js:72:460

            [email protected]://localhost:2772/libs/angular/angular.min.js:77:796

            [email protected]://localhost:2772/libs/angular/angular.min.js:79:25

            (anonymous function) (angular.min.js, line 92)

            (anonymous function) (angular.min.js, line 68)

            J (angular.min.js, line 54)

            g (angular.min.js, line 47)

            (anonymous function) (angular.min.js, line 46)

            (anonymous function) (angular.min.js, line 48)

            w (angular.min.js, line 52)

            v (angular-route.min.js, line 6)

            $broadcast (angular.min.js, line 115)

            (anonymous function) (angular-route.min.js, line 11)

            J (angular.min.js, line 101)

            J (angular.min.js, line 101)

            (anonymous function) (angular.min.js, line 102)

            $eval (angular.min.js, line 113)

            $digest (angular.min.js, line 110)

            $apply (angular.min.js, line 113)

            m (angular.min.js, line 72)

            w (angular.min.js, line 77)

            onreadystatechange (angular.min.js, line 79)

          • / Arvind Ravulavaru

            Damn! how could I have missed that!! The variable alias name of your factory is dataFactory and not readingsFactory. Change your code to

          • Brooks Wood

            Thank you that did resolved all client issues. The server side is now returning 401 error. Its interesting because I am able to login successfully. The same user and token through postman works perfect.

          • Brooks Wood

            Hello Arvind!

            Thank you for the your help on this so far. I think am very close at this point. When inspecting the headers in an attempt to get the ‘Readings’ return. I noticed the the http$ interceptor is only passing along the token. The username and role are “undefined”. I am assuming this is why I am getting a 401.

            Attached are the screenshots. My app.js screenshot shows what appears to be the user information being stored and sent along with each request after auth. Do you have any thoughts or guidance?

          • Brooks Wood

            I was able to get this working finally. Based my API I had to change

            auth.controller.js line22 to $window.sessionStorage.user = data.user; instead of ‘data.user.username’

          • / Arvind Ravulavaru

            ah! ok! Looks like the complete architecture is setup now!

          • Kev

            Hi Brooks, glad that you had it working. I’d like to connect to nedb (user with encrypted password). Can you shed some lights how to integrate with this secure restful feature? Thanks in advance.

  • Sam

    Hi, Is there a way I can restrict this endpoint: (router.delete(‘/api/v1/product/:id’, products.delete);) to be accessed from a specific IP?. Thanks Sam

    • / Arvind Ravulavaru

      Hey Sam,

      Yes, you can create a middleware that gets added to your delete route. All the middleware does is gets the IP from the request and validates if it can execute the delete operation. If yes, you can call the next() or else you return an error req.status(403).send("You do not have access to delete this resource")

      Your code would look something like

      And you can use this middleware on your delete request

      More info : http://www.hacksparrow.com/how-to-write-midddleware-for-connect-express-js.html

      Thanks.

  • Robin Duda

    Using bodyParser, the content type of req.body parameters are not limited to strings. An attacker may post to the server specifying the JSON-type, req.body would then contain an object. If you send this object to the database without verifying it’s type, injections are possible. Consider the following payload for /login, { username: “administrator”, password: {“$gt”:””} }.

    How do you handle this scenario?

    “You can request the client to send the logged in user’s username along with the token. This way, first we will authenticate the token and then the client.”

    How does this improve security when a token is generated by a /login? Should the tokens not be bound to an user on the server?

    If an attacker acquires only the token, it cannot be used since the username is also required. But the token and username are always sent together, thus if the attacker gains access to the token the username will also be available.

    • / Arvind Ravulavaru

      Hey Robin,

      Very valid points. To answer the question, based on my knowledge

      1. The password which is sent from the client in plain text, will be hashed. So they no more look like injectable pieces of mongoDB queries. And as far as the application logic goes, we are no where evaluating the password. So I think we should be safe. (Let me know if I missed something here)

      2. So, there is where the concept of OAuth 2.0 comes into picture. Where the client and server exchange tokens which gets created based on an API key. That solution would be a full blow OAuth server, that registers a client with a API key and then allows it to make calls to it.
      Did that answer your question?

      Thanks,
      Arvind.

      • Robin Duda

        Thank you for the reply.

        Yes the password will be hashed before it is added to a query, however if the password is itself an object the hashing function will fail. The hashing function will throw an exception since it expects a buffer and not an object, the server will most likely crash.

        The username should still be able to inject queries if it’s passed directly into one. Depending on the implementation the attacker might be able to attempt a login to all accounts at once with one password, or attempt logins to accounts without knowing their username. I am unsure whether the second password parameter could be modified or nullified, like SQL );–

        • / Arvind Ravulavaru

          Good point! Have not tried it though. I will give it a try. This article was my first attempt at security in Nodejs. I have learned a lot since then. I will probably write one more article with a couple of new approaches. Thanks for your comments!!

  • http://www.i-visionblog.com/ s.shivasurya

    really awesome !

  • Maxime ROBERT

    Hi, thx for this great tutorial Arvind !
    I have a problem when i try to log in.

    The error is the following : “Error: Not Found    at /home/maxime/Desktop/myRESTApp-master/server/server.js:34:13″

    Do you have any idea why ?

    Cheers

    • / Arvind Ravulavaru

      Hi Robert. Thanks! May I know to which end point are you making a request to?

      • https://friendly-account.fr Maxime ROBERT

        Not sure of what you mean, correct me if i’m not answering well.
        (what do you call “end point” ?)

        I’m trying to reach the api with postman at the following url : http://localhost:3000/[email protected]&password=pass123

        • / Arvind Ravulavaru

          The end point here is /login. And if you see the routes, /login is of type post. So, you cannot use the browser address bar to make the call. You need to create a new request with type post to http://localhost:3000/login and send the username and password as request body or Payload. You can either use curl to make a request or a chrome extension like postman to do the same.

  • Lamar

    Wouldn’t it be more secure to do this in auth.js:

    function genToken(user) {
    var expires = expiresIn(7); // 7 days
    var token = jwt.encode({
    exp: expires,
    user: user // add the user object to the token
    }, require(‘../config/secret’)());

    Then later in middlewares/validateRequest.js do this:

    var dbUser = validateUser(decoded.user.name); // The key would be the logged in user’s username

    This way you can eliminate the need for the extra ‘x-key’ header and you will prevent a user from escalating privileges by:
    1. getting a valid token for a user that has lower permissions
    2. passing that valid token and an ‘x-key’ of an admin user

    • / Arvind Ravulavaru

      Hello Lamar, Yes, You can could do that as well.

      • Buri Erich

        It’s a great tutorial, thank you!
        But what Lamar pointed out is a not a “could do”, it’s a MUST DO! You should fix this in the tutorial as people copy & paste and distribute this vulnerability!

        • / Arvind Ravulavaru

          Thanks Buri. Sure. I am planning to write one more article (I don’t know when!) on all the things I learnt after I wrote this one. I will add this and more to that! Thanks for pointing it out!

      • Thomas Dunne

        I think this is very important to implement too. Without it, everyone can get admin level privileges by just changing this header.

        Also, removing the password field of the user object (ignoring the fact that the password shouldn’t be stored, just a hash + random salt), before sending it back to the client would be beneficial in cases that the /login endpoint was the only one secured by an SSL cert.

        Quick question: is using a secret-encrypted access token beneficial? Wouldn’t storing a shorter, random token (until it expired or is invalidated) be better?

        • http://kolyjjj.github.io koly

          I believe it is more safe to use a random token, however if a random token is used, you need a relationship between the user and the token, and somewhere to store it. So if a random token is used, we may need back-end storage(redis or database) to store the mapping between user and the token, so when the token is sent to server, server can know the user from the token by a query.

    • Marco Solari

      This is for sure very important.
      Just a question: how to get the username on the client, if you only have the encripted token? Should you decript it client-side, too? I’m a bit confused… :-(

    • aldopraherda

      Hi Lamar
      do you have updated code about this vulnerability if yes please share

  • Varma Bhupatiraju

    Great start to securing Node services. Well done Arvind!!!

  • salesmen library

    Hi @arvindravulavaru:disqus I am trying to replace your DB spoof object with an object from the database, however, I am unable to make it work. Could you please help me out?
    I am using Bookshelf ORM, with MySQL database. I added a new function to users.js for checkLogin case as below:

    checkLogin: function(username, password) {

    return new Users({‘username':username, ‘password_hash': password}).fetch()
    .then(function(user) {
    return user.toJSON();
    }).catch(function(error) {
    console.log(error);
    return ”;
    });
    }

    and in auth.js replaced the dbSpoof with:

    validate: function(username, password) {
    // spoofing the DB response for simplicity
    // var dbUserObj = { // spoofing a userobject from the DB.
    // name: ‘sales.men’,
    // role: ‘admin’,
    // username: ‘[email protected]
    // };
    var dbUserObj = users.checkLogin(username, password);

    return dbUserObj;
    },

    When I console.log dbUserObj, it returns a promise object and since the below is always true, I do not receive an error when I pass in the wrong credentials:

    if (!dbUserObj) { // If authentication fails, we send a 401 back
    res.status(401);
    res.json({
    “status”: 401,
    “message”: “Invalid credentials”
    });
    return;
    }

    if (dbUserObj) {

    // If authentication is success, we will generate a token
    // and dispatch it to the client

    res.json(genToken(dbUserObj));
    }

    • salesmen library

      Hey @arvindravulavaru:disqus, I have solved this problem. Cheers.

      • / Arvind Ravulavaru

        Cool! What was the issue?

        • salesmen library

          The world of async. I was trying to get the async function to return the value. I changed the code to use promises and pass in the result from async function to generate token later. :)

          • salesmen library

            As well, because bookshelf returns a promise.

  • salesmen library

    @arvindravulavaru:disqus
    Great article. Thanks a lot!
    I have a question.. When I POST using REST client “http://localhost:3000/[email protected]&password=abc112″, I receive an error :

    {
    status: 401
    message: “Invalid credentials”
    }
    I have the following line of code in server.js
    app.use(bodyParser.json());
    As I tried debugging a bit, I found that it never reaches to the database call section in auth.js. I read the comments below as well and tried content-type:application/json but to no avail.

    • salesmen library

      Hey, I found the solution: I am using a windows client with CURL now..
      curl -D- -X POST –data @tests/test-data.json -H “Content-Type: application/json” http://localhost:3000/login
      contents of the file have the json data to post.

      • / Arvind Ravulavaru

        Is this not working

        • salesmen library

          No it isn’t on windows. I suspect because of a different version of curl or the requirement of explicitly setting the arguments -X POST.

        • salesmen library

          @arvindravulavaru:disqus
          Hey Arvind, I was just thinking, why haven’t you used promises?
          Moreover, how could I implement the SCIM architecture as it is believed that the person having an account only uses the app and must login.

          • / Arvind Ravulavaru

            Sorry I missed your comment somehow.

            Use promises where? Also can you elaborate on the SCIM and how it can applied here?

  • b4dblog

    Tnks mate!

  • Anil

    Hello Arvind,

    I have a question may sound silly but asking.
    After authentication client will have a valid token with username in it, is it necessary to query db for checking username on every api request with valid token? can’t we just check if token is valid and and get username from it and use it without querying db for checking the username as token is valid. Don’t you think every api request will fire a db query thousands of api request and with every request a db query that will create a lot of extra load on database server.

    Thanks,
    Anil

    • / Arvind Ravulavaru

      Hello Anil,

      This DB call on every request will add an extra security to the app. Imagine a user has expired his/her paid subscription to your app. His token was generated when his subscription was valid. How do you check with every request if the user is authorized/subscribed to use your app?

      This extra DB call will gets more info. about the user so that you can make decisions about his/her access. Imagine you have role based routes/APIs that a certain segment of users can only access. This extra call will help you with that. In the above scenario, you may argue that you would send the user’s role along with the token & email. In that case, the extra DB call would not be needed to make a decision on the Route/API access.

      But in the first case, you would need to be 100% sure that no one is mooching off your app.

      Does this makes sense?

      Also, if you are worried about the DB query performance, I recommend

      1. MongoDB Cache

      2. Caching repeating query results in MongoDB

      3. mongoose-redis-cache

      Thanks.

      • Anil

        Thanks Arvind.
        If the user is not in a scenario of subscription based api then do you think its ok without checking for username by querying DB? what do you recommend should I check by DB call on every api call?

        Also one more question related to deployment, is it ok to deploy MongoDB server on the same application server where I am hosting the api or Mongodb server should be deployed on a separate server than application server? If we can deploy both on same server do you think it will create any performance issue for the apis?

        • / Arvind Ravulavaru

          That is a decision that you and your team need to take based on the application you are building. I recommend leaving the db call in place and using it to re-authenticate the user on every request.

          As far as the deployment goes, it again depends on the load. You can see how your app performs when both the app server and db are on the same box. If the performance is not as expected, you can separate them. This is the cloud era, so problems like this are already solved. You can design your app for horizontal scaling as you need.

          Thanks.

          • Anil

            Thanks Arvind.

            I have one more question. When a user logs out token gets removed from the on the Angular or any other client side, but it’s still a valid token and could be used by an attacker to make request on the API until its expiration. How to handle the log out? Can you give some suggestions on that.

            Thanks.

  • John

    Hi Arvind,

    Thanks for such a great and helpful article it really helped me a lot.

    I using your architecture for creating an API for multiple devices and need your help in creating refresh tokens for authentication so that API can generate a refresh token without asking for username and password whenever authentication token is expired. Please can you write some code and show how this can be done in the above architecture.

    That will really help me and maybe others too.

    Thanks so much in advance.

    • / Arvind Ravulavaru

      Hello John,

      Thanks! First off, Auto refreshing tokens may be a security risk. Please use them with caution. Understand and see if there might be any security threats in your app. Some way “an unintended” user can gain access to another user’s machine and the token auto refreshes and the “unintended” user can see the actual user’s data. Does this makes sense?

      If you still want, below is the solution for auto-refreshing tokens

      Step 1 : Let server dispatch a 401 when the token is expired (http://stackoverflow.com/a/10135309/1015046)

      Step 2 : Create another REST End point which takes a Username/Email Address and generates a new token. Similar to the login method, expect this takes only the username/email address and generates the token after querying the DB.

      Step 3 : Update the token interceptor inside Angularjs like

      Inside the responseError, when you receive a 401, make a request to the REST end point on Step 2 and get a new token.

      PS : I have not tried this solution, but it should “technically” work.

      Let me know otherwise.

      Thanks,
      Arvind.

      • John

        Hi Arvind,

        Thanks so much for the help and for mentioning about the unintended user security threat.

        Actually I want to implement auto-refresh tokens for native Android app as user only usually logs in only once in it. Please give your suggestion if auto-refresh token is the right way or I should implement some other solution for native Android app authentication from the API.

        • / Arvind Ravulavaru

          Hello John,

          This totally depends on the what kind of data you are dealing with and how much security you want. As per my knowledge and experience, I would set a high value for the expiry date of the token. May be 6 months, 1 year? but not implement an auto-refreshing system. Asking a user to login after 6 months seems reasonable.

          Also another alternative is to generate a new token on every 5th or 10th request (depending on the API usage) when a user makes a request to the server with a valid token. This may or may not be a better solution depending on the app you want to implement. But, this way you are not worried about an end-point that exposes an auto-refreshing token but rather the token refreshes on the usage of the server.

          You can combine the above two approaches and make a system, where the token generated expires every 6 months and with every 10th request from the client to the server API, you send a flag asking the server to send a new token, after authenticating and authorizing both the request and the user. This way, your auto-refresh happens on an on-demand basis.

          Thanks.

  • jack

    Great tutorial thanks so much!! i just was curious tho if the curl command you give is incorrect or some code is? O am getting issues trying to get a token with the fixed user you suggest and am not getting very much luck. after removing the

    || ”

    out of the lines:

    var username = req.body.username || ”;
    var password = req.body.password || ”;

    I was able to get a token with postman, but no luck with curl on the command line. I played around a bit with the parameters but didn’t have any luck. Was curious if you could help out. I am using a mac, and I tried several times with your code and me replicating the project. the error I get with curl is

    curl: (3) [globbing] unmatched brace in column 4
    curl: (7) Failed to connect to port 80: Connection refused
    curl: (6) Could not resolve host: myapp.com”,
    curl: (6) Could not resolve host: ”password”
    curl: (7) Failed to connect to port 80: Connection refused
    curl: (3) [globbing] unmatched close brace/bracket in column 14
    curl: (52) Empty reply from server

    and when I use postman without romoving the or check for empty string I get an error for invalid credentials and tracked it to this check in auth.js

    if (username == ” || password == ”) {
    res.status(401);
    res.json({
    “status”: 401,
    “message”: “Invalid credentials”
    });
    return;
    }

    Well any help would be very much appreciated, but also thanks so much for a great article, hard to find stuff specifically on rest api authentication.

    Thank!

    • / Arvind Ravulavaru

      Hello Jack. Thanks! Can you paste the curl command you are using here?

      • jack

        I used the command you provide in the article, the second one u use to try to get a token

        • / Arvind Ravulavaru

          It still works for me. Can you copy paste the same here?

          • jack

            Hmm are you using a windows machine? I think the curl command is just nota syntactically right.. The curl is the same curl u did, sorry I am away from a computer, but its curl -data… And the errors I put below. Thanks again

          • / Arvind Ravulavaru

            I use a Mac.

  • Vaclav

    Hi Arvind,

    At the beginning you mentioned that if token expires (…”If the token is not present/invalid or expired, it will throw a 401 HTTP status code.”…), server then returns 401. It seems that validateRequest.js (line 20-27) response to this situation with status code 400 – correct me if I’m wrong.

    Excellent article btw :)

    Cheers,
    Vaclav

    • / Arvind Ravulavaru

      Hello Vaclav. Thanks! Yes, you are right. Look at line 63, the else clause. Here, if the token and key are not present I am sending a 401, you can add the same on line 20 as well. This is after decoding the token and validating the same.

  • Tadeu

    That perfect! Thanks!

  • Andreas

    Great article! Im trying to implement mongojs and put my mongo-query in the validate-method. The problem is that since it’s asynchronous dbUserObj gets undefined and the if-statement will always be false and throw me the “invalid credentials”-page. I tried to put “res.json(genToken(dbUserObj));” in the querys success callback but then I get “headers already sent”-error. Any ideas on this?

    • / Arvind Ravulavaru

      Hello Andreas, Thanks!

      As you mentioned, all I/O calls in Nodejs are Async. So you need to know how to deal with them. Checkout Nodejs design patterns. You can find a live example of working with Mongojs and Nodejs : MEAN Stack hands on tutorial

      To answer your question in simple words, move all the code below the DB call into its callback. So the response will be sent only when the DB operation is completed.

      Thanks.

      • Andreas

        Thanks for the help! Solved it by moving the respons inside the callback :)

        • Michal Zimmermann

          Would you mind sharing the code?

  • SGH

    Hi Arvind,

    Thanks for the great tutorial. I am looking forward to using this tutorial towards a project and I need some help.

    I have mongodb running and have created the following database and user.

    >db.testdb.insert({name:”John”,username:”john”, password:”roberts”, role:”admin”})
    > db.testdb.find({username:”john”})

    The following proves that the user was created into the db.
    { “_id” : ObjectId(“5514e6c2b6e09033d4746dca”), “name” : “John”, “username” : “john”, “password” : “roberts”, “role” : “admin” }

    Now in my auth.js I have the following requires:

    var mongojs = require(‘mongojs’);
    var db = mongojs(‘testdb’,[‘testdb’]);

    And, this is what I did to the validate() functions:

    validate: function(username, password) {
    // spoofing the DB response for simplicity
    var dbUserObj = db.testdb.findOne({username:username});

    // var dbUserObj = { // spoofing a userobject from the DB.
    // name: ‘arvind’,
    // role: ‘admin’,
    // username: ‘[email protected]
    // };
    return dbUserObj;
    },

    And I have done the same thing for validateUser. I restart the server and it compiles fine. When I try to send the following JSON request from POSTMAN I get the following 401 error:

    {
    “username” :”john”,
    “role” : “admin”,
    “password” : “roberts”
    }

    With above input to postman I get :

    {
    “status”: 401,
    “message”: “Invalid credentials”
    }

    I am pretty sure my DB calls might be screwed up. Can you please help?

    • / Arvind Ravulavaru

      Hello SGH, Thanks!

      Can you try console logging the username and password inside the validate()? Also in postman, make sure you set the content-type header to application/json. Thanks.

      • SGH

        Console.Log gives me “undefined” :(

        • SGH

          Nevermind, it actually returns the username and password. Do you think my query has a problem?

          • / Arvind Ravulavaru

            SGH,

            is not the correct way, when you are dealing with MongoDB/Nodejs. You need to write

            Checkout

            MEAN Stack Hands on tutorial

            Nodejs Restify MongoDB – Build your own REST API

            or

            Ionic Restify MongoDB – An End to End Hybrid App

            to get a better understanding.

            Thanks!

          • SGH

            What exactly is “data” in the function? and how do I print out its contents?

            Thanks,

          • / Arvind Ravulavaru

            Please go through the provided links or Google about mongojs/mongoskin or MongoDB & Nodejs.

  • JL Griffin

    ok im stuck at logging in with curl.

    running the command exactly as you have it (copy+paste) just changing the port returns this:

    curl: (6) Could not resolve host: xn--data-z86a
    curl: (3) [globbing] unmatched brace in column 4
    curl: (6) Could not resolve host:
    curl: (6) Could not resolve host: myapp.xn--com,-ib7a
    curl: (6) Could not resolve host: xn--password-cq3di
    curl: (6) Could not resolve host:
    curl: (3) [globbing] unmatched close brace/bracket in column 14
    curl: (52) Empty reply from server

    is there something ive done wrong. also express refuses to listen on the specified port. it only works if i run it from port 38531?

    • / Arvind Ravulavaru

      Hello JL Griffin. Not sure about the express port issue, you can check what process is running on port 3000 using

      Coming to the cURL issues, you can use any of the below chrome extensions to test your API

      * Advanced REST client
      * Postman – REST Client

      Let me know how it works out. Thanks.

      • JL Griffin

        I cannot even connect to express from those. im not following this on localhost, im implimenting it on a test server, however putting the server IP it says “The response status was 0.Check out the W3C XMLHttpRequest Level 2 spec for more details about when this happens.”

        Now if i do the command prior to logging in with curl, it completes just fine from CLI on the server, but i get the above text when i try to log in CLI from the server. i cant get postman or Advanced to work at all

        • / Arvind Ravulavaru

          I am not sure, but this looks like a issue with the test server. Can you try the same on your local machine and check?

          • JL Griffin

            i can give it a try but what good does this do me if it only works from local host, this would need to be deployed to a server.

          • / Arvind Ravulavaru

            The good it does is for you to identify how your server setup is different from the local machine so that you can either change your JS source code to match it or modify the configs of your server.

          • JL Griffin

            same issues on localhost

    • jack

      I got the same error just running on localhost.. not sure what to do..

      • jack

        ehh the curl still wont work for me but i just got a token from postman submitting a form!!! had to remove the || ”; part out of the lines var username = req.body.username || ”; and var password = req.body.password || ”; still working on why curl gives so many errors not sure why tho

        • JL Griffin

          same here this is frustrating to say the least

          • jack

            Yea I feel yea, I moved on and made a simple token based backend that works alright.. building the frontend now. I checked it into this repo https://github.com/jziesing/NodeAuthStarter/blob/master/app.js but it needs this code

            app.all(‘/*’, function(req, res, next) {

            // CORS headers

            res.header(“Access-Control-Allow-Origin”, “*”); // restrict it to the required domain

            res.header(‘Access-Control-Allow-Methods’, ‘GET,PUT,POST,DELETE,OPTIONS’);

            // Set custom headers for CORS

            res.header(‘Access-Control-Allow-Headers’, ‘Content-type,Accept,X-Access-Token,X-Key’);

            if (req.method == ‘OPTIONS’) {

            res.status(200).end();

            } else {

            next();

            }

            });

            to work with a client of a different host. It works well with postman and you can generate a token by sending a get with username and password in the header. I am working on a frontend and will write more about it when i finish,

            but yes very frustrating, the curl command seems incorrect though I am no master of curl

  • ghzmdr

    Very nice article (and blog), I’d like to add a few things about the token.

    I’m building a service where ‘/api/username/caregory/thing’ maps to a DB
    resource, these resources are private to the user who owns them.

    So when the token is created for user ‘foo’ he should be able to access only ‘/api/foo/category/…’ but since the token doesn’t store any info on the user it has been created for, user ‘bar’ can log as ‘bar’, get a token and access ‘/api/foo/…’.

    I solved this by adding an username field to the token, then checking if
    the required path starts with that username and run next() only if this
    condition holds true.

    I’d like to know what do you think about this implementation

    • / Arvind Ravulavaru

      Hello ghzmdr, Thanks!

      The solution you have provided seems fine. I would recommend 2 things in the token

      1. Email/username/db unique id
      2. User role

      Use #1 for authentication purposes after successful validation of token.
      Use #2 for authorizing the request after successful authentication.

      #2 decides if the current client/request is authorized to access the resources it is requesting. And you may have a Role vs. Route mapping in an object, fetch it and include that as part of the validate request middleware. This will recheck the authorization part.

      In your case #1 should be equal to request.params.username. Then the user is said to be authorized. You can add this condition to your middleware along with the above. You can branch this logic into another middleware as not all routes would require this check.

      I think this would make the solution a bit tighter for all routes.

      Does that make sense?

      • ghzmdr

        Yes, it does, but in my application there’s only one role: user, which can only access it’s own resources, so for the time he’s authenticated he’s authorized.

        Every user has a field wich points via ObjectID to it’s root category, So he has no way of requesting a resource owned by someone else, thus application logic alone prevents unauthorized access.

        Eg: foo has a category named ‘quests’, bar has a category named ‘jewels’.
        foo can’t lookup bar’s ‘jewels’ since he has no pointer to the root category of bar, nor a way to gain it, since categories alone are not mapped to routes but only accessed via the user’s pointer. Basically he can only search inside it’s own category tree.

        • / Arvind Ravulavaru

          In this scenario, I guess your solution is fine. I cannot think of any other scenarios at this moment. If I get any I will let you know.

          Thanks for sharing.

  • Tsao

    Thanks for this great tutorial.However I have a question. If a hacker get the key and token,will he get authenticated and authorized successfully?

    • / Arvind Ravulavaru

      Thanks Tsao. Yes, it is as good as sharing your username and password with the hacker. But, one upside to JWT is that you can set the token to expire. If the token expires and the hacker sends the expired token to the server, he will get a 403 response. At this point the hacker would need a valid username and password to get a fresh token. Else the hacker is locked out of the system.

      Does that make sense?

  • Gluay

    Thank you for sharing. However I get the following error when I try to access http://localhost:3000/login

    What have I done wrong?

    Error: Not Found    at /tmp/server/server.js:28:11    at Layer.handle [as handle_request] (/tmp/server/node_modules/express/lib/router/layer.js:82:5)    at trim_prefix (/tmp/server/node_modules/express/lib/router/index.js:302:13)    at /tmp/server/node_modules/express/lib/router/index.js:270:7    at Function.proto.process_params (/tmp/server/node_modules/express/lib/router/index.js:321:12)    at next (/tmp/server/node_modules/express/lib/router/index.js:261:10)    at /tmp/server/node_modules/express/lib/router/index.js:603:15    at next (/tmp/server/node_modules/express/lib/router/index.js:246:14)    at Function.proto.handle (/tmp/server/node_modules/express/lib/router/index.js:166:3)    at router (/tmp/server/node_modules/express/lib/router/index.js:35:12)

    • / Arvind Ravulavaru

      Hello Gluay. Thanks!

      Are you trying to access

      from the browser?

      • Gluay

        I use RESTClient Firefox plugin

        • / Arvind Ravulavaru

          And you are making a POST request?

  • Josef

    One question Arvind. How can you send form data to mongodb using your code above?

    I have a DB model ‘Product’ and use your code.. Can you post an example how to save data from a form to mongoDB ? I cant get the controller to get the form data, push data to factory and then to products.js where it saves the product to DB.

    • / Arvind Ravulavaru

      Hello Josef, You can follow this tutorial MEAN stack – A Hands on Tutorial to see how to work with a MEAN stack application, where you save data from a Form to DB. Let me know if you still face issues. Thanks!

  • Sean

    Nice article, one of the things that I would’ve liked to see covered is the following. I have a secure REST endpoint that can be consumed by any device, as long as they have a token. We have a way to create tasks within the system. What prevents a hacker from using the rest API and calling a for loop and just filling out our database? Do you have maybe an alert that alerts you when someone is legitimately trying to screw you, and set some boundaries that you can make only this amount of requests per minute? What is your recommended solution of protecting against abuse of API?

    • / Arvind Ravulavaru

      Hello Sean, Thanks!

      A very nice question! IMO, the solution as you have suggested – Rate limiting – is very apt. You can set a threshold for every single client for number of API request per second or per minute depending on the expected traffic. Then you could use a module like node-rate-limiter or node-ratelimiter or simple-rate-limiter and wrap the authorization middleware with it.

      So the flow would be

      So if the rate is exceeded, we would send a HTTP 429 status code back. This way, we do not need to authorize the requests that are rate limited.

      Let me know if this helps.

      Thanks.

      • Sean

        This helps, thank you so much!! Great little node package

        • / Arvind Ravulavaru

          Yup. Thanks.

  • kc

    Hi Arvind,
    Very good article.Can you please tell how can i add more than 1 admin or create a profile for role of user?

    • / Arvind Ravulavaru

      Thanks kc. You can create your own role based registration and add users at various role. And the use the above auth middle to authenticate and authorise them.

  • Josef

    I following your tutorial and when i come to “Testing the REST API”, when trying to login i get

    Raw: {“status”:401,”message”:”Invalid credentials”}

    I have everything like you have in your files. Can this be a problem with express 4?

    • / Arvind Ravulavaru

      Hello Josef. Can you console log the credentials you are receiving at your server, just to check if the data is passed correctly?

      • Josef

        It looks like it’s fails on line 7 in auth.js – if statement

        if (username == ” || password == ”) {

        I have everything in server.js as you have in the tutorial. I saw you mentioned earlier in this thread about app.use(bodParser.json()); and i also check and its in the server.js to.

        • / Arvind Ravulavaru

          Strange. When you send the request from client, it does have the content-type : application/json right?

          • Josef

            Fixed! :)

          • / Arvind Ravulavaru

            Cool!

          • salesmen library

            Hi Josef, could you please share your solution? I am also having the same issue. when I do a console.log(‘username’+username); in routes/auth.json, i receive an empty value. Seems querystring params are not readable for me.

  • NewNoder

    Hi Arvind,
    Thank you very much for this excellent tutorial, it has helped me a great deal. I am trying to learn these technologies and was wondering if you could point me in the direction of an example which connects to a mysql database for the username/password check? I am about to start a project which is heavily reliant upon a mysql datastore. Once again, thanks for your work.

    • / Arvind Ravulavaru

      Hello NewNoder!,

      Thanks much. Take a look at node-mysql module. This module is specifically used for working with MySQL. Let me know if this works for you.

      Thanks.

      • NewNoder

        Thanks for pointing me in the right direction. I just can’t figure out how to use the mysql ‘pool’ (defined in server.js) inside auth.js. Is there some way I can ‘include’ it? Apologies if this is a stupid question. I am very new to javascript/node.

  • Auine .

    why you fetch user from db by username instead of token? Why we can’t save token to bd, and fetch user by it token?

    • / Arvind Ravulavaru

      Hello Auine,

      If you want to use, you can save the token to database whenever it is generated and then use that token to get the user data. But do remember to decode the token first to get the expiry value and check if the token is still valid and then fetch the user assigned with this token. Either way you cannot skip the decode token step.

      You can encode all the user data using JWT, but how will you decode on the client? If you are planning to implement a copy of JWT on client, then you will also need the secret with which you can decode the server side encoded user object and when you do this on client side, anyone who views the source can know all the security implementations you have done on the server side.

      That is why we do not encode the entire object.

      Does this makes sense?

  • Chris

    Hi Arvind, I can’t consume the login using postman, can you pls help me?

    • / Arvind Ravulavaru

      Hello Chris,

      Were you able to successfully create a new user? And was it with the same credentials above?

      Thanks.

      • Chris

        No, the problem is in auth.js, when i sent a request using post with username and password, the req.body.username or req.body.password is empty that’s why the server is giving me 401.

        • / Arvind Ravulavaru

          Did you add

          before the routes are called?

          • Chris

            Actually i used your code just for testing.

          • / Arvind Ravulavaru

            cool.

          • djohnsonkc

            The Postman solution is to add a header parameter for Content-Type: application/json. This has bit me before when testing using Postman :-)

          • Chris

            Arvind, I already solved the problem. Reason I’m passing invalid json.

          • Josef

            How did you solve this problem?

  • Alex

    Excellent article! Thank you!

  • Amine

    is it necessary to use gulp command ?? if yes , if we want to deploy the application will we find any problems ???
    please answer me because im using this project in prod

    • / Arvind Ravulavaru

      Hey Amine, you need gulp only to host the angular files locally. If you are already hosting the angular code on a server, you can access it directly.

  • http://julianalimin.com Julian

    Hi Arvind,
    Love your articles!

    I was trying to push these to Heroku. Has success with the Server by adding forever and a procfile.
    But I have no idea how to push the AngularJS Client to Heroku. Any pointers on what I should do?

    Thanks

    • / Arvind Ravulavaru

      Hey Julian, Thanks!

      Take a look at Deploying Node Applications to Heroku for deploying files to heroku.

      Also make sure you commit your changes before pushing changes to Heroku.

      Thanks.

  • T-Money

    Thanks for the great article…quick question: What size of apps would this architecture be suitable for? Is the architecture really all I need for a real world line of business app?

    • / Arvind Ravulavaru

      Thanks T-Money. Technically yes, and this architecture is quite scalable too. I have not tested this on clusters but I think it should work fine. This architecture can be used as a base for your app and you can build on top of it. But do not believe this 100%. Add your checks where possible.

      • T-Money

        Thank you for your response!

  • p.florian91

    Thank you! Flawless

  • Davids

    Excelent Article Arvind, thanks a lot, this may be my nodejs app foundments for a long. Just a question, i am new in writing apps in nodejs, do this rest app implement the mvc pattern ?

    Thanks a lot Again !

    • / Arvind Ravulavaru

      Hello David. Thanks!

      We are using Express.js (http://expressjs.com/ ) as the web framework. This acts as the MVC web framework for the app.

      Thanks.

  • Mercea

    Hi Arvind,

    Thanks for such a well written tutorial! I love it.

    In my validaterequest.js file, which looks exactly like yours, on line 65, I keep getting this error. It also happens if I include a status in the response (line 64). The error then would be “IncomingMessage> has no method ‘status'”. Any ideas why? Setting the status and json response works for the routes but doesn’t work in my validaterequest file.

    Thanks.

    • / Arvind Ravulavaru

      Hello Mercea,

      Thanks! Glad you liked it.

      I have not faced a issue like this so far. But a quick googling suggests

      Another reason could be

      Let me if it works out.

      Thanks.

      • mercea

        Thanks a lot! I had the req and res switched. Bad idea to code when tired :)

        • / Arvind Ravulavaru

          Glad it worked out. Thanks.

  • Marcus

    Hi Arvind,

    Thank you so much for the nice written article! I was looking for a template like this for my experimental node.js REST API. Awesome :)

    • / Arvind Ravulavaru

      Great! Thanks Marcus.

  • Cerberus

    Hey Arvind,

    Thanks for the article. I’m a bit new to node and angular and I was just curious about the statement:

    return config || $q.when(config);

    I’ve tried examining what’s returned by the $q.when() and it doesn’t seem like anything useful… I’m currently looking into $q and $http intercepters, so forgive my ignorance.

  • Cyber

    Thank you for your time devoted to write this article. Would love to see more of some advance stuffs in nodejs rest api.

    • / Arvind Ravulavaru

      Thanks Cyber! Are you looking for anything specific?

  • Abdurrahman

    Hello Arvind,

    Thanks for the great article. But I get an error below when I try to post a request from PostMan extension. Do you have any idea that how I can handle it?

    • / Arvind Ravulavaru

      Hi Abdurrahman, Thanks!

      Is that route defined in your code? any sample code I can take a look at. Thanks.

      • Abdurrahman

        Actually I’m running your repo on my local.
        https://github.com/arvindr21/myRESTApp

        • / Arvind Ravulavaru

          So, you cloned the repo. Ran npm install inside the server folder, then init server with node server.js and then tried to access http://localhost:3000/api/v1/products without any request headers right?

          You should ideally get a 401. Can you pl. try cloning the repo and running again?

          Thanks.

          • Abdurrahman

            Ok, I found the problem. I’ve forgotten request method as POST, changing it to GET resolved my issue.Thanks again for the quick reply and great article Arvind.

          • / Arvind Ravulavaru

            Great! Thanks!

  • Christian Martin

    Thanks for the tutorial! I really appreciate it! By the way, the username should be baked into the token instead of the x-key. Now you can easily change it by typing sessionStorage.user= “[email protected]” in the console.

    • / Arvind Ravulavaru

      Thanks Christian. Yes, that is another good/a more secure way to handle it.

      We can always do the below in routes/auth.js

      But, on the client, you will not know what is in the token, as we are not decoding the same. And the only reason we store the username on the client is to handle the page refresh and maintain the login of the client. You can use a cookie too to do this.

  • Ward Bell

    I forgot to mention perhaps the most important omission: you’re not encrypting at the transport level with HTTPS. All communications are in clear text … including the login credentials. Any man-in-the-middle (say, someone listening in a coffee shop) can pick up the credentials or the the JWT token (good for 7 days).

    It’s not hard to turn on TLS (HTTPS). Search the web; here’s an example: http://www.hacksparrow.com/express-js-https.html

    Note also that EVERY exchange with the site must be via HTTPS, not just the login. Search the web for why; here’s one such explanation: http://mashable.com/2011/05/31/https-web-security/ I quote:

    “To be clear, sniffing attacks don’t need to grab your password in order to impersonate you. Web apps that use HTTPS for authentication protect your password. If they use regular HTTP after you log in, they’re not protecting your privacy or your temporary identity.”

    • / Arvind Ravulavaru

      Very valid comment. I have added this info to the post itself. Thanks for pointing it out.

  • Ward Bell

    Thanks for this abundantly clear example. I do think there are two danger points: (1) clear text user name and (2) home-brew identity management.

    1. User name concern

    Why do you respect the clear-text value of the user coming from the client. Couldn’t a rogue client who had authenticated as a non-admin easily poke in the user name of an admin … and thus be granted admin rights?

    It seems to me that the user name (user_id, whatever one uses to find the app authorization) must be encrypted in the JWT. The conventional place for that is as the {sub: user_id} member; see https://docs.auth0.com/jwt for example which also recommends other settings for conventional claims (see http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#RegisteredClaimName).

    2. Identity Management

    I understand that your app (your database) is holding username and password for the simplicity of this example. I would use a real identity provider myself. Roll-your-own authentication is dangerous, IMO and the opinion of folks with far more experience than I have.

    • / Arvind Ravulavaru

      Hello Ward,

      To clarify a few things,

      1. This article is just one of the generic solution on how one can go about implementing a secure REST API.
      2. To overcome the username concern, apart from encrypting the user_id, you can send in the logged in user’s UID (1fa2fae0f7a34f8da68fa252f43561e8) along with the username. If this is also a concern, you can work with a private and public key concept.
      3. Completely agree on IDM. If the app is built for a small ideas, then IDM will be an overhead. So DB would suffice. If you already have an Active Directory available, you can always plug it in.

      Again, this is just one of the generic solutions.

      + Thanks for initiating a discussion on this!

      • Ward Bell

        “Initiating discussion” is really my point. You’ve demystified many concepts and mechanics by simplifying. Whether one should actually follow this prescriptively is another matter.

        Curious that you didn’t mention passport (http://passportjs.org/) which has become a popular (and easy) node module for authentication.

        • / Arvind Ravulavaru

          True that. From a bird’s eye view, this is more of an awareness on how you can go about secure REST API. A few of these concepts may be helpful when one architect the apps for their startups.

          I wanted this example to be a “from the scratch design and development”. I would rather leave the decision (of including passport or other IDMs) to the Architect!

  • acveer

    Awesome article Arvind. I applaud your careful attention for laying out things clearly with line numbers. I am a huge fan of your articles as they provide clear & quick intro to web technologies.

    Every API request from the “ngclient” is actually making 2 requests to the REST app, for example, if the client is making login request, the REST app has 2 calls in the log.
    OPTIONS /login 200 0.923 ms – 2
    POST /login 200 27.115 ms – 211

    Although there are no errors for POST, GET requests are followed with this error, for ex:
    For getProducts, I was getting this error on the OPTIONS call
    OPTIONS /api/v1/products 200 0.414 ms – 3
    Error: Can’t set headers after they are sent.
    GET /api/v1/products 200 1.939 ms – 121

    After some goggling, I have added the below code to line#17 in server.js resolve this issue. What do you think about it ?

    if (req.method == ‘OPTIONS’) {
    res.send(200);
    }
    else {
    next();
    }

    • / Arvind Ravulavaru

      Hello acveer. Thanks! Glad you like it.

      If you do notice our API server is on port 3000 and the client is on 2772. This is Cross Origin Resource Sharing scenario. And CORS specs dictate that the client (a web browser in this case) would need to make a pre-flighted request to the server to check if the resource it is trying access is safe. This would be of the type OPTIONS. And once this is a success, we fire the actual API call. That is why you see 2 calls made for fetching a single resource.

      Pls. refer here for more info on Preflighted requests : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

      If you do notice Line 11 of server/middlewares/validateRequest.js we have added

      This is to skip authentication/authorization for the requests of type OPTIONS and directly dispatch a 200. I did not observe the errors on the console – my bad.

      And yes, what you have pointed out should be there to avoid the warning/error and dispatch the response even earlier. Thanks for pointing that out. I have updated this post/code accordingly.

      Thanks.

      • acveer

        Thank you Arvind for the info. on CORS. Did you notice, now the requests are made twice for each call from the client. ex:
        OPTIONS /api/v1/products 200 0.275 ms – –
        OPTIONS /api/v1/products 200 0.332 ms – –
        GET /api/v1/products 200 0.627 ms – 91
        GET /api/v1/products 200 0.517 ms – 91

        It looks like the browser itself is making the duplicate calls. could not source out why. (also tried your code at github, same issue.)

        For my curiosity sake, I wanted to see, if we can avoid pre-flight CORS requests, by adding the access_token in the url and removing it from the header, but no luck here. Do you know any way to avoid pre-flight CORS requests ( reducing calls to increase performance )

        • / Arvind Ravulavaru

          Hello acveer. Did some quick testing and I did notice that. Not sure why the 2 requests. Let me see if I can debug this. Thanks.

  • Omar

    Great article. Line 24 of server.js why do you pass ‘bodyParser.json()’ since it’s already being used on line 9 ‘app.use(bodyParser.json());’

    • / Arvind Ravulavaru

      Thanks and nice catch Omar. Fixed it. (probably a copy paste error).

  • Jean-Francois Bouzereau

    Hello, in index.js, the products.delete and user.delete routes are wrong, they should be declared with a router.delete command.

    • / Arvind Ravulavaru

      Thanks Jean. Fixed it.