Wednesday, February 22, 2017

Secure your Node/Express REST APIs using Passport JS

Recently I have been involved in various discussions on how to make the REST APIs as secure as SOAP.
First of all, let me start with a very basic statement, about security, it doesn't depend on your Webservice type, be it REST or SOAP, your design decisions depicts whether they can be made secure or not.
In this example blog, I will use "Micro CRUD services for Oracle Database Cloud" APIs and implement (read attach) Passport's "local" authentication strategy, to make them secure.

Code in Github : LeasifyAPIs with Passport

What is Passport JS? (from Documentation)

Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When writing modules, encapsulation is a virtue, so Passport delegates all other functionality to the application. This separation of concerns keeps code clean and maintainable, and makes Passport extremely easy to integrate into an application.

What are "strategies" in Passport JS? 

"Strategies" are different techniques to authenticate an user, who is accessing the hosted REST APIs. Be it, authenticating using LDAPs, Identity stores, OAuth, social (Facebook, Google or LinkedIn) etc. Passport provides around 307 different strategies to choose from. Check out their website for more info : http://passportjs.org/

What is it I'm going to "attach"?

I mentioned "attach", not implement. Because, I am going to :
  • Add 2 Node Modules
  • Initialize passport for a "strategy"
  • Add a /login API for users to authenticate
  • Add one extra parameter to the existing API definitions to make them secure.
So basically, the existing APIs can be made secure just by "attaching" one extra parameter.

Authentication mechanism

Let me explain, my authentication mechanism a bit.

"salt" and "hash"
I am going to authenticate the users, from database. I have a table containing all the users. But I don't store passwords, instead, I store "salt" and "hash".
The "salt" is a string of characters unique to each user.
The "hash" is created by combining the password provided by the user and the salt, and then applying one-way encryption.
As the hash cannot be decrypted, the only way to authenticate a user is to take the password, combine it with the salt and encrypt it again. If the output of this matches the hash, then the password must have been correct. Resulting in a simple yet very secure implementation.

JSON Web Token
Once, I authenticate the users, via an API, I return a JSON Web Token. Which has a validity of 7 days, and needs to used in the header for all other API calls.

What is JSON Web Token, aka JWT? 
Instead of supplying credentials such as a username and password with every request, we can allow the client to exchange valid credentials for a token. This token gives the client access to resources on the server. Tokens are generally much longer and more obfuscated than a password. For example, the JWTs we’re going to be dealing with are on the order of ~150 characters. Once the token is obtained, it must be sent with every API call.
Think of the token like a security pass. You identify yourself at the front desk of a restricted building on arrival (supply your username and password), and if you can be successfully identified you’re issued a security pass. As you move around the building (attempt to access resources by making calls to the API) you are required to show your pass, rather than go through the initial identification process all over again.

So my implementation consists of these 3 major parts :
  1. /addUser : API which adds a new user to the database. This API is also responsible to generates salt and hash based on the password provided, and stores it in the DB.
  2. DB Table : Users, with 3 important columns. Email (my username), salt and hash.
  3.  /login : API which takes email and password and returns JSON Web Token, this needs to be in the header for all the calls to secured APIs.
Code examples 
users.js
A simple JS model for user object.
Some of the important implementation : "setPassword"

var crypto = require('crypto');

User.prototype.setPassword = function(password){
  this.salt = crypto.randomBytes(16).toString('hex');
  this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};


"validate password" :
User.prototype.validPassword = function(password) {
  var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
  return this.hash === hash;
};


Nothing needs installing as crypto ships as part of Node. Crypto itself has several methods; we’re interested in randomBytes to create the random salt and pbkdf2Sync to create the hash (there’s much more about Crypto in the Node.js API docs).

"generate JWT" :
var jwt = require('jsonwebtoken');

User.prototype.generateJwt = function() {
  var expiry = new Date();
  expiry.setDate(expiry.getDate() + 7);

  return jwt.sign({
    _id: this._id,
    email: this.email,
    name: this.name,
    exp: parseInt(expiry.getTime() / 1000),
  }, "MY_SECRET");
};


To create the JWT we’ll use a module called jsonwebtoken which needs to be installed in the application (use npm install for this).
This module exposes a sign method that we can use to create a JWT, simply passing it the data we want to include in the token, plus a secret that the hashing algorithm will use. The data should be sent as a JavaScript object, and include an expiry date in an exp property.
It is important that your secret ("MY_SECRET") is kept safe – only the originating server should know what it is. It is best practice to set the secret as an environment variable, and not have it in the source code, especially if your code is stored in version control somewhere.

