In this NextJS Iron Session Tutorial, we will learn how to implement a robust authentication functionality with the help of sessions.

Basically, iron-session is a stateless NodeJS utility that enables the use of sessions to store data. The session data is stored in encrypted cookies also known as seals. Only your server can decode the session data. There are no session ids, making iron sessions “stateless” from the server’s point of view.

In case you are new to NextJS in general, I will recommend starting with the basics of NextJS.

1 – NextJS Login Authentication Components

For our demo example, we need only 3 basic things:

  • A page where user can login to our application. This is basically a sign in page.
  • An API endpoint to validate the user’s credentials and creating the user session.
  • Lastly, a page that is available only to an authenticated user.

Note that we are keeping things intentionally simple for the purpose of this application. To use the iron-session utility, we first need to make sure it is installed to our project.

$ npm install --save iron-session

Once the installation is complete, we can start building the actual logic.

2 – NextJS Iron Session Reusable Configuration

To manage our configuration properly, we will first create a lib/config folder and create a file iron-config.js with the below contents:

export const ironOptions = {
    cookieName: "MY_APP_COOKIE",
    password: "yPo4T7apfbdvctV1Bso1oAndQH9qwC94",
    // secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
    cookieOptions: {
        secure: process.env.NODE_ENV === "production" ? true: false,
  },
}

This is a configuration object that takes the cookieName and password. Basically, this password is used to encrypt the session information so that no one can decipher the actual contents of the session. For security, this password should be a complex string containing at least 32 characters.

Also, this is a secret key and should be kept secret and not committed to version control. In a real prod application, this key should be stored in the runtime environment.

Next, we will create a couple of wrapper functions to expose the functionalities provided by iron-session. These functions will be present in the file withSession.js.

import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
import { ironOptions } from "./iron-config";

export function withSessionRoute(handler) {
    return withIronSessionApiRoute(handler, ironOptions);
}

export function withSessionSsr(handler) {
    return withIronSessionSsr(handler, ironOptions);
}

Basically, the iron-session/next package provides a couple of functions. The withIronSessionApiRoute is for use with API routes. The withIronSessionSsr is for use with our server-side rendered pages. These wrapper functions take a handler callback and the iron-session configuration object as input.

Using this, we will avoid unnecessary repetition within our component code.

3 – Login API using NextJS Iron Session

Let us start with building the second piece first i.e. an API for validating the user’s credentials.

Within the pages folder, we create another folder api. Inside the api folder, we create a file sessions.js.

import { withSessionRoute } from "pages/lib/config/withSession";

const VALID_EMAIL = "test@gmail.com";
const VALID_PASSWORD = "password";

export default withSessionRoute(createSessionRoute);

async function createSessionRoute(req, res) {
    if (req.method === "POST") {
        const { email, password } = req.body;

        if (email === VALID_EMAIL && password === VALID_PASSWORD) {
            req.session.user = {
                username: "test@gmail.com",
                isAdmin: true
            };
            await req.session.save();
            res.send({ ok: true });
        }
        return res.status(403).send("");
    }
    return res.status(404).send("");
}

Basically, here we have hard-coded values for VALID_EMAIL and VALID_PASSWORD. We import the withSessionRoute package from the wrapper function we created in the previous section. This package contains the session-related functionality.

We pass the createSessionRoute() function to the withSessionRoute() wrapper function. Inside the createSessionRoute() function, we basically check whether the email and password enter by the user matches the valid credentials. If yes, we add the user information to the req.session object and save it to the browser session.

4 – Building the NextJS Login Page

Moving on, we will now implement the page where users can enter their credentials to log in to the application.

Basically, it is a simple NextJS page login.js.

import { useRouter } from "next/router";
import { useRef, useState } from 'react'

export default function Login() {
  const router = useRouter();
  const emailInput = useRef();
  const passwordInput = useRef();

  const handleSubmit = async (e) => {
    e.preventDefault();

    const email = emailInput.current.value;
    const password = passwordInput.current.value;

    const response = await fetch("/api/sessions", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password })
    });

    if (response.ok) {
      return router.push("/private");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Email: <input type="text" ref={emailInput} />
        </label>
      </div>
      <div>
        <label>
          Password: <input type="password" ref={passwordInput} />
        </label>
      </div>
      <div>
        <button type="submit">Sign in</button>
      </div>
    </form>
  );
}

Basically, this component is quite straightforward. We have a form for the user to enter the email and password. On submitting the form, we basically make a call to sessions api that we implemented in the previous section. If the response is success, we redirect the user to the /private page.

5 – NextJS Iron Session Protected Route

Let us now create the final component which is a page that is protected by the session.

In NextJS, we can easily create such a page within the pages folder. We name it private.js.

import React from 'react';
import { withSessionSsr } from './lib/config/withSession';

const PrivatePage = ({ user }) => (
    <div>
        <h1>Hello {user.username}</h1>
        <p>Secret Content</p>
    </div>
);

export const getServerSideProps = withSessionSsr(
    async ({req, res}) => {
        const user = req.session.user;

        if(!user) {
            return {
                notFound: true,
            }
        }

        return {
            props: { user }
        }
    }
);

export default PrivatePage;

Basically, here we import the second wrapper function withSessionSsr. The actual component only has a couple of markup tags with a Hello greeting.

With the getServerSideProps(), we extract the session details from the req object. If a valid user was found, we return it as a prop for the component. If not, we set the notFound property to true so that a 404 page is triggered.

In case you want to know more about this, check out this post about NextJS getServerSideProps function.

6 – NextJS Iron Session Logout

Finally, we can also create an API route to logout from the application.

Within the api folder, we can create a file named logout.js.

import { withSessionRoute } from "pages/lib/config/withSession";

export default withSessionRoute(logout);

async function logout(req, res, session) {
    req.session.destroy();
    res.send({ok: true})
}

Basically, here we use the withSessionRoute wrapper function. In the logout() function, we call the destroy() function to destroy the session object from the browser.

With this, our application is ready in terms of the basic functionality. We can now login to the application using the hard-coded credentials and also logout.

Conclusion

This was all about the NextJS Iron Session Tutorial. Though the example was simplified, we tried to implement the complete flow along with the good practice of restricting the configuration properties of the iron-session and creating reusable wrapper functions.

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

Categories: NextJS

2 Comments

Lawrence Mugambi · 15th March 2023 at 9:05 am

Thanks a lot for a great article. How do you implement auto logout when session expires?

Rizki Ramadhan · 20th August 2023 at 2:52 am

Thank’s for your tutorial, it helped me a lot.

Leave a Reply

Avatar placeholder

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