Authentication is required whenever you want to deliver a personal experience to a user or whenever you are dealing with sensitive data. Today, modern websites communicate using the old trusty HTTP protocol, which by design is stateless. By stateless, we mean that each request (I.E opening a new web page) is treated as a new, separate request which knows nothing about the previous request a user sent to the web server.
You might have read that last sentence and thought “Wait, but how do online stores keep track of shopping carts…” or “how do online application forms keep track of previously entered answers?” the answers are via cookies and sessions.
Cookies are small text files which live client side (I.E on your Laptop or iPhone) and contain data which helps enrich the visitors experience of a site by presenting the web server with data/customized options previously saved from a user’s last visit. It is a very bad idea to put anything sensitive inside of a cookie, as cookies can be stolen via man in the middle attacks and changed with ease via the Chrome Dev tools if a user wishes to do so. An example of how this could be dangerous could be if you granted a cookie to a user which contained their customerID and the web server then checked against this cookie and the contained customerID to validate who the user is. At this point you are putting your application at serious risk as you open up the opportunity for someone to change the customerID to spoof another person’s session; potentially granting them access to their previous orders and billing information.
Sessions are kind of similar to cookies, however they live server side. We can regard sessions as the much more secure counterpart of storing session data as a user cannot manually edit their information or become a victim of man in the middle attacks. The real downside to sessions is that they’re volatile and not persistent due to being stored inside of a web servers’ memory. This means if you had a million users concurrently accessing your application, you would need to store 1 million session variables inside memory, which is not the best approach either.
Best Practice 🔥
The current best practice is to utilize both cookies and sessions together, but to store the session data inside of a database instead of just having it sit there inside of a web servers memory. We will also encrypt the data stored within the cookie so it cannot be spoofed by an attacker.
I’ve omitted a large amount of the ExpressJS setup code as I really wanted to make it clear what we are doing in each section of this config. Before we get started, we first need to import two packages into our application in order to work with database secured sessions. express-session is not included within express by default, but it is developed by the same people whereas connect-mongodb-session is a 3rd party NodeJS module which we can be included to help us streamline connection to our MongoDB database. When you import connect-mongodb-session, you are required to pass your local express-session variable to the function. You can then instantiate a new MongoDBStore and pass an options object such as the above, which tells connect-mongodb-session how you would like your store configuring. The only two required parameters of this options object is the uri of your database and the name of the MongoDB collection you would like to store the session data in. You don’t have to create a new MongoDB collection of the same name prior to testing, the connect-mongodb-session will just quietly do this on initial connection to MongoDB if its not already setup.
Once all of this has been done, you’re ready to use database sessions with ExpressJS. On the final line of the screenshot above, I have created a middleware to be served for each and every request we get to our NodeJS server. The two important parts of this middleware is the “secret” and “store” properties. Secret is a string which is used as a hash to encrypt all the session keys we generate; in production this would be a crazy long and complex string instead of just “supersecret”. Store is simply used to point to the store we created earlier which contains our URI and collection data.
To test if everything is working as should be, you could try to visit your auth route or wherever you set a session variable and check to see if you have something similar to the above within your Chrome Dev Tools. You can see that the key connect.sid has been created and its value is set to the session variable after being hashed by our secret we supplied.
One thing I found really cool thing about using the express-session module is that it gives us access to a new session property which we can use to manage our sessions and is available upon every request. In my example above, I am calling the destroy() method on the session which quietly and asynchronously removes the session from the database we have connected in our previous setup. The destroy method takes a function callback which you can utilize to do some clean up code – I decided to use this for error handling and to redirect a user back to the root of the application.