passport configurations 

To use Passport, first install it and the strategy, saving them in package.json.

npm install passport --save
npm install passport-local --save

create a file "passport.js" and put the following code

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./users');
var oracledb = require('oracledb');
var dbutil = require('../dbutil');

passport.use(new LocalStrategy({
    usernameField: 'email'
  },
  function(username, password, done) {
     
      dbutil.executeDatabaseOperation(function(error) {
          return done(err);
      }, function(connection) {
          var selectStatement = "SELECT NAME, EMAIL, HASH, SALT FROM USERS WHERE EMAIL= :userEmail";
          connection.execute(   selectStatement  
            , [username], {outFormat: oracledb.OBJECT // Return the result as Object
            }, function (err, result) {
                if (err) {
                  console.log('Error in execution of select statement'+err.message);
                  return done(err); 
                } else {
                   console.log('db response is ready '+result.rows);
                   if(result.rows.length === 0) {
                       return done(null, false, {
                          message: 'User not found'
                       });
                   }
                   dbutil.doRelease(connection);
                  
                   var user = new User(result.rows[0].NAME, result.rows[0].EMAIL);   
                   user.setHash(result.rows[0].HASH);
                   user.setSalt(result.rows[0].SALT);
                    // Return if password is wrong
                   if (!user.validPassword(password)) {
                    return done(null, false, {
                      message: 'Password is wrong'
                    });
                   }
                   // If credentials are correct, return the user object
                   return done(null, user);
                }               
              }
          );
      });   
  }
));


