@hapi/bell Documentation
repository·master·Indexed 20 days ago
https://github.com/hapijs/bellA 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.
What's inside @hapi/bell
- @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.
How bell works and its role in authentication
masterbell 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'); } } });Configure a bell authentication strategy
masterTo 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 tofalseif developing locally without HTTPS.isSameSite: Set to'Lax'if experiencing issues with local testing.location: Manually set the baseredirect_uriif 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 });How to write a new Bell provider
masterTo implement a custom provider for
@hapi/bell, you should use the existing implementations located in thelib/providersdirectory 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/...Configure Facebook provider
masterThe Facebook provider defaults to the
['email']scope. You can customize the profile fields retrieved by setting thefieldsoption inconfig.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' }Implement a custom OAuth protocol in Bell
masterIf your provider is not built-in, you can define a
CustomProtocolin theprovideroption. Bell supports bothoauth(v1) andoauth2protocols.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 tofalse).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
profilefunction (ProfileGetter) to normalize user information into thecredentials.profileobject.Configure Auth0 provider
masterTo use Auth0 with Bell, you must provide a
domainin theconfigobject. Thescopedefaults 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
providerParamsandtokenParams.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' }Configure Slack provider
masterThe Slack provider defaults to the
['identify']scope.- Set
config.extendedProfiletofalseif you only require theaccess_tokenwithout user details. - To authenticate a user within a specific team, provide the
teamID inproviderParams.
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' }- Set
Configure Cognito provider
masterCognito requires a
uripointing to your Cognito user pool in theconfigobject. Thescopedefaults 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' }Configure Instagram provider
masterThe Instagram provider defaults to the
['basic']scope. You can fetch more information by settingextendedProfile: truein theconfigobject.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 }Configure LinkedIn provider
masterThe LinkedIn provider defaults to the
['r_basicprofile', 'r_emailaddress']scope. To request additional profile fields, use thefieldsoption withinproviderParams.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)' }Configure Okta provider
masterOkta requires a
uripointing to your organization's Okta instance in theconfigobject. If you are using a custom authorization server, you must provide theauthorizationServerIdin theconfig.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' }