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
- What You’ll Build
- Prepare Your Project: Stack & Structure
- Environment Variables and Storing Secrets
- Set Up MongoDB and Connect With Mongoose
- Sending Emails in Node.js
- Load the User’s Profile Picture From Gravatar Using Virtuals in Mongoose
- Implement a User Profile Editing Screen
- Generate a Username in Mongoose Middleware
- Displaying Seasons and Episodes for TV Shows with Mongoose Relationship Population
- Implementing Pagination for Movies
- Implement a Watchlist
- Create a Full Text Search with MongoDB
- Create a REST API with JSON Endpoints
- Update Mongoose Models for JSON Responses
- API Pagination for TV Shows
- Customize API Endpoints with Query Parameters
- Always Throw and Handle API Validation Errors
- Advanced API Validation With Custom Errors
- Create an API Documentation with Swagger
- Customize Your Swagger API Documentation URL
- Describe Endpoint Details in Your Swagger API Documentation
- 10 Tips on API Testing With Postman
- JWT Authentication in Swagger API Documentation
- API Versioning with Request Headers
- API Login With Username and Password to Generate a JWT
- JWT Authentication and Private API Endpoints
- Refresh Tokens With JWT Authentication
- Create a JWT Utility
- JWT Refresh Token for Multiple Devices
- Check Refresh Token in Authentication Strategy
- Rate Limit Your Refresh Token API Endpoint
- How to Revoke a JWT
- Invalidate JWTs With Blacklists
- JWT Logout (Part 1/2)
- JWT “Immediate” Logout (Part 2/2)
- A Better Place to Invalidate Tokens
- How to Switch the JWT Signing Algorithm
- Roll Your Own Refresh Token Authentication Scheme
- JWT Claims 101
- Use JWT With Asymmetric Signatures (RS256 & Co.)
- Encrypt the JWT Payload (The Simple Way)
- Increase JWT Security Beyond the Signature
- Unsigned JSON Web Tokens (Unsecured JWS)
- JWK and JWKS Overview
- Provide a JWKS API Endpoint
- Create a JWK from a Shared Secret
- JWT Verification via JWKS API Endpoint
- What is JOSE in JWT
- Encrypt a JWT (the JWE Way)
- Authenticate Encrypted JWTs (JWE)
- Encrypted and Signed JWT (Nested JWT)
- Bringing Back JWT Decoding and Authentication
- Bringing Back JWT Claims in the JWT Payload
- Basic Authentication With Username and Password
- Authentication and Remember Me Using Cookies
- How to Set a Default Authentication Strategy
- Define Multiple Authentication Strategies for a Route
- Restrict User Access With Scopes
- Show „Insufficient Scope“ View for Routes With Restricted Access
- Access Restriction With Dynamic and Advanced Scopes
- hapi - How to Fix „unknown authentication strategy“
- Authenticate with GitHub And Remember the Login
- Authenticate with GitLab And Remember the User
- How to Combine Bell With Another Authentication Strategy
- Custom OAuth Bell Strategy to Connect With any Server
- Redirect to Previous Page After Login
- How to Implement a Complete Sign Up Flow With Email and Password
- How to Implement a Complete Login Flow
- Implement a Password-Reset Flow
- Views in hapi 9 (and above)
- How to Render and Reply Views
- How to Reply and Render Pug Views (Using Pug 2.0)
- How to Create a Dynamic Handlebars Layout Template
- Create and Use Handlebars Partial Views
- Create and Use Custom Handlebars Helpers
- Specify a Different Handlebars Layout for a Specific View
- How to Create Jade-Like Layout Blocks in Handlebars
- Use Vue.js Mustache Tags in Handlebars Templates
- How to Use Multiple Handlebars Layouts
- How to Access and Handle Request Payload
- Access Request Headers
- How to Manage Cookies and HTTP States Across Requests
- Detect and Get the Client IP Address
- How to Upload Files
- Quick Access to Logged In User in Route Handlers
- How to Fix “handler method did not return a value, a promise, or throw an error”
- How to Fix “X must return an error, a takeover response, or a continue signal”
- Query Parameter Validation With Joi
- Path Parameter Validation With Joi
- Request Payload Validation With Joi
- Validate Query and Path Parameters, Payload and Headers All at Once on Your Routes
- Validate Request Headers With Joi
- Reply Custom View for Failed Validations
- Handle Failed Validations and Show Errors Details at Inputs
- How to Fix AssertionError, Cannot validate HEAD or GET requests
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.
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.
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.
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.
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.
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!