The above code, will retrieve the "user" from database using the email, and then will validate the password passed to this function, eventually return the user object (which we'll use a little bit late to return the JWT).

So, now I have a implemented the "local" strategy to validate a username and password. Time to write an "authenticator" which will use this strategy to validate users.

authentication.js

var passport = require('passport');
var User = require('./users');

var sendJSONresponse = function(res, status, content) {
  res.status(status);
  res.json(content);
};

module.exports.login = function(req, res) {

  if(!req.body.email || !req.body.password) {
     sendJSONresponse(res, 400, {
       "message": "All fields required"
     });
     return;
  }

  passport.authenticate('local', function(err, user, info){
    var token;

    // If Passport throws/catches an error
    if (err) {
        console.log(err);
      res.status(404).json(err);
      return;
    }

    // If a user is found
    if(user){
      token = user.generateJwt();
      res.status(200);
      res.json({
        "token" : token
      });
    } else {
        console.log(info);
      // If user is not found
      res.status(401).json(info);
    }
  })(req, res);

};


Now I have to link this to /login endpoint. So, in "/routes/index.js", I will add the following :

var ctrlAuth = require('./authenticate/authentication');

router.post('/login', ctrlAuth.login);


The "login" will return the JWT in the response. Time to secure rest of the REST apis.

First initialize passport in "app.js"

var passport = require('passport');
require('./routes/authenticate/passport');


app.use(passport.initialize());

Then, in "/routes/index.js", initialize JWT module. 

var jwt = require('express-jwt');
var auth = jwt({
  secret: 'MY_SECRET',
  userProperty: 'payload'
});


Again, don’t keep the secret in the code! Even though I did :P

To apply this middleware simply reference the function in the middle of the route to be protected, like this: 
router.get('/api/getUser', auth, function(req, res, next)...

So, the endpoints which were defined earlier as :
router.<method>('/endpoint', function(req, res, next) {})...

Will be now :
router.<method>('/endpoint', auth, function(req, res, next) {})...

Also, add a "unauthorized" error handler, to validate our implementation

app.use(function(err, req, res, next) {
   
  if (err.name === 'UnauthorizedError') {
    res.status(401);
    res.json({"message" : err.name + ": " + err.message});
  } else { 
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
  }
});


All done. You should run the application and check the endpoint "/api/catalogue". It has all the details on how to invoke all the apis.
Ideally :
1. Invoke /addUser : to create a new user.
2. Invoke /login : to generate the JWT token.
3. Invoke other APIs using that JWT token.

Happy coding!!

References :

Tuesday, December 13, 2016

Chatbots and Oracle Cloud Services

Thanks to Oracle A-Team, I had a chance to work with Chatbots.
3 pure NodeJS applications, on couple of Oracle Cloud platforms and Facebook messenger, and my chatbot was running.

Let me explain, the architecture a bit. To start with, following is the simple representation of how it works.





Message Platform Server : Is a NodeJS application, deployed on Oracle Application Container cloud, acts as a channel between Facebook Messenger and the chatbot engine. It simply converts the incoming messages from Facebook and sends it to chatbot readable format. Also, when chatbot replies, it converts to Facebook readable formats and passes it to messenger.

Chatbot Engine : Is a NodeJS application, which communicate with some REST APIs based on a conversation flow document and moves the flow of the conversation from one state to another.

Flow JSON : Where we document, every state of a conversation and which APIs to call to generate a response. For example, at the beginning of the conversation, start from "menu" state, and call "/start" API. The flow metadata file is driving the behavior of the bot engine.  The bot engine uses a finite-state-machine (FSM) to drive the conversation. Every step in the conversation is modeled as a state, and all possible next steps to move the conversation to a next state are defined as state transitions.  Every time a state is entered, the response elements defined for this state in the flow metadata are processed and the response is constructed and returned to the messaging platform.

Component APIs : Where several microservices are running, In my case I create a set of APIs, named Airport API which returns, starting from get flight info based on flight number, where is the check-in gate, baggage information, delayed flights etc.

Every component api should have a flow json, which defines that state transitions in the conversation and which APIs to call inorder to generate the response of the chat.

Now a bit more details about Component APIs and Flow JSON relationship.
In my case, Airport API, so I will explain my flow json in context to that.

First of all, below is the structure of each "state" in the flow json.


Each state has a "stateName", which generates a "response", using one or multiple component APIs. Optionally, it can also provides some options, which user can choose to further communicate with the chatbot.
Each of these options, generates an Event, which is mapped to another "state".

There is a special "state" called "stateTransitionError", it gets invoked when chatbot engine cannot translate/understand user input and it gives user option to navigate to the "start" state.

This is a simple, state transition map, only depicting a single flow.






The flow is written in Mustache(https://mustache.github.io/mustache.5.html). It is a simple “logic-less” template engine. It works by expanding tags in a template using values provided in a JSON object. It is "logic-less" because there are no if statements, else clauses, or for loops. Instead there are only tags. Some tags are replaced with a value, some nothing, and others a series of values. A state definition in mustache looks like this :

{"name": "flightDetails", "type" : "response"
             ,"responseItems" :
            [ { "type":"prompt", "languages":
                 [{"language":"en","prompt": "Your flight departs at {{flowScope.departureTime}} , {{#isDelayed}} And, it looks like, your flight has reported a delay in departure. Sorry about that. {{/isDelayed}}"}
                 ]
              }
              ,{"type":"prompt","languages":
                 [{"language":"en","prompt": "Do you want, the following info on your flight?"}]
                ,"options" :
                     [{"payload" : { "event" : "immigrationInfo" }
                        ,"languages" :
                          [{"language": "en", "prompt": "Immigration Info"}]  
                       }
                      ,{"payload" : { "event" : "checkinInfo" }
                        ,"languages" :
                          [{"language": "en", "prompt": "Check-in Counter/Gate"}]  
                       }
                      ,{"payload" : { "event" : "onlineCheckin" }
                        ,"languages" :
                          [{"language": "en", "prompt": "Online Check-in URL"}]  
                       }
                      ] 
                       }
            ]
            ,"componentServices" : [{"name" : "getFlightDepartureTime"
                               ,"inputParams":[{"name" : "flightNo", "value": "{{flowScope.enterFlightNo}}"}]
                               ,"outputParams": [{"name" : "departureTime","scope": "flow"}]
                               }
                               ,{"name" : "getIsFlightDelayed"
                               ,"inputParams":[{"name" : "flightNo", "value": "{{flowScope.enterFlightNo}}"}]
                               ,"outputParams": [{"name" : "isDelayed","scope": "request"}]
                               }
                              ]
       }
 


Regarding the Flow, a multiple approach can be used, rather than writing a huge JSON document :
  1. Use Oracle Policy Automation to write the rules and state transitions
  2. Use Oracle BPM to define human taskflows
  3. Use a custom application where admin/business users can define the states and transitions, as well as pick up APIs from a catalog.
Either way, we have to make sure the end result is readable by the chatbot engine.

Here is a GIF of the running bot :




According to Oracle A Team, Oracle will soon release an intelligent bot cloud, which will be used then to replace the messaging platform server and chatbot engine.