In this post, we will look at NodeJS Session Management. We will use the Express Session package and learn how to store session data in a session store such as MongoDB.

In case you are new to NodeJS and Express, consider going through this post on getting started with ExpressJS.

1 – Cookies and Sessions

Cookies and sessions go hand in hand while building stateless HTTP applications. In a stateless application, clients and servers forget about each other after every request. However, cookies and sessions help us keep track of a user across several requests.

A cookie is basically a key-value pair that is stored in the browser. Once the server sets the cookie for a particular user, the browser sends the cookie as part of subsequent requests. Using the cookie the server can track the user.

See below illustration of how a cookie actually works.

setting up a cookie illustration
Setting a Cookie

The main issue with a cookie is that it is accessible via the browser. Users can actually modify the cookie information. This makes it a terrible choice for sensitive data such as credentials or secret information.

This is where sessions come into the picture.

The session contains a unique set of characters to identify the client. When the client makes a login request, the server creates a session (using a secret key) and stores it on the server-side using some sort of session storage. The server also sends a cookie back to the client.

The cookie contains the unique identifier for the session and will be stored on the client. For every subsequent request, the browser will send the session information as part of the cookie. However, only the server knows how to interpret and validate the session information using the secret key. This makes the session-based mechanism secure.

See below illustration that describes how the concept of using sessions work in a typical application.

nodejs session management illustration
The Concept of Sessions

2 – Implementing the NodeJS Session Management

Let us now implement a basic session and cookie mechanism using NodeJS and Express. To keep things simple, we will have just two pages – the login page and the welcome page that will be displayed if the user logs in successfully.

2.1 – Installing the NPM Packages

We will begin by installing the required packages.

First, we have the packages for express and ejs templating engine.

$ npm install --save express ejs

Next, we have the packages for connecting our Express application to MongoDB.

$ npm install --save mongodb mongoose

Lastly, we have the necessary packages for managing the sessions.

$ npm install --save express-session connect-mongodb-session

At the end of it, your package.json should look like below:

{
  "name": "nodejs-session-cookie-demo",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "nodemon app.js",
    "start-server": "node app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.20.1",
    "connect-mongodb-session": "^3.1.1",
    "ejs": "^3.1.8",
    "express": "^4.18.2",
    "express-session": "^1.17.3",
    "mongodb": "^4.10.0",
    "mongoose": "^6.6.5"
  },
  "devDependencies": {
    "nodemon": "^2.0.20"
  }
}

To run the application, we have also used nodemon. So, you can also install it as a development dependency in case you don’t have it globally available.

2.2 – Creating the EJS Templates

Within the root directory of the project, we create a folder named views. Inside this folder, we will create the template files for the login page and the welcome page.

First, we have the login page (home.ejs)

<%- include('./includes/head.ejs') %>
    <link rel="stylesheet" href="/css/forms.css">
    <link rel="stylesheet" href="/css/auth.css">
</head>

<body>
    <main>
        <form class="login-form" action="/login" method="POST">
            <div class="form-control">
                <label for="email">Email</label>
                <input type="email" name="email" id="email">
            </div>
            <div class="form-control">
                <label for="password">Password</label>
                <input type="password" name="password" id="password">
            </div>
            <button class="btn" type="submit">Login</button>
        </form>
    </main>
<%- include('./includes/end.ejs') %>

Basically, the above file contains a form for entering email and password for log in purpose.

Next, we have the welcome page (welcome.ejs).

<%- include('./includes/head.ejs') %>
</head>

<body>

    <main>
        <h2>Welcome to the Node Session Cookie Demo.</h2>
        <p>You are successfully logged in!</p>
        <button><a href="/logout">Logout</a></button>
    </main>
<%- include('./includes/end.ejs') %>

Here, we have some simple welcome message followed by a button to logout from the application.

As you can see, we have used EJS partials for removing duplication. Within the includes folder, we can keep the partials file.

Below is the code for head.ejs.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%= pageTitle %></title>
    <link rel="stylesheet" href="/css/main.css">

Then, we have end.ejs for the end of the HTML page.

</body>

</html>

You can read more about NodeJS Templating with EJS.

2.3 – Create the Authorization Controller

