@hapi/bell Documentation

repository·master·Indexed 20 days ago

https://github.com/hapijs/bell

A third-party login plugin for the hapi.js framework that facilitates authentication via OAuth and OpenID Connect. It manages the handshake with providers, including a wide range of built-in options like Google, GitHub, and Twitter, as well as support for custom OAuth 1.0a and 2.0 protocols. The plugin handles the authorization flow and provides credentials to route handlers, though developers must implement their own long-term session management.

Tokens
6.3K
Snippets
22
Records
29
Agent score
69%

What's inside @hapi/bell

  1. What is @hapi/bell?

    master
    @hapi/bell is a third-party login plugin designed for the hapi.js web framework. While it is part of the hapi ecosystem and integrates seamlessly with hapi and its other components, it can also be used independently or with other web frameworks.
  2. How bell works and its role in authentication

    master

    bell manages the third-party OAuth authentication flow. It works by adding a login endpoint and setting it to use a bell-based authentication strategy.

    Key Lifecycle & Limitations:

    • Flow: bell manages the handshake with the provider. Once successful, it calls your route handler.
    • Handler's Role: Your handler is responsible for examining the third-party credentials (e.g., looking up an existing account in your database), setting up a local session, and redirecting the user to the application.
    • Session Management: bell does not maintain a session beyond the temporary state required for the authorization flow. Once the handler is called, you must implement your own session management (e.g., using @hapi/cookie).
    // Typical pattern: bell handles the OAuth flow, then your handler sets the local session
    server.route({
        method: ['GET', 'POST'],
        path: '/login',
        options: {
            auth: {
              mode: 'try',
              strategy: 'twitter'
            },
            handler: function (request, h) {
                if (!request.auth.isAuthenticated) {
                    return `Authentication failed due to: ${request.auth.error.message}`;
                }
    
                // 1. Use request.auth.credentials to find/create local user
                // 2. Set up local session (e.g., via @hapi/cookie)
                // 3. Redirect to application
                return h.redirect('/home');
            }
        }
    });
  3. Configure a bell authentication strategy

    master

    To use bell, register it as a plugin to your Hapi server and then define an authentication strategy using server.auth.strategy().

    Required Strategy Options:

    • provider: The name of the built-in provider (e.g., 'twitter', 'google', 'github') or a custom provider object.
    • password: A string used to encrypt the temporary state cookie.
    • clientId: The OAuth client identifier.
    • clientSecret: The OAuth client secret (can be a string or an object for custom client authentication, or a function returning a string).

    Commonly Used Options:

    • isSecure: Set to false if developing locally without HTTPS.
    • isSameSite: Set to 'Lax' if experiencing issues with local testing.
    • location: Manually set the base redirect_uri if it cannot be inferred (useful when behind a proxy).
    const Bell = require('@hapi/bell');
    const Hapi = require('@hapi/hapi');
    
    const server = Hapi.server({ port: 8000 });
    await server.register(Bell);
    
    server.auth.strategy('twitter', 'bell', {
        provider: 'twitter',
        password: 'cookie_encryption_password_secure',
        clientId: 'my_twitter_client_id',
        clientSecret: 'my_twitter_client_secret',
        isSecure: false
    });
  4. How to write a new Bell provider

    master

    To implement a custom provider for @hapi/bell, you should use the existing implementations located in the lib/providers directory of the repository as a reference. When designing your provider, consider whether you need to support the following options to meet your requirements:

    • uri: To provide a URI for the authentication process.
    • extendedProfile: To request additional user profile information from the provider.
    // Reference existing implementations in the source code:
    // lib/providers/...
  5. Configure Facebook provider

    master

    The Facebook provider defaults to the ['email'] scope. You can customize the profile fields retrieved by setting the fields option in config.

    Default fields: 'id,name,email,first_name,last_name,middle_name,gender,link,locale,timezone,updated_time,verified'.

    Profile Structure:

    {
        "id": "profile.id",
        "username": "profile.username",
        "displayName": "profile.name",
        "name": {
            "first": "profile.first_name",
            "last": "profile.last_name",
            "middle": "profile.middle_name"
        },
        "email": "profile.email",
        "raw": "profile"
    }
    // Example: Requesting specific fields
    config: {
        fields: 'id,name,email'
    }
  6. Implement a custom OAuth protocol in Bell

    master

    If your provider is not built-in, you can define a CustomProtocol in the provider option. Bell supports both oauth (v1) and oauth2 protocols.

    OAuth (v1) Configuration

    Set protocol: 'oauth' and provide:

    • auth: The authorization endpoint URI.
    • token: The access token endpoint URI.
    • signatureMethod: 'HMAC-SHA1' (default) or 'RSA-SHA1'.
    • temporary: The temporary credentials (request token) endpoint.

    OAuth2 Configuration

    Set protocol: 'oauth2' and provide:

    • auth: The authorization endpoint URI.
    • token: The access token endpoint URI.
    • scope: An array of scope strings or a function returning them.
    • useParamsAuth: Boolean; if true, client ID and secret are sent as parameters instead of an Authorization header (defaults to false).
    • pkce: Set to 'plain' or 'S256' to use proof key exchange.
    • scopeSeparator: The character used to separate scopes (e.g., , for Facebook/GitHub). Defaults to space.

    Profile Mapping

    For both protocols, you must provide a profile function (ProfileGetter) to normalize user information into the credentials.profile object.

  7. Configure Auth0 provider

    master

    To use Auth0 with Bell, you must provide a domain in the config object. The scope defaults to ['openid', 'email', 'profile'] if not specified.

    To target a specific API endpoint for tokens or to authenticate with a specific identity provider (connection), use providerParams and tokenParams.

    Profile Structure:

    {
        "id": "profile.user_id",
        "email": "profile.email",
        "displayName": "profile.name",
        "name": {
            "first": "profile.given_name",
            "last": "profile.family_name"
        },
        "raw": "profile"
    }
    // Example: Targeting a specific endpoint and connection
    providerParams: {
        endpoint: 'https://api.service.com',
        connection: 'Username-Password-Authentication'
    },
    tokenParams: {
        endpoint: 'https://api.service.com'
    }
  8. Configure Slack provider

    master

    The Slack provider defaults to the ['identify'] scope.

    • Set config.extendedProfile to false if you only require the access_token without user details.
    • To authenticate a user within a specific team, provide the team ID in providerParams.

    Profile Structure:

    {
      "scope": "params.scope",
      "access_token": "params.access_token",
      "user": "params.user",
      "user_id": "params.user_id"
    }
    // Example: Authenticating for a specific team
    providerParams: {
        team: 'T0XXXXXX'
    }
  9. Configure Cognito provider

    master

    Cognito requires a uri pointing to your Cognito user pool in the config object. The scope defaults to ['openid', 'email', 'profile'].

    Profile Structure:

    {
        "id": "profile.sub",
        "username": "profile.preferred_username",
        "displayName": "profile.name",
        "firstName": "profile.given_name",
        "lastName": "profile.family_name",
        "email": "profile.email",
        "raw": "profile"
    }
    // Example configuration
    config: {
        uri: 'https://your-cognito-user-pool.amazoncognito.com'
    }
  10. Configure Instagram provider

    master

    The Instagram provider defaults to the ['basic'] scope. You can fetch more information by setting extendedProfile: true in the config object.

    Profile Structure:

    {
        "id": "params.user.id",
        "username": "params.user.username",
        "displayName": "params.user.full_name",
        "raw": "params.user"
    }
    // Example: Fetching extended profile
    config: {
        extendedProfile: true
    }
  11. Configure LinkedIn provider

    master

    The LinkedIn provider defaults to the ['r_basicprofile', 'r_emailaddress'] scope. To request additional profile fields, use the fields option within providerParams.

    Profile Structure:

    {
        "id": "profile.id",
        "name": {
            "first": "profile.firstName",
            "last": "profile.lastName"
        },
        "email": "profile.email",
        "headline": "profile.headline",
        "raw": "profile"
    }
    // Example: Requesting specific LinkedIn fields
    providerParams: {
        fields: ':(id,first-name,last-name,positions,picture-url,picture-urls::(original),email-address)'
    }
  12. Configure Okta provider

    master

    Okta requires a uri pointing to your organization's Okta instance in the config object. If you are using a custom authorization server, you must provide the authorizationServerId in the config.

    Profile Structure:

    {
        "id": "profile.sub",
        "username": "profile.email",
        "displayName": "profile.nickname",
        "firstName": "profile.given_name",
        "lastName": "profile.family_name",
        "email": "profile.email",
        "raw": "profile"
    }
    // Example: Using a custom authorization server
    config: {
        uri: 'https://your-org.okta.com',
        authorizationServerId: 'abc123xyz'
    }