{ Cookies and Sessions in Express. }

Objectives:

By the end of this chapter, you should be able to:

  • Compare and contrast cookies and sessions
  • Read and write cookies using cookie-parser
  • Compare and contrast the cookie-session and express-session modules

Cookies / Sessions

Now that we are able to successfully store passwords in the database and check whether a user has entered the correct password, we now need a way to remember when a user has logged in! Since the web is stateless, we need some way of remembering across multiple requests that a user has successfully authenticated. To do that we will need to learn about some more persistance tools like cookies and sessions. Here's some vocabulary:

cookies - cookies are small pieces of information that we can send from a server to the browser to enable persistence in the browser. Cookies are set using the Set-Cookie response header from the server, and are sent to the server using the Cookie header. We can set and read cookies using req.cookies which is provided by the cookie-parser module (npm install --save cookie-parser).

session - in order to store data on the server, we make use of something called the session. The session is an object that persists during requests from the client and can be accessed anywhere in the request-response cycle. The session will be different for each user (so if we store information like a userId in the session, it will not be overwritten when other users log in). There are different modules used for storing sessions; two common ones are:

  • express-session - stores a session in memory. If the server goes down, the user must log in again.
  • cookie-session - stores a session in memory and sets a cookie in the browser in case the session is destroyed (the cookie will restore the session if it exists and has not expired)

Since it is a better user experience to not have to re-log in when the server goes down, we will be using cookie-session for future examples.

Log in / Log out

Now that we have a conceptual idea of how to store information across multiple requests using the session (server-side storage), let's see how we can use the cookie-session module to do that. When we install and use the cookie-session module, we gain access to an object called session, which is part of the req object.

var session = require("cookie-session");

app.use(session({
    secret: 'what does this do?'; // great question! We will come back to that, but we need it for now!
}))

// make sure we head to this route
app.get('/', function(req, res, next){
    req.session.username = 'Elie';
})

app.get('/person', function(req, res, next){
    res.send(req.session.username); // it persists!
})

Here's how we can use the session to remember when a user is authenticated. When they successfully authenticate, we will place a very small piece of information in the session. Then, on future requests, we'll check the session for that information. If it's there, we can be sure the user is logged in. Similarly, when a user logs out, we will remove the information from the session to mark them as "logged out". Let's see what that looks like:

app.post('/login', function(req, res, next){
    // find a user in the db
    db.User.find(req.body.username).then(function(foundUser){
        // use our comparePassword from the user model in the last section
        foundUser.comparePassword(req.body.password, function(match){
            if(match){
                // if the user is successfully authenticated
                req.session.user_id = foundUser.id;
            }
        });
    });
});

Secret Keys and dotenv

So what is this "secret" that we need when we use the cookie-session module? The secret key is a string that is used to encrypt and decrypt the cookie that is being sent to the browser with information from the session. Since we do not want users getting access to the information on the cookie, it's essential that we encrypt it. At the same time, we do not want people getting access to this secret key, so we need to hide that as well!

Since we have now created quite a few variables that we do not want exposed (like our secret key), we need a way to hide them. Fortunately there is a wonderful module called dotenv to help us. You can read more about it here. We first need to install the module npm install --save dotenv and then create a .env file (touch .env). We can now place our environment variables in this file.

SECRET_KEY=shhhhh

Now in our app.js we can load this file using

require('dotenv').load();

var secret = process.env.SECRET_KEY;

Finally MAKE SURE you add .env to your .gitignore with echo .env >> .gitignore so that you do not check in your .env file with Git.

Ensuring authentication on each request

Now how can we make sure that users are logged in during each request? We need some middleware, which will intercept a request, check if there is some necessary information in the session (which we place when a user logs in), and either redirect the user back to a log in page, or allow the user to continue with their request. Let's examine how to implement this in the next section!

Sample App

You can find a sample app with bcrypt, cookies, and sessions here.

When you're ready, move on to Authentication with Express

Continue