Home Mobile Tutorials Building the Middle-Tier

Building the Middle-Tier

0
3761
34 min read

In this article by Kerri Shotts , the author of the book PhoneGap for Enterprise covered how to build a web server that bridges the gap between our database backend and our mobile application.

If you browse any Cordova/PhoneGap forum, you’ll often come across posts asking how to connect to and query a backend database. In this article, we will look at the reasons why it is necessary to interact with your backend database using an intermediary service. If the business logic resides within the database, the middle-tier might be a very simple layer wrapping the data store, but it can also implement a significant portion of business logic as well. The middle-tier also usually handles session authentication logic.

Learn Programming & Development with a Packt Subscription

Although many enterprise projects will already have a middle-tier in place, it’s useful to understand how a middle-tier works, and how to implement one if you ever need to build a solution from the ground up.

In this article, we’ll focus heavily on these topics:

  • Typical middle-tier architecture
  • Designing a RESTful-like API
  • Implementing a RESTful-like hypermedia API using Node.js
  • Connecting to the backend database
  • Executing queries
  • Handling authentication using Passport
  • Building API handlers

You are welcome to implement your middle-tier using any technology with which you are comfortable. The topics that we will cover in this article can be applied to any middle-tier platform.

Middle-tier architecture

It’s tempting, especially for simple applications, to have the desire to connect your mobile app directly to your data store. This is an incredibly bad idea, which means your data store is vulnerable and exposed to attacks from the outside world (unless you require the user to log in to a VPN). It also means that your mobile app has a lot of code dedicated solely to querying your data store, which makes for a tightly coupled environment. If you ever want to change your database platform or modify the table structures, you will need to update the app, and any app that wasn’t updated will stop working. Furthermore, if you want another system to access the data, for example, a reporting solution, you will need to repeat the same queries and logic already implemented in your app in order to ensure consistency.

For these reasons alone, it’s a bad idea to directly connect your mobile app to your backend database. However, there’s one more good reason: Cordova has no nonlocal database drivers whatsoever.

Although it’s not unusual for a desktop application to make a direct connection to your database on an internal network, Cordova has no facility to load a database driver to interface directly with an Oracle or MySQL database. This means that you must build an intermediary service to bridge the gap from your database backend to your mobile app.

No middle-tier is exactly the same, but for web and mobile apps, this intermediary service—also called an application server—is typically a relatively simple web server. This server accepts incoming requests from a client (our mobile app or a website), processes them, and returns the appropriate results. In order to do so, the web server parses these requests using a variety of middleware (security, session handling, cookie handling, request parsing, and so on) and then executes the appropriate request handler for the request. This handler then needs to pass this request on to the business logic handler, which, in our case, lives on the database server. The business logic will determine how to react to the request and returns the appropriate data to the request handler. The request handler transforms this data into something usable by the client, for example, JSON or XML, and returns it to the client.

The middle-tier provides an Application Programming Interface (API). Beyond authentication and session handling, the middle-tier provides a set of reusable components that perform specific tasks by delegating these tasks to lower tiers. As an example, one of the components of our Tasker app is named get-task-comments. Provided the user is properly authenticated, the component will request a specific task from the business logic and return the attached comments. Our mobile app (or any other consumer) only needs to know how to call get-task-comments. This decouples the client from the database and ensures that we aren’t unnecessarily repeating code.

The flow of request and response looks a lot like the following figure:

Designing a RESTful-like API

A mobile app interfaces with your business logic and data store via an API provided by the application server middle-tier. Exactly how this API is implemented and how the client uses it is up to the developers of the system. In the past, this has often meant using web services (over HTTP) with information interchange via Simple Object Access Protocol (SOAP).