Now, we can implement the functions for building the authorization functionality (login, logout and so on). To maintain a good code structure, we will create a folder named controllers. Within the folder, we create a file named auth.js.

exports.getLogin = (req, res, next) => {
    const session = req.session;

    if (session.isLoggedIn) {
        res.render('welcome', {
            pageTitle: 'Welcome'
        })
    } else {
        res.render('home', {
            pageTitle: 'Login',
        });
    }
};

exports.postLogin = (req, res, next) => {
    if (req.body.email === "abc@gmail.com" && req.body.password === "pass123") {
        req.session.isLoggedIn = true;
        res.redirect('/welcome');
    }
    else{
        res.redirect('/')
    }
}

exports.getWelcome = (req, res, next) => {
    res.render('welcome', {
        pageTitle: 'Welcome'
    })
}

exports.logout = (req, res, next) => {
    req.session.destroy(() => {
        res.redirect('/')
    });
}

Basically, we implement four functions:

  • The getLogin() function checks the session object. If the user is logged in, it renders the welcome page. Else, we render the login or the home page.
  • Next, we have the postLogin() function. Basically, this is the function that gets triggered when user submits the login form. Here, we check the user credentials (email and password) and if they are correct, we set the session object with the statement req.session.isLoggedIn = true. Basically, when we set the req.session object, Express automatically makes sure to set the appropriate cookie in the browser.
  • The getWelcome() function is quite simple. We simply use it to render the welcome page.
  • Lastly, we have the logout() function. Here, we call req.session.destroy(). This function destroys the session on the server side. In other words, it delete the session data from the session store.

2.4 – Create the Express Router

Next, we need to implement the Express router for handling the various routes that users can access. Again, we will create a new folder named routes within the root directory. Inside the routes folder, we create a file named auth.js.

const express = require('express');

const authController = require('../controllers/auth');

const router = express.Router();

router.get('/', authController.getLogin);
router.post('/login', authController.postLogin);
router.get('/welcome', authController.getWelcome);
router.get('/logout', authController.logout);

module.exports = router;

As you can see, we have different route handlers for different paths. For every handler, we use the corresponding function from the controller.

2.5 – Create the Main File of the Project

All the pieces are done. Now, it is time to create the main file for the application.

We will create this file (app.js) within the root directory of the project.

const path = require('path');

const express = require('express');
const bodyParser = require('body-parser')

const mongoose = require('mongoose');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);

const authRoutes = require('./routes/auth');

const MONGODB_URI = 'mongodb://localhost:27017';

const app = express();

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
    extended: true
  }));

const store = new MongoDBStore({
    uri: MONGODB_URI,
    collection: 'sessions'
});

app.set('view engine', 'ejs');
app.set('views', 'views');

app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
    secret: 'secret code',
    resave: false,
    saveUnitialized: false,
    store: store
}));

app.use(authRoutes);

mongoose
    .connect(
        MONGODB_URI
    )
    .then(result => {
        app.listen(3000);
    })
    .catch(err => {
        console.log(err);
    });

As you can see, this file does a lot of things.

  • In the first step, we import the Express package.
  • Next, we have the imports for MongoDB and the MongoDBStore for session storage. We configure the store to use the MongoDB URI and the sessions collection.
  • Also, we configure the session object with a few important parameters such as the secret code and the store. In a real production application, this secret code will not be hard-coded in the source code but should come from an environment variable.
  • We also add the Express route to the application context.
  • At the end, we simply start the application on port 3000.

3 – Testing the NodeJS Session Management application

To test the application, we first access http://localhost:3000. On the login page, we enter the user id and password.

nodejs session management login
Login Page

On clicking the Login button, we are taken to the welcome page.

nodejs session management success
The Welcome Page

Also, we can see the session information in the application cookies section. The name of the key is connect.sid.

nodejs express session cookie

The same session data is also available in the MongoDB sessions collection.

mongodb session store nodejs

Once we logout from the application by clicking the logout button, the session data from the MongoDB store is deleted automatically. Even if the session store is preserved in the cookie, it does not matter as the session is invalidated at the server side.

Conclusion

This NodeJS session management example deals with the concept of cookies and sessions. Though we didn’t explicitly get into the topic of authentication, we made a basic login flow with simple data and page structure.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *