Game Servers and Couchbase with Node.js - Part 2

In this part of the series, we will be implementing session management and authenticated endpoints (endpoints that require you to be logged in). Lets get started!

If you have yet to read Part 1 of this series, I suggest you do as it sets up the basic project layout as well as basic user management and is a prerequisit to Part 2!

Session Management - The Model

The first step towards building our session management endpoints is setting up a model to use from these endpoints for manipulating the database. Unlike the account model, this model does not need referential documents or any complicated logic and thus is fairly simple. The first step is importing the various modules we will need, as well as getting a reference to our database connection from our database module.

Next, again similar to our account model, we build a small function for stripping away our database properties. 'type' is the only one we need to remove in this case as well.

function cleanSessionObj(obj){delete obj.type;return obj;}

Now lets start working on the session model class itself. First comes our blank constructor. I'd like to mention at this point that our model classes are currently entirely static in this example, but a good practice to follow is returning your model classes from the static CRUD operations, we just are not at the point that this would be helpful yet.

function SessionModel(){}module.exports= SessionModel;

Now for our first session model function that will actually do any work.

SessionModel.create=function(uid, callback){};

And inside that function we need to create our document we will be inserting and its associated key (we only store the uid, and then access the users remaining information directly from their account document through our account model, which you will see later.

Then we store our newly created session document to our cluster. You will also notice that we call our sanitization function from above here, as well as setting an expiry value of 60 minutes. This will cause the cluster to 'expire' the session by removing it after that amount of time.

Next we need to build a function on our model that allows us to retrieve session information that we have previous stored. To do this, we generate a key matching the one we would have stored, and then execute a get request, returning the users uid from the session to our callback.

Session Management - Account Lookup

Before we can start writing our request handler itself, we need to build a method that will allow us to lookup a user based on their username. We don't really want users to have to remember their uid's! In part 1 we built our account model in accountmodel.js. Lets go back to that file and add a new method.

AccountModel.getByUsername=function(username, callback){};

How we handle this method is that we will build a key using the provided username that will search for one of the referential documents we created in `AccountModel.create`. If we are not able to locate this referential document, we assume that the user does not exist and return an error. If the username is able to be located, and we find the referential document, we then execute a `AccountModel.get` to locate the user document itself, and forward the callback through there. This means that calls to `AccountModel.getByUsername` will return the full user object as if you had directly called `AccountModel.get` with the uid.

Session Management - Request Handling

The last step of enabling session creation is writing the request handler itself. Thankfully, most of the important logic has gone into the sections above, and our request handler simpler forwards information off to each for processing. First we validate our inputs from the user to make sure everything neccessary was provided. Next we try to locate the account by the provided username. Next we validate that the password matches what the user provided by hashing the provided password and comparing. And finally we create the session with our newely created session model and return the details to the user. You may notice that the session id is not directly provided to the user, but instead is passed in the header. This is for consistency as the header is also used to authenticate each request later.

Now that our session creation exists, lets add a method to authenticate the user on a per-request basis. This will check the users session, for now I have placed this in our app.js, however it may be better put in its own separate file later once routes begin being separated into separate files. To authenticate the user, we check the standard HTTP Authorization header, grab the session id from it and then look this up using our session model. If everything goes as plan, we store the newly found user id in the request for later route handlers.

Mainly for the purpose of displaying the authUser method in action I implemented a `/me` endpoint that returns the user document. We simply do a get through the account model based on the uid that was stored into the request by the authInfo handler, and return this to the client, of course stripping away the password first.