Recently, RESTful APIs have become the norm when working with web and mobile applications. These APIs conform to the following constraints:

  • Client/Server: Clients are not concerned with how data is stored, (that’s the server’s job), and servers are not concerned with state (that’s the client’s job). They should be able to be developed and/or replaced completely independently of each other (low coupling) as long as the API remains the same.
  • Stateless: Each request should have the necessary information contained within it so that the server can properly handle the request. The server isn’t concerned about session states; this is the sole domain of the client.
  • Cacheable: Responses must specify if they can be cached or not. Proper management of this can greatly improve performance and scalability.
  • Layered: The client shouldn’t be able to tell if there are any intermediary servers between it and the server. This ensures that additional servers can be inserted into the chain to provide caching, security, load balancing, and so on.
  • Code-on-demand: This is an optional constraint. The server can send the necessary code to handle the response to the client. For a mobile PhoneGap app, this might involve sending a small snippet of JavaScript, for example, to handle how to display and interact with a Facebook post.
  • Uniform Interface: Resources are identified by a Uniform Resource Identifier (URI), for example, https://pge-as.example.com/task/21 refers to the task with an identifier of 21. These resources can be expressed in any number of formats to facilitate data interchange. Furthermore, when the client has the resource (in whatever representation it is provided), the client should also have enough information to manipulate the resource. Finally, the representation should indicate valid state transitions by providing links that the client can use to navigate the state tree of the system.

There are many good web APIs in production, but often they fail to address the last constraint very well. They might represent resources using URIs, but typically the client is expected to know all the endpoints of the API and how to transition between them without the server telling the client how to do so. This means that the client is tightly coupled to the API. If the URIs or the API change, then the client breaks.

RESTful APIs should instead provide all the valid state transitions with each response. This lets the client reduce its coupling by looking for specific actions rather than assuming that a specific URI request will work. Properly implemented, the underlying URIs could change and the client app would be unaffected. The only thing that needs to be constant is the entry URI to the API.

There are many good examples of these kinds of APIs, PayPal’s is quite good as are many others. The responses from these APIs always contain enough information for the client to advance to the next state in the chain. So in the case of PayPal, a response will always contain enough information to advance to the next step of the monetary transaction. Because the response contains this information, the client only needs to look at the response rather than having the URI of the next step hardcoded.

RESTful APIs aren’t standardized; one API might provide links to the next state in one format, while another API might use a different format. That said, there are several attempts to create a standard response format, Collection+JSON is just one example. The lack of standardization in the response format isn’t as bad as it sounds; the more important issue is that as long as your app understands the response format, it can be decoupled from the URI structure of your API and its resources. The API becomes a list of methods with explicit transitions rather than a list of URIs alone. As long as the action names remain the same, the underlying URIs can be changed without affecting the client.

This works well when it comes to most APIs where authorization is provided using an API key or an encoded token. For example, an API will often require authorization via OAuth 2.0. Your code asks for the proper authorization first, and upon each subsequent request, it passes an appropriate token that enables access to the requested resource.

Where things become problematic, and why we’re calling our API RESTful-like, is when it comes to the end user authentication. Whether the user of our mobile app recognizes it or not, they are an immediate consumer of our API. Because the data itself is protected based upon the roles and access of each particular user, users must authenticate themselves prior to accessing any data.

When an end user is involved with authentication, the idea of sessions is inevitably required largely for the end user’s convenience. Some sessions can be incredibly short-lived, for example, many banks will terminate a session if no activity is seen for 10 minutes, while others can be long-lived, and others might even be effectively eternal until explicitly revoked by the user. Regardless of the session length, the fact that a session is present indicates that the server must often store some information about state. Even if this information applies only to the user’s authentication and session validity, it still violates the second rule of RESTful APIs.

Tasker’s web API, then, is a RESTful-like API. In everything except session handling and authentication, our API is like any other RESTful API. However, when it comes to authentication, the server maintains some state in order to ensure that users are properly authenticated.

In the case of Tasker, the maintained state is limited. Once a user authenticates, a unique single-use token and an Hash Message Authentication Code (HMAC) secret are generated and returned to the client. This token is expected to be sent with the next API request and this request is expected to be signed with the HMAC secret. Upon completion of this API request, a new token is generated. Each token expires after a specified amount of time, or can be expired immediately by an explicit logout.

