Menu Close

Server-Side Development with Hapi.js — Login and Cookie Auth

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Cookie

We can add cookie authentication easily with a few lines of code.

For example, we can write:

const Bcrypt = require('bcrypt');
const Hapi = require('@hapi/hapi');

const users = [{
  username: 'john',
  password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm',
  name: 'John Doe',
  id: '1'
}];

const start = async () => {

  const server = Hapi.server({
    port: 4000,
    host: '0.0.0.0'
  });

  await server.register(require('@hapi/cookie'));

  server.auth.strategy('session', 'cookie', {
    cookie: {
      name: 'sid-example',
      password: '!wsYhFA*C2U6nz=Bu^%A@^F#SF3&kSR6',
      isSecure: false
    },
    redirectTo: '/login',
    validateFunc: async (request, session) => {
      const account = await users.find(
        (user) => (user.id === session.id)
      );

      if (!account) {
        return {
          valid: false
        };
      }

      return {
        valid: true,
        credentials: account
      };
    }
  });

  server.auth.default('session');

  server.route([{
      method: 'GET',
      path: '/',
      handler (request, h) {
        return 'Welcome to the restricted home page!';
      }
    },
    {
      method: 'GET',
      path: '/login',
      handler (request, h) {
        return `<html>
            <head>
                <title>Login page</title>
            </head>
            <body>
                <h3>Please Log In</h3>
                <form method="post" action="/login">
                    Username: <input type="text" name="username"><br>
                    Password: <input type="password" name="password"><br>
                <input type="submit" value="Login"></form>
            </body>
        </html>`;
      },
      options: {
        auth: false
      }
    },
    {
      method: 'POST',
      path: '/login',
      handler: async (request, h) => {

        const {
          username,
          password
        } = request.payload;
        const account = users.find(
          (user) => user.username === username
        );

        if (!account || !(await Bcrypt.compare(password, account.password))) {
          return h.view('/login');
        }

        request.cookieAuth.set({
          id: account.id
        });

        return h.redirect('/');
      },
      options: {
        auth: {
          mode: 'try'
        }
      }
    }
  ]);

  await server.start();
  console.log('server running at: ' + server.info.uri);
};

start();

We have the users array with the user data.

password is the hash of the password. It’s 'secret' in plain text.

The start function has the code for our app.

The Hapi.server function creates the server.

We register the @hapi/cookie module with:

await server.register(require('@hapi/cookie'));

Then we add the user authentication fucntion with:

server.auth.strategy('session', 'cookie', {
  cookie: {
    name: 'sid-example',
    password: '!wsYhFA*C2U6nz=Bu^%A@^F#SF3&kSR6',
    isSecure: false
  },
  redirectTo: '/login',
  validateFunc: async (request, session) => {
    const account = await users.find(
      (user) => (user.id === session.id)
    );

    if (!account) {
      return {
        valid: false
      };
    }

    return {
      valid: true,
      credentials: account
    };
  }
});

We call the server.auth.strategy method to add authentication.

'session' is the name. 'cookie' is the strategy.

The object has the validation logic. cookie sets the cookie secret.

redirect adds the redirect when auth fails.

validateFunc has the logic to validate the credentials.

We check if the user has a valid cookie with:

const account = await users.find(
  (user) => (user.id === session.id)
);

We return an object with the valid property to indicate whether the credentials are valid.

credentials has the credentials for the account.

Then we call server.route to add the routes. The GET / route is the restricted route.

GET /login displays the login form.

We set options.auth to false to let us access the page without authentication.

The POST /login route lets us search for the user with the given username and password.

We validate the password with Bcrypt.compare .

And then we call h.redirect to the route we want to access if the username and password are valid.

Otherwise, we redirect to the login route.

Conclusion

We can create an app with a login page and cookie authentication easily with Hapi.

Posted in Hapi