Authentication

This tutorial is compatible with hapi v17

Authentication within hapi is based on the concept of schemes and strategies.

Think of a scheme as a general type of auth, like "basic" or "digest". A strategy on the other hand, is a pre-configured and named instance of a scheme.

First, let's look at an example of how to use hapi-auth-basic:

'use strict';

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

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

const validate = async (request, username, password) => {

    const user = users[username];
    if (!user) {
        return { credentials: null, isValid: false };
    }

    const isValid = await Bcrypt.compare(password, user.password);
    const credentials = { id: user.id, name: user.name };

    return { isValid, credentials };
};

const start = async () => {

    const server = Hapi.server({ port: 4000 });

    await server.register(require('hapi-auth-basic'));

    server.auth.strategy('simple', 'basic', { validate });

    server.route({
        method: 'GET',
        path: '/',
        options: {
            auth: 'simple'
        },
        handler: function (request, h) {

            return 'welcome';
        }
    });

    await server.start();

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

start();

First, we define our users database, which is a simple object in this example. Then we define a validation function, which is a feature specific to hapi-auth-basic and allows us to verify that the user has provided valid credentials.

Next, we register the plugin, which creates a scheme with the name of basic. This is done within the plugin via server.auth.scheme().

Once the plugin has been registered, we use server.auth.strategy() to create a strategy with the name of simple that refers to our scheme named basic. We also pass an options object that gets passed to the scheme and allows us to configure its behavior.

The last thing we do is tell a route to use the strategy named simple for authentication.

Schemes

A scheme is a method with the signature function (server, options). The server parameter is a reference to the server the scheme is being added to, while the options parameter is the configuration object provided when registering a strategy that uses this scheme.

This method must return an object with at least the key authenticate. Other optional methods that can be used are payload and response.

authenticate

The authenticate method has a signature of function (request, h), and is the only required method in a scheme.

In this context, request is the request object created by the server. It is the same object that becomes available in a route handler, and is documented in the API reference.

h is the standard hapi response toolkit.

When authentication is successful, you must call and return h.authenticated({ credentials, artifacts }). credentials property is an object representing the authenticated user (or the credentials the user attempted to authenticate with) Additionally, you may also have an artifacts key, which can contain any authentication related data that is not part of the user's credentials.

The credentials and artifacts properties can be accessed later (in a route handler, for example) as part of the request.auth object.

If authentication is unsuccesful, you can either throw an error or call and return h.unauthenticated(error, [data]) where error is an authentication error and data is an optional object containing credentials and artifacts. There's no difference between calling return h.unauthenticated(error) or throwing an error if no data object is provided. The specifics of the error passed will affect the behavior. More information can be found in the API documentation for server.auth.scheme(name, scheme). It is recommend to use boom for errors.

payload

The payload method has the signature function (request, h).

Again, the standard hapi response toolkit is available here. To signal a failure throw an error, again it's recommended to use boom for errors.

To signal a successful authentication, return h.continue.

response

The response method also has the signature function (request, h) and utilizes the standard response toolkit.

This method is intended to decorate the response object (request.response) with additional headers, before the response is sent to the user.

Once any decoration is complete, you must return h.continue, and the response will be sent.

If an error occurs, you should instead throw an error where the error is recommended to be a boom.

Registration

To register a scheme, use either server.auth.scheme(name, scheme). The name parameter is a string used to identify this specific scheme, the scheme parameter is a method as described above.

Strategies

Once you've registered your scheme, you need a way to use it. This is where strategies come in.

As mentioned above, a strategy is essentially a pre-configured copy of a scheme.

To register a strategy, we must first have a scheme registered. Once that's complete, use server.auth.strategy(name, scheme, [options]) to register your strategy.

The name parameter must be a string, and will be used later to identify this specific strategy. scheme is also a string, and is the name of the scheme this strategy is to be an instance of.

Options

The final optional parameter is options, which will be passed directly to the named scheme.

Setting a default strategy

You may set a default strategy by using server.auth.default().

This method accepts one parameter, which may be either a string with the name of the strategy to be used as default, or an object in the same format as the route handler's auth options.

Note that any routes added before server.auth.default() is called will not have the default applied to them. If you need to make sure that all routes have the default strategy applied, you must either call server.auth.default() before adding any of your routes, or set the default mode when registering the strategy.

Route configuration

Authentication can also be configured on a route, by the options.auth parameter. If set to false, authentication is disabled for the route.

It may also be set to a string with the name of the strategy to use, or an object with mode, strategies, and payload parameters.

The mode parameter may be set to 'required', 'optional', or 'try' and works the same as when registering a strategy.

If set to 'required', in order to access the route, the user must be authenticated, and their authentication must be valid, otherwise they will receive an error.

If mode is set to 'optional' the strategy will still be applied to the route but in this case the user does not need to be authenticated. Authentication data is optional, but must be valid if provided.

The last mode setting is 'try'. The difference between 'try' and 'optional' is that with 'try' invalid authentication is accepted, and the user will still reach the route handler.

When specifying one strategy, you may set the strategy property to a string with the name of the strategy. When specifying more than one strategy, the parameter name must be strategies and should be an array of strings each naming a strategy to try. The strategies will then be attempted in order until one succeeds, or they have all failed.

Lastly, the payload parameter can be set to false denoting the payload is not to be authenticated, 'required' or true meaning that it must be authenticated, or 'optional' meaning that if the client includes payload authentication information, the authentication must be valid.

The payload parameter is only possible to use with a strategy that supports the payload method in its scheme.