hapi — Authenticate with GitLab And Remember the User

Securing routes with authentication mechanisms is a common way to allow only selected people access to a given resource. If you’re tired of implementing boilerplate code for login and signup again, benefit from hapi’s powerful plugin system and integrate with third-party services to leverage OAuth access.

This tutorial guides you through the integration of your hapi app with GitLab and shows you how to let users authenticate with their GitLab account for your platform.

hapi Series Overview

Prepare Your Project

At first, you need to install two dependencies: bell to simply integrate OAuth within your application for a given provider (e.g., GitLab, Twitter & Co.) and hapi-auth-cookie to remember the user’s successful login.

npm i -S bell hapi-auth-cookie  

Once you’ve the two packages successfully installed, move on to the next step and create an OAuth application on GitLab.

Register new OAuth Application on GitLab

Using GitLab in your application to authenticate users requires a dedicated OAuth application on GitLab. This step is required, because you want users to explicitly authenticate for your application, not anyone else’s.

Once the user grants your application access to the user’s data, GitLab will request a provided URL and sends the credentials as payload.

In this tutorial we’ll create an OAuth application on gitlab.com, but you can use your self-hosted GitLab as well. To point bell to a self-hosted GitLab instance, please refer to the provider configuration details.

To create your OAuth application on GitLab, move over to your GitLab settings and visit the „Applications“ tab.

Register new GitLab OAuth Application

Provide a name and please keep an eye on the Redirect URI, because this URL is called once the user grants your application access to their data. And you want GitLab to call an URL that belongs to your application :)

During development you can use a localhost address. The picture above depicts http://localhost:3000/auth/gitlab as the redirect URL and we need to listen on that route within our server to handle the call properly.

Click Save application to create it.

New GitLab OAuth Application

Alright, there you go. Keep this tab open, because you need the values for Application Id and Secret in a minute. Verify the Callback url if everything is correct. Please use your own values and don’t use them from the screenshot. The app won’t be available at the time you’re reading this :)

Create a gitlab Authentication Strategy

In the first step, you’ve installed bell and now you’ll create a new authentication strategy based on the bell scheme.

You can choose whatever strategy name you want, we’ll use gitlab for the rest of this tutorial. That it’s easily referable and you immediately know what’s behind this strategy.

We suggest to create a dedicated authentication plugin and add your strategy this way:

async function register (server, options) {  
  // declare and install dependency to bell
  await server.register({
    plugin: require('bell')
  })

  /**
   * Register 'gitlab' authentication strategy
   */
  server.auth.strategy('gitlab', 'bell', {
    provider: 'gitlab',
    password: 'ThisIsASecretCookiePasswordForGitLab',
    clientId: 'your-application-id',
    clientSecret: 'your-secret',
    isSecure: process.env.NODE_ENV === 'production'
  })

  server.log('info', 'Auth strategy created: »gitlab«')
  server.log('info', 'Plugin registered: authentication with strategy »gitlab«')
}

exports.plugin = {  
  register,
  name: 'authentication',
  version: '1.0.0'
}

With bell, you’ve multiple settings that can be configured while creating the strategy. The settings from above are sufficient to integrate GitLab with your application.

The gitlab provider is a predefined provider in bell that sets the required OAuth URLs to complete the OAuth flow. If you self-host GitLab, adjust the bell settings to point to your domain so that the correct GitLab instance is referenced.

clientId and clientSecret are the values of your GitLab OAuth application. On GitLab, the values are called Application Id and Secret. Both values are required to identify your application and the callback with the user’s data as response payload.

The password is used for a cookie that will be set and isSecure defines whether the cookie is used on encrypted (HTTPS) routes.

Add a Route That Requires gitlab Authentication Strategy

The authentication strategy is prepared, now go ahead and add a route that requires the gitlab strategy. Remember the callback URL from the GitLab OAuth application? The path for the new route needs to match the defined URL.

Above, you’ve seen that we used http://localhost:3000/auth/gitlab for the GitLab callback URL. Now you need to add a route on path /auth/gitlab to your server.

server.route({  
  method: 'GET',
  path: '/auth/gitlab',
  config: {
    auth: 'gitlab',
    handler: (request, h) => {
      if (request.auth.isAuthenticated) {
        const user = request.auth.credentials.profile
        const data = {
          name: user.name,
          username: user.username,
          avatar: user.avatar_url
        }

        return h.view('authenticated', data)
      }

      return h.view('index', {
        error: 'Could not authenticate with GitLab.'
      }).code(400)
    }
  }
})

With gitlab as the required authentication strategy, bell makes sure you’re redirected to GitLab first where you’re asked to grant the given application access to your data. You only need to allow the access once, afterwards each authentication request will finish without further questions for this app (unless you revoke access to this app).

Once you allow the created OAuth application to use your GitLab data, bell continues and finishes the OAuth flow and ultimately your endpoint is called.

Within your route, the GitLab data is available within request.auth.credentials, including an API access token if you need to call the GitLab API separately.

The GitLab profile credentials are stored within a nested object request.auth.credentials.profile. Pick the data you need and finally show the resource that is secured with GitLab authentication.

Test Your Code

If you’ve taken the presented snippets from above and put them into your application, test your integration and hopefully everything goes smooth.

We’ve created a sample project to authenticate your hapi app with GitLab and published over at GitHub. It adds basic view support and has two views: one that requires you to authenticate with GitLab and another one shown once the authentication flow completed successfully.

If you’ve completed the GitLab integration within your application, start your server and visit the route that requires GitLab authentication. The following screen uses the mentioned sample project from above.

Sign in with GitLab view

Click the link to „Authenticate with GitLab“ which results in the OAuth web flow that redirects you to GitLab and asks for permission to access your personal user data.

Authorize Application on GitLab

Once you click „Authorize“, you’ll be redirected to the defined app’s callback url and with that to your application. Within your route handler, do the required processing and use the user credentials for good.

Signed in with GitLab

The sample application shows a simple greeting and your GitLab avatar. We’ve also added an extra functionality to save a cookie and remember the user’s login.

The following paragraph shows you how to leverage hapi-auth-cookie to remember the user.

Remember the User with hapi-auth-cookie

The downside of the previous implementation is that all requests to your routes that require the gitlab authentication strategy will do the roundtrip to GitLab, ask for an authorization grant and get back to your route handler once the OAuth flow finished.

Add a second authentication strategy within your server that uses a session cookie for authentication. Combine third-party authentication with bell and hapi-auth-cookie by using a cookie that gets set once your GitLab authentication is successful.

Therefore, extend the authentication plugin from above with a new session strategy:

async function register (server, options) {  
  // declare and install dependency to bell
  await server.register([
    {
      plugin: require('bell')
    },
    {
      plugin: require('hapi-auth-cookie')
    }
  ])

  /**
   * Register session based auth strategy to store
   * credentials received from GitLab and keep
   * the user logged in
   */
  server.auth.strategy('session', 'cookie', {
    password: 'ThisIsASecretPasswordForTheAuthCookie',
    redirectTo: '/',
    isSecure: process.env.NODE_ENV === 'production'
  })

  server.log('info', 'Auth strategy created: »session«')

  /**
   * Register 'gitlab' authentication strategy
   */
  server.auth.strategy('gitlab', 'bell', {
    provider: 'gitlab',
    password: 'ThisIsASecretCookiePasswordForGitLab',
    clientId: 'your-application-id',
    clientSecret: 'your-secret',
    isSecure: process.env.NODE_ENV === 'production'
  })

  server.log('info', 'Auth strategy created: »gitlab«')

  server.log('info', 'Plugin registered: authentication with strategies »session« and »gitlab«')

}

exports.plugin = {  
  register,
  name: 'authentication',
  version: '1.0.0'
}

With the session strategy in place, you can add a single line of code to your route handler on path /auth/gitlab to store the desired data and remember the user.

server.route({  
  method: 'GET',
  path: '/auth/gitlab',
  config: {
    auth: 'github',
    handler: (request, h) => {
      if (request.auth.isAuthenticated) {
        const user = request.auth.credentials.profile
        const data = {
          name: user.name,
          username: user.username,
          avatar: user.avatar_url
        }

        request.cookieAuth.set(data)     // <-- new line to set the session cookie

        return h.view('authenticated', data)
      }

      return h.view('index', {
        error: 'Could not authenticate with GitLab.'
      }).code(400)
    }
  }
})

The snippet above saves the name, username, and avatar properties into the session cookie. You can add other fields as well.

Now you can use the session strategy for all routes that require authentication. Check for an existing cookie and use the stored data. If the session cookie is invalid or expired, redirect to your route that requires GitLab authentication so that the user does the OAuth flow and finally creates a new session.

Sample Code and Project

We’ve published a sample project for this tutorial on GitHub containing the code that is used to integrate a hapi project with GitLab. If you feel stuck with the integration in your own application, sneak peek at our code snippets to get a grasp on how to do the connection. We hope that helps :)

Outlook

You made it :)

With assistance of this tutorial, you’re able to integrate your hapi app with GitLab and use the OAuth flow to access user credentials. And you don’t necessarily need to implement your own login and signup boilerplate. With bell and the GitLab integration there are the foundations to authenticate users for secured resources on your platform.

We’re happy to hear your thoughts and comments. Please don’t hesitate to use the comments below or find us on Twitter @futurestud_io. Let us know what you think!

Enjoy coding & make it rock!


Additional Resources

Explore the Library

Find interesting tutorials and solutions for your problems.