Each token is stored in the backend, which means we violate the stateless rule. Our tokens are just a cryptographically random series of bytes, and because of this, there’s nothing in the token that can be used to identify the user. This means we need to maintain the valid tokens and their user associations in the database. If the token contained user-identifiable information, we could technically avoid maintaining state, but this also means that the token could be forged if the attacker knew how tokens were constructed. A random token, on the other hand, means that there’s no method of construction that can fool the server; the attacker will have to be very lucky to guess it right. Since Tasker’s tokens are continually expiring after a short period of time and are continually regenerated upon each request, guessing a token is that much more difficult. Of course, it’s not impossible for an attacker to get lucky and guess the right token on the first try, but considering the amount of entropy in most usernames and passwords, it’s more likely that the attacker could guess the user’s password than they could guess the correct token.

Because these tokens are managed by the backend, our Tasker’s API isn’t truly stateless, and so it’s not truly RESTful, hence the term RESTful-like. If you want to implement your API as a pure RESTful API, feel free. If your API is like that of many other APIs (such as Twitter, PayPal, Facebook, and so on), you’ll probably want to do so.

All this sounds well and good, but how should we go about designing and defining our API? Here’s how I suggest going about it:

  1. Identify the resources. In Tasker, the resources are people, tasks, and task comments. Essentially, these are the data models. (If you take security into account, Tasker also has user and role resources in addition to sessions.)
  2. Define how the URI should represent the resource. For example, Bob Smith might be represented by /person/bob-smith or /person/29481. Query parameters are also acceptable: /person?administeredBy=john-doe will refer to the set of all individuals who have John Doe as their administrator. If this helps, think of each instance of a resource and each collection of these resources as web pages each having their own URL.
  3. Identify the actions that can be performed for each resource. For example, a task can be created and modified by the owner of the task. This task can be assigned to another user. A task’s status and progress can be updated by both the owner and the assignee. With RESTful APIs, these actions are typically handled by using the HTTP verbs (also known as methods) GET, POST, PUT, and DELETE. Others can also be used, such as OPTIONS, PATCH, and so on. We’ll cover in a moment how these usually line up against typical Create, Read, Update, Delete (CRUD) operations.
  4. Identify the state transitions that are valid for resources. As an example, a client’s first steps might be to request a list of all tasks assigned to a particular user. As part of the response, it should be given URIs that indicate how the app should retrieve information about a particular task. Furthermore, within this single task’s response, there should be information that tells the client how to modify the task.

Most APIs generally mirror the typical CRUD operations. The following is how the HTTP verbs line up against the familiar CRUD counterparts for a collection of items:

HTTP verb

CRUD operation

Description

GET

READ

This returns the collection of items in the desired format. Often can be filtered and sorted via query parameters.

POST

CREATE

This creates an item within the collection. The return result includes the URI for the new resource.

DELETE

N/A

This is not typically used at the collection level, unless one wants to remove the entire collection.

PUT

N/A

This is not typically used at the collection level, though it can be used to update/replace each item in the collection.

 The same verbs are used for items within a collection:

HTTP verb

CRUD operation

Description

GET

READ

This returns a specific item, given the ID.

POST

N/A

This is not typically used at the item level.

DELETE

DELETE

This deletes a specific item, given the ID.

PUT

UPDATE

This updates an existing item. Sometimes PATCH is used to update only specific properties of the item.

 Here’s an example of a state transition diagram for a portion of the Tasker API along with the corresponding HTTP verbs:

Now that we’ve determined the states and the valid transitions, we’re ready to start modeling the API and the responses it should generate. This is particularly useful before you start coding, as one will often notice issues with the API during this phase, and it’s far easier to fix them now rather than after a lot of code has been written (or worse, after the API is in production).

How you model your API is up to you. If you want to create a simple text document that describes the various requests and expected responses, that’s fine. You can also use any number of tools that aid in modeling your API. Some even allow you to provide mock responses for testing. Some of these are identified as follows:

  • RAML (http://raml.org): This is a markup language to model RESTful-like APIs. You can build API models using any text editor, but there is also an API designer online.
  • Apiary (http://apiary.io): Apiary uses a markdown-like language (API blueprint) to model APIs. If you’re familiar with markdown, you shouldn’t have much trouble using this service. API mocking and automated testing are also provided.
  • Swagger (http://swagger.io): This is similar to RAML, where it uses YAML as the modeling language. Documentation and client code can be generated directly from the API model.

Building our API using Node.js

In this section, we’ll cover connecting our web service to our Oracle database, handling user authentication and session management using Passport, and defining handlers for state transitions.

You’ll definitely want to take a look at the /tasker-srv directory in the code package for this book, which contains the full web server for Tasker. In the following sections, we’ve only highlighted some snippets of the code.

Connecting to the backend database

Node.js’s community has provided a large number of database drivers, so chances are good that whatever your backend, Node.js has a driver available for it. In our example app, we’re using an Oracle database as the backend, which means we’ll be using the oracle driver (https://www.npmjs.org/package/oracle).

Connecting to the database is actually pretty easy, the following code shows how:

var oracle = require("oracle");
oracle.connect ( { hostname: "localhost", port: 1521,
database: "xe",
user: "tasker", password: "password" },
function (err, client) {
if (err) { /* error; return or next(err) */ }
/* query the database; when done call client.close() */
});

In the real world, a development version of our server will be using a test database, and a production version of our server will use the production database. To facilitate this, we made the connection information configurable. The /config/development.json and /config/production.json files contain connection information, and the main code simply requests the configuration information when making a connection, the following code line is used to get the configuration information:

oracle.connect ( config.get ( "oracle" ), … );

Since we’re talking about the real world, we also need to recognize that database connections are slow and they need to be pooled in order to improve performance as well as permit parallel execution. To do this, we added the generic-pool NPM module (https://www.npmjs.org/package/generic-pool) and added the following code to app.js:

var clientPool = pool.Pool( {
name: "oracle",
create: function ( cb ) {
   return new oracle.connect( config.get("oracle"),
     function ( err, client ) {
       cb ( err, client );
     }
   )
},
destroy: function ( client ) {
   try {
     client.close();
   } catch (err) {
     // do nothing, but if we don't catch the error,
     // the server crashes
   }
},
max: 5,
min: 1,
idleTimeoutMillis: 30000
});

Because our pool will always contain at least one connection, we need to ensure that when the process exits, the pool is properly drained, as follows:

process.on("exit", function () {
clientPool.drain( function () {
   clientPool.destroyAllNow();
});
});

On its own, this doesn’t do much yet. We need to ensure that the pool is available to the entire app:

app.set ( "client-pool", clientPool );

Executing queries

We’ve built our business logic in the Oracle database using PL/SQL stored procedures and functions. In PL/SQL, functions can return table-like structures. While this is similar in concept to a view, writing a function using PL/SQL provides us more flexibility.

As such, our queries won’t actually be talking to the base tables, they’ll be talking to functions that return results based on the user’s authorization. This means that we don’t need additional conditions in a WHERE clause to filter based on the user’s authorization, which helps eliminate code duplication.

Regardless of the previous statement, executing queries and stored procedures is done using the same method, that is execute. Before we can execute anything, we need to first acquire a client connection from the pool.

To this end, we added a small set of database utility methods; you can see the code in the /db-utils directory. The query utility method is shown in the following code snippet:

DBUtils.prototype.query = function ( sql, bindParameters, cb ) { var self = this,
   clientPool = self._clientPool,
   deferred = Q.defer();
   clientPool.acquire( function ( err, client ) {
   if ( err ) {
   winston.error("Failed to acquire connection.");
     if ( cb ) {
       cb( new Error( err ) );
     else {
       deferred.reject( err );
     }
   }
   try {
     client.execute( sql, bindParameters,
       function ( err, results ) {
         if ( err ) {
           clientPool.release( client );
           if ( cb ) {
           cb( new Error( err ) );
           } else {
             deferred.reject( err );
           }
          }
         clientPool.release( client );
           if ( cb ) {
           cb( err, results );
         } else {
           deferred.resolve( results );
         }
       } );
     }
     catch ( err2 ) {
     try {
       clientPool.release( client );
     }
     catch ( err3 ) {
       // can't do anything...
     }
     if ( cb ) {
       cb( err2 );
     } else { deferred.reject( err2 );
     }
   }
} );
if ( !cb ) {
   return deferred.promise;
}
};

It’s then possible to retrieve the results to an arbitrary query using the preceding method, as shown in the following code snippet:

dbUtil.query( "SELECT * FROM " + "table(tasker.task_mgmt.get_task(:1,:2))", [ taskId, req.user.userId ] )
.then( function ( results ) {
// if no results, return 404 not found
if ( results.length === 0 ) {
   return next( Errors.HTTP_NotFound() );
}
// create a new task with the database results
// (will be in first row)
req.task = new Task( results[ 0 ] );
return next();
} )
.catch( function ( err ) {
return next( new Error( err ) );
} )
.done();

The query used in the preceding code is an example of calling a stored function that returns a table structure. The results of the SELECT statement will depend on parameters (taskId and username), and get_task will decide what data can be returned based on the user’s authorization.

Using Passport to handle authentication and sessions

Although we’ve implemented our own authentication protocol, it’s better that we use one that has already been well vetted and is well understood as well as one that suits our particular needs. In our case, we needed the demo to stand on its own without a lot of additional services, and as such, we built our own protocol. Even so, we chose a well known cryptographic method (PBKDF2), and are using a large number of iterations and large key lengths.

In order to implement authentication easily in Node.js, you’ll probably want to use Passport (https://www.npmjs.org/package/passport). It has a large community, and supports a large number of authentication schemes. If at all possible, try to use third-party authentication systems as often as possible (for example, LDAP, AD, Kerberos, and so on).

In our case, because our authentication method is custom, we chose to use the passport-req strategy (https://www.npmjs.org/package/passport-req). Since Tasker’s authentication is token-based, we will use this to inspect a custom header that the client will use to pass us the authentication token.

The following is a simplified diagram of how Tasker’s authentication process works:

Please don’t use our authentication strategy for anything that requires high levels of security. It’s just an example, and isn’t guaranteed to be secure in any way.

Before we can actually use Passport, we need to define how our authentication strategy actually works. We do this by calling passport.use in our app.js file:

var passport = require("passport");
var ReqStrategy = require("passport-req").Strategy;
var Session = require("./models/session");
passport.use ( new ReqStrategy (
function ( req, done ) {
   var clientAuthToken = req.headers["x-auth-token"];
   var session = new Session ( new DBUtils ( clientPool ) );
   session.findSession( clientAuthToken )
   .then( function ( results ) {
   if ( !results ) { return done( null, false ); }
   done( null, results );
   } )
   .catch( function ( err ) {
   return done( err );
   } )
   .done();
}
));

In the preceding code, we’ve given Passport a new authentication strategy. Now, whenever Passport needs to authenticate a request, it will call this small section of code. You might be wondering what’s going on in findSession. Here’s the code:

Session.prototype.findSession = function ( clientAuthToken, cb ) {
var self = this,
deferred = Q.defer();
// if no token, no sense in continuing
if ( typeof clientAuthToken === "undefined" ) {
   if ( cb ) { return cb( null, false ); }
   else { deferred.reject(); }
}
// an auth token is of the form 1234.ABCDEF10284128401ABC13...
var clientAuthTokenParts = clientAuthToken.split( "." );
if ( !clientAuthTokenParts ) {
   if ( cb ) { return cb( null, false ); }
   else { deferred.reject(); }
} // no auth token, no session.
// get the parts
var sessionId = clientAuthTokenParts[ 0 ],
authToken = clientAuthTokenParts[ 1 ];
// ask the database via dbutils if the token is recognized
self._dbUtils.execute(
"CALL tasker.security.verify_token (:1, :2, :3, :4, :5 )
INTO :6",
[ sessionId,
authToken, // authorization token
self._dbUtils.outVarchar2( { size: 32 } ),
self._dbUtils.outVarchar2( { size: 4000 } ),
self._dbUtils.outVarchar2( { size: 4000 } ),
self._dbUtils.outVarchar2( { size: 1 } )
] )
.then( function ( results ) {
   // returnParam3 has a Y or N; Y is good auth
   if ( results.returnParam3 === "Y" ) {
     // notify callback of successful auth
     var user = {
       userId:   results.returnParam, sessionId: sessionId,
       nextToken: results.returnParam1,
       hmacSecret: results.returnParam2
     };
     if ( cb ) { cb( null, user ) }
     else { deferred.resolve( user ); }
   } else {
     // auth failed
     if ( cb ) { cb( null, false ); } else { deferred.reject(); }
   }
} )
.catch( function ( err ) {
   if ( cb ) { return cb( err, false ); }
   else { deferred.reject(); }
} )
.done();
if ( !cb ) { return deferred.promise; }
};

The dbUtils.execute() method is a wrapper method around the Oracle query method we covered in the Executing queries section.

Once a session has been retrieved from the database, Passport will want to serialize the user. This is usually just the user’s ID, but we serialize a little more (which, from the preceding code, is the user’s ID, session ID, and the HMAC secret):

passport.serializeUser(function( user, done ) {
done (null, user);
});

The serializeUser method is called after a successful authentication and it must be present, or an error will occur. There’s also a deserializeUser method if you’re using typical Passport sessions: this method is designed to restore the user information from the Passport session.

Before any of this will work, we also need to tell Express to use the Passport middleware:

app.use ( passport.initialize() );

Passport makes handling authentication simple, but it also provides session support as well. While we don’t use it for Tasker, you can use it to support a typical session-based username/password authentication system quite easily with a single line of code:

app.use ( passport.session() );

If you’re intending to use sessions with Passport, make sure you also provide a deserializeUser method.

Next, we need to implement the code to authenticate a user with their username and password. Remember, we initially require the user to log in using their username and password, and once authenticated, we handle all further requests using tokens. To do this, we need to write a portion of our API code.

Building API handlers

We won’t cover the entire API in this section, but we will cover a couple of small pieces, especially as they pertain to authentication and retrieving data.

First, we’ve codified our API in /tasker-srv/api-def in the code package for this book. You’ll also want to take a look at /tasker-srv/api-utils to see how we parse out this data structure into useable routes for the Express router.

Basically, we codify our API by building a simple structure:

[ { "route": "/auth", "actions": [ … ] },
{ "route": "/task", "actions": [ … ] },
{ "route": "/task/{:taskId}", "params": [ … ],   "actions": [ … ] },
… ]

Each route can have any number of actions and parameters. Parameters are equivalent to the Express Router’s parameters. In the preceding example, {:taskId} is a parameter that will take on the value of whatever is in that particular location in the URI. For example, /task/21 will result in taskId with the value of 21. This is useful for our actions because each action can then assume that the parameters have already been parsed, so any actions on the /task/{:taskId} route will already have task information at hand.

The parameters are defined as follows:
{ "name": "taskId", "type": "number",
"description": "…",
"returns": [ … ],
"securedBy": "tasker-auth",
"handler": function (req, res, next, taskId) {…} }

Actions are defined as follows:

{ "title": "Task",
"action": "get-task", "verb": "get",
"description": { … }, // hypermedia description
"returns": [ … ],     // http status codes that are returned
"example": { … },     // example response
"href": "/task/{taskId}", "template": true,
"accepts": [ "application/json", … ],
"sends": [ "application/json", … ],
"securedBy": "tasker-auth", "hmac": "tasker-256",
"store": { … }, "query-parameters": { … },
"handler": function ( req, res, next ) { … } }

Each handler is called whenever that particular route is accessed by a client using the correct HTTP verbs (identified by verb in the prior code). This allows us to write a handler for each specific state transition in our API, which is nicer than having to write a large method that’s responsible for the entire route. It also makes describing the API using hypermedia that much simpler, since we can require a portion of the API and call a simple utility method (/tasker-srv/api-utils/index.js) to generate the description for the client.

Since we’re still working on how to handle authentication, here’s how the API definition for the POST /auth route looks (the complete version is located at /tasker-srv/api-def/auth/login.js):

action = {
   "title": "Authenticate User",
   "action": "login",
   "description": [ … ], "example":     { … },
   "returns":     {
     200: "User authenticated; see information in body.",
     401: "Incorrect username or password.", …
   },
   "verb": "post", "href": "/auth",
   "accepts": [ "application/json", … ],
   "sends": [ "application/json", … ],
   "csrf": "tasker-csrf",
   "store": {
     "body": [ { name: "session-id", key: "sessionId" },
     { name: "hmac-secret", key: "hmacSecret" },
     { name: "user-id", key: "userId" },
     { name: "next-token", key: "nextToken" } ]
   },
   "template": {
     "user-id": {
       "title": "User Name", "key": "userId",
       "type": "string", "required": true,
       "maxLength": 32, "minLength": 1
     },
     "candidate-password": {
       "title": "Password", "key": "candidatePassword",
       "type": "string", "required": true,
       "maxLength": 255, "minLength": 1
     }
   },

The earlier code is largely documentation (but it is returned to the client when they request this resource). The following code handler is what actually performs the authentication:

   "handler": function ( req, res, next ) {
     var session = new Session( new DBUtils(      req.app.get( "client-pool" ) ) ),
       username,
       password;
     // does our input validate?
     var validationResults =       objUtils.validate( req.body, action.template );
     if ( !validationResults.validates ) {
       return next(         Errors.HTTP_Bad_Request( validationResults.message ) );
     }
     // got here -- good; copy the values out
     username = req.body.userId;
     password = req.body.candidatePassword;
     // create a session with the username and password
     session.createSession( username, password )
       .then( function ( results ) {
         // no session? bad username or password
         if ( !results ) {
           return next( Errors.HTTP_Unauthorized() );
         }
       // return the session information to the client
       var o = {
         sessionId: results.sessionId,
         hmacSecret: results.hmacSecret,
         userId:   results.userId,
         nextToken: results.nextToken,
         _links:   {}, _embedded: {}
      };
       // generate hypermedia
       apiUtils.generateHypermediaForAction(         action, o._links, security, "self" );
         [ require( "../task/getTaskList" ),
         require( "../task/getTask" ), …
         require( "../auth/logout" )
         ].forEach( function ( apiAction ) {
           apiUtils.generateHypermediaForAction(
           apiAction, o._links, security );
         } );
         resUtils.json( res, 200, o );
       } )
       .catch( function ( err ) {
         return next( err );
         } )
       .done();
     }
   };

The session.createSession method looks very similar to session.findSession, as shown in the following code:

Session.prototype.createSession =
function ( userName, candidatePassword, cb ) {
var self = this,
deferred = Q.defer();
if ( typeof userName === "undefined" ||
typeof candidatePassword === "undefined" ) {
   if ( cb ) { return cb( null, false ); }
   else { deferred.reject(); }
}
// attempt to authenticate
self._dbUtils.execute(
"CALL tasker.security.authenticate_user( :1, :2, :3," +
" :4, :5 ) INTO :6", [
userName, candidatePassword,
self._dbUtils.outVarchar2( { size: 4000 },
self._dbUtils.outVarchar2( { size: 4000 } ),
self._dbUtils.outVarchar2( { size: 4000 } ),
self._dbUtils.outVarchar2( { size: 1 } ] )
.then( function ( results ) {
   // ReturnParam3 has Y or N; Y is good auth
   if ( results.returnParam3 === "Y" ) {
     // notify callback of auth info
     var user = {
       userId:   userName,
       sessionId: results.returnParam,
       nextToken: results.returnParam1,
       hmacSecret: results.returnParam2
     };
     if ( cb ) { cb( null, user ); }
     else { deferred.resolve( user ); }
   } else {
     // auth failed
     if ( cb ) { cb( null, false ); }
     else { deferred.reject(); }
   }
} )
.catch( function ( err ) {
   if ( cb ) { return cb( err, false ) }
   else { deferred.reject(); }
} )
.done();
if ( !cb ) { return deferred.promise; }
};

Once the API is fully codified, we need to go back to app.js and tell Express that it should use the API’s routes:

app.use ( "/", apiUtils.createRouterForApi(apiDef, checkAuth));

We also add a global variable so that whenever an API section needs to return the entire API as a hypermedia structure, it can do so without traversing the entire API again:

app.set( "x-api-root", apiUtils.generateHypermediaForApi( apiDef, securityDef ) );

The checkAuth method shown previously is pretty simple; all it does is ensure that we don’t authenticate more than once in a single request:

function checkAuth ( req, res, next ) {
if (req.isAuthenticated()) {
   return next();
}
passport.authenticate ( "req" )(req, res, next);
}

You might be wondering where we’re actually forcing our handlers to use authentication. There’s actually a bit of magic in /tasker-srv/api-utils. I’ve highlighted the relevant portions:

createRouterForApi:function (api, checkAuthFn) {
var router = express.Router();
// process each route in the api; a route consists of the
// uri (route) and a series of verbs (get, post, etc.)
api.forEach ( function ( apiRoute ) {
   // add params
   if ( typeof apiRoute.params !== "undefined" ) {
     apiRoute.params.forEach ( function ( param ) {
       if (typeof param.securedBy !== "undefined" ) {
         router.param( param.name, function ( req, res,
         next, v) {
           return checkAuthFn( req, res,
           param.handler.bind(this, req, res, next, v) );
         });
       } else {
         router.param(param.name, param.handler);
       }
     });
   }
   var uri = apiRoute.route;
   // create a new route with the uri
   var route = router.route ( uri );
   // process through each action
   apiRoute.actions.forEach ( function (action) {
     // just in case we have more than one verb, split them out
     var verbs = action.verb.split(",");
     // and add the handler specified to the route
     // (if it's a valid verb)
     verbs.forEach ( function (verb) {
       if (typeof route[verb] === "function") {
         if (typeof action.securedBy !== "undefined") {
           route[verb]( checkAuthFn, action.handler );
         } else {
           route[verb]( action.handler );
         }
       }
     });
   });
});
return router;
};

Once you’ve finished writing even a few handlers, you should be able to verify that the system works by posting requests to your API. First, make sure your server has started ; we use the following code line to start the server:

export NODE_ENV=development; npm start

For some of the routes, you could just load up a browser and point it at your server. If you type https://localhost:4443/ in your browser, you should see a response that looks a lot like this:

If you’re thinking this looks styled, you’re right. The Tasker API generates responses based on the client’s requested format. The browser requests data in HTML, and so our API generates a styled HTML page as a response. For an app, the response is JSON because the app requests that the response be in JSON. If you want to see how this works, see /tasker-srv/res-utils/index.js.

If you want to actually send and receive data, though, you’ll want to get a REST client rather than using the browser. There are many good free clients: Firefox has a couple of good clients as does Chrome. Or you can find a native client for your operating system.

Although you can do everything with curl on the command prompt, RESTful clients are much easier to use and often offer useful features, such as dynamic variables, various authentication methods built in, and many can act as simple automated testers.

Summary

In this article, we’ve covered how to build a web server that bridges the gap between our database backend and our mobile application. We’ve provided an overview of RESTful-like APIs, and we’ve also quickly shown how to implement such a web API using Node.js. We’ve also covered authentication and session handling using Passport.

Resources for Article:

 Further resources on this subject:


NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here