Tutorial: Securing API Endpoints

Inspecting The Code

Integrating PassportJS in to our API is a significantly easier task than developing a new authentication mechanism from scratch.  Let’s take a look at how to integrate PassportJS in to an API.  All code for this API is included in YourAPIExpert’s GitHub repository.

The package.json file
{
  "name": "002",
  "version": "1.0.0",
  "description": "YourAPIExpert.com Restify JWT Authentication Template",
  "main": "server.js",
  "dependencies": {
    "bunyan": "^1.8.5",
    "bunyan-syslog": "github:mcavage/node-bunyan-syslog",
    "config": "^1.24.0",
    "jsonwebtoken": "^7.2.1",
    "passport": "^0.3.2",
    "passport-jwt": "^2.2.1",
    "passport-local": "^1.0.0",
    "restify": "^4.3.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "YourAPIExpert <yourapiexpert@gmail.com>",
  "license": "ISC"
}

Lines 11 o 13 are the ones of interest and show how the main PassportJS middleware is included as well as the JWT and ‘local’ strategies.

Integrating PassportJS With RESTify

To make use of PassportJS within our RESTify API we need to advise the framework to include PassportJS as middleware.  This is accomplished by the ‘use’ directives as seen below in the server.js file.

  server.use(passport.initialize());
  server.use(passport.session());

These two statements located on lines 115 and 116 of server.js instructs the RESTify framework to make use of the PassportJS middleware and to initialize it.

Also note the previous declarations on line 26 which includes the library in to the API.

Making Use Of PassportJS In A Controller

For this API I’ve designed a separate controller (in controllers/auth.js) to demonstrate the relative ease at which PassportJS can be used to provide authentication.

// The Essentials
var passport = require('passport'); // Authentication and authorization
var LocalStrategy = require('passport-local').Strategy; // Local passport strategy
var JwtStrategy = require('passport-jwt').Strategy; // JWT passport strategy
var ExtractJwt = require('passport-jwt').ExtractJwt; // Various JWT extraction methods
var jwt = require('jsonwebtoken'); // Jason Web Tokens
var config = require('config'); // Easy configuration file parser
var crypto = require('crypto'); // Cryptographic library

// Configuration
var jwtConfig = config.get('jwt');

There’s a few things going on here in the inclusion of libraries.  Between lines 16 and 18 we detail the necessary inclusions for our ‘local’ and JWT passport strategies.  On line 19 we include a convenient library which helps us to create and sign JWT tokens and on line 20 another convenient library to extract the token information.

Other known libraries help us to read the configuration file as well as perform cryptographic functions.  Our configuration file, as a matter of interest, contains a secret key used to sign the web tokens.

{
  "general": {
        "service": {
                "name":"002auth"
        }
  },
  "connections": {
    "http": {
      "address": "0.0.0.0",
      "port": 8085
    },
    "sysLog": {
        "host": "localhost",
        "port": 514
    }
  },
  "jwt": {
    "secretKey":"|}9yY),]Wtv^H=##Nor#{\/!g%;@}=r",
    "tokenValidity": "1200"
  }
}

Let’s take a look at how to define a PassportJS strategy.

/** Serialize the user information in to an object */
passport.serializeUser(function(user,done){
    done(null,user);
});

/** De-serialize the user from an object */
passport.deserializeUser(function(user,done){
    done(null,user);
});

/** Configure the local passport strategy
  *
  * This is very rudementary with hard coded credentials to say the least.
  * Ideally one would veryify a hashed version of these credentials against 
  * a database or some other sort of authentication backend before signing
  * a token */
passport.use(new LocalStrategy(
    function(username, password, done) {
      // Test the access credentials
      if (username === "testuser" && password === "testpassword") {
        // They check out OK.  Let's go ahead and format a dummy payload and sign it with our JWT.
        var payload = {username: username, password:password, otherText: 'This is a test message'};
        jwt.sign(payload, jwtConfig.secretKey, { algorithm: 'HS256', expiresIn:parseInt(jwtConfig.tokenValidity), issuer: 'http://www.yourapiexpert.com', audience: 'http://www.yourapiexpert.com', subject:username}, function(err, token) {
                if (err) {
                   return done(true, null);
                } else {
                   return done(null, {token: token});
                }
        });

      } else {
        return done(true, null);
      }
    }));

Between lines 44 and 77 we set up the ‘local’ strategy.  The functions to serialize and deserialize pertain to how the user object is constructed and deconstructed which, for now, is straightforward in our API.  We simply return the user object as-is.

On line 63 we perform a static check of the user’s credentials and, if matching, we format a small payload and include it inside our JSON web token which is then signed with our secret key.  For this we’ve chosen an HMAC256 symmetric encryption scheme.

Because of PassportJS’s versatility we can make use of multiple authentication strategies within an API.  Let’s take a look at how we’ve made use of the ‘JWT’ strategy.

/** Configure the JWT passport strategy
  *
  * To demonstrate passport's flexibility this function works with the
  * verification of JSON web tokens.  To be somewhat compatible with
  * oAUTH and Bearer tokens we will have this function extract the 
  * JWT from the authorization header */
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = jwtConfig.secretKey;
opts.issuer = 'http://www.yourapiexpert.com';
opts.audience = 'http://www.yourapiexpert.com';
opts.passReqToCallback = true;
passport.use(new JwtStrategy(opts, function(req, jwt_payload, done) {
      // Test for successful verification
      if (jwt_payload.hasOwnProperty('username')) {
         return done(null,jwt_payload);
      } else {
         return done(null, false);
      }

}));

Between lines 80 and 100 we set up the ‘JWT’ strategy.  Note how on line 87 we specify that the JWT token is to be extracted from the header and verified using the key included in the configuration file.  We determine the token to be valid when we test for the ‘username’ property within the payload.

Making Use Of The Strategies In Endpoints

Once the strategies have been defined we can make use of them in our routes as shown below.

 /** Exchanges basic credentials for a JWT token */
 app.post('/auth/login', function(req, res, next) { passport.authenticate('local',
    function(err, user, info) {
       if (!user.hasOwnProperty('token')) {
          return res.send(401, {error:{code: 401, status: 'ACCESSDENIED', message: 'Access denied'}});
       } else {
          /** Inject the user object or anything else in to the request */
          req.user = user;
          auth.getToken(req, res, next);
       }

    })(req,res,next);
 });

Between lines 144 and 156 we make use of the ‘local’ strategy for our /auth/login endpoint and a similar definition exists for the /user/profile endpoint.  On successful authentication the strategy populates a user object through our serialization routine which we test for in this endpoint.  If it exists we execute the function to generate a token.

The functions from here on are pretty self explanatory with the code comments doing most of the talking.  Take some time to explore it.

Up Next: Executing The API

Pages: 1 2 3 4 5

Written by YourAPIExpert