hapi — Restrict User Access With Scopes

Building an application that allows users to sign up for your platform always involves authentication and some kind of authorization. You may have dedicated administration areas where „normal“ users shouldn’t have access to.

With hapi you can build on a great feature: scopes. Scopes are part of the authentication flow that allow simple access restrictions in your application.

In this tutorial, you’ll learn how to use hapi’s scope to allow and restrict access for individual routes.

hapi Series Overview

Scope Fundamentals: How Do They Work?

Scopes in hapi allow you to apply access restriction on individual routes. It’s a two step procedure where you need to define a scope property on a route and another scope property within the authenticated user credentials. Both scope properties are evaluated against each other.

Let’s imagine the following example: you only want registered users to access the /profile page on your platform. Therefore, you require the scope: 'user' on your route. Each user requesting the /profile page needs to have the scope: 'user' attribute set within request.auth.credentials.

Precisely, scopes always require authentication to work properly. There’s a dedicated tutorial for cookie based authentication including remember me available on Future Studio that shows you how to set a user specific object to request.auth.credentials.

Scopes can be either a single string or an array of string values. Alright, enough theory for now and let’s jump into the actual setup.

Restrict Access on Routes With Scope Requirements

In hapi you’ve dozens of route options to customize the processing to your needs. One option is the access restriction using the scope property in config.auth on your route definition. Let’s start with a sample route only accessible for admins:

server.route({  
  method: 'GET',
  path: '/admin',
  config: {
    auth: {
      strategy: 'session',
      scope: 'admin'
    },
    handler: (request, h) => {
      return h.view('admin')
    }
  }
})

Please notice the scope: 'admin' declaration that allows only users to access this route that have the admin scope in their authenticated credentials. Users without the admin scope will receive an error message with HTTP status 403 (Forbidden). This error is generated automatically within hapi. You’ll learn how to customize the error handling within a later tutorial.

The scope property can be either a string or an array of strings. That means, you can check for a single or multiple scopes.

Let’s say you want a user to have the scope of either admin or book-buyer to download a book. Registered users that didn’t buy the book are not allowed to download it, because they just have the user scope.

Your route setup can look like this:

server.route({  
  method: 'GET',
  path: '/book/{slug}/download',
  config: {
    auth: {
      strategy: 'session',
      scope: [ 'admin', 'book-buyer' ]
    },
    handler: (request, h) => {
      return h.view('admin')
    }
  }
})

If you define an array of scopes like in the snippet above, hapi evaluates them as OR. The user only needs to have one of the scopes to access this route. In a later tutorial, we’ll look at advanced scope handling. The scope of this tutorial (pun intended) is to get you going with this great feature.

Up to this point, you’ve restricted access to individual routes. Now you need to take care of the user part and assign each user one or many scopes.

Assign Scopes to Users

Required scopes on routes get validated against the user requesting that route. That means, the authenticated credentials need to have a scope property as well. The following code snippet is a hardcoded user database which we use for illustration.

Both users have the user scope assigned. The admin user also has the admin scope.

// hardcoded users object … just for illustration purposes :)
const usersDB = {  
  future: {
    username: 'future',
    password: '12345',
    name: 'Future Studio',
    scope: [ 'user' ],
    id: '1'
  },
  admin: {
    username: 'admin',
    password: '1234567890',
    name: 'Future Studio Admin',
    scope: [ 'admin', 'user' ],
    id: '2'
  }
}

If one of the two users from the illustrative database sign in to your application, you need to store at least the value for scope within request.auth.credentials. Of course you can set additional data, but it’s not affecting the access related scopes.

If you require scopes in your platform, keep a scope property for each user that signs up containing their individual scopes. Use a list of user related scopes to create advanced scenarios that allow only a segment of your users to access a given resource.

Scope Evaluation in hapi

To determine if a request is allowed to access the resource, hapi evaluates the required scope defined within config.auth.scope against the scope property defined within request.auth.credentials. Make sure to store the user’s scope into the authenticated request credentials so that both scope properties can be validated correctly.

Outlook

This tutorial walked you through the details of access restriction in hapi. Define the required scope for a route and evaluate it against the authenticated requests to allow or restrict access to the resource.

Make use of the ability to set an array of scopes for the user to enable advanced scenarios where you only give access to a segment of your user base to a route. Like, only book buyers should be able to see a book’s download page.

Authentication in hapi can cause a common issue and you’ll learn how to avoid that in the next tutorial on how to fix the unknown authentication strategy error in hapi.

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!

Explore the Library

Find interesting tutorials and solutions for your problems.