npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hapiboo/module-auth

v2.0.1

Published

MK13 Studio Hapiboo - Auth Module

Readme

@hapiboo/module-auth

Authentication and authorization for HapiBoo applications: password credentials, registration and invitations, JWT access and refresh tokens, account membership, roles and permissions, and Express routes and middleware.

Persistence uses @hapiboo/flux and your database provider. Applications supply email delivery, account provisioning, token data, and deployment configuration.

Installation

pnpm add @hapiboo/module-auth @hapiboo/core @hapiboo/flux @hapiboo/server express
pnpm add -D typescript @types/express @types/node

Enable experimentalDecorators in TypeScript applications using Flux models. The package contains JavaScript and TypeScript declarations. Its public entry point exports auth, the Credential, User, Permission, Role, and UserAccess models, configuration interfaces, and Express request interfaces.

Application Setup

import express from 'express';
import type { Express } from 'express';
import { auth } from '@hapiboo/module-auth';
import type { IAuthPerformers, IAuthSettings } from '@hapiboo/module-auth';
import { fluxApplication } from '@hapiboo/flux';
import type { IDBProviderFlux } from '@hapiboo/flux';
import { routingProvider } from '@hapiboo/server';

export function configureAuth(
    jwtSecret: string,
    settings: IAuthSettings,
    performers: IAuthPerformers,
    provider: IDBProviderFlux
): Express {
    const app = express();
    app.use(express.json());

    auth.init(jwtSecret, settings, performers);
    fluxApplication.registerModule(auth.module);
    fluxApplication.initialize(provider);
    fluxApplication.start();
    routingProvider.processApp(app, auth.routes);

    return app;
}

Register all application modules before initializing and starting Flux. Coordinate database readiness before exposing routes. auth.module registers the five auth models; initial security data and the first administrator are created separately through auth.services.authentication.general.initData(baseURL).

Use a securely generated JWT signing secret and HTTPS in deployment. Configuration and caches are shared within the loaded module instance. HTTP controllers obtain the frontend base URL from process.env.FRONTEND_URL; set it before requests that generate confirmation, invitation, or recovery email links.

Settings

Pass a complete IAuthSettings object to auth.init(jwtSecret, settings, performers):

| Setting | Purpose | | --- | --- | | allowSelfRegistration | Whether users may register without an invitation. | | allowMultiLogin | Multi-login configuration flag exposed by the settings service. | | allowTestMode | Controls use of the fixed confirmation code when test mode is active. Keep disabled in production. | | passwordHashLength | Password hash length passed to the crypto package. | | passwordHashIterations | Password hash iteration count passed to the crypto package. | | firstAdmin | Initial administrator's name and email. | | overrulingPermission | Permission ID that satisfies every permission check, or undefined. | | getPermissions? | Supplies initial Permission models. | | getRoles? | Supplies initial Role models. |

Choose hashing parameters according to the crypto provider and your deployment's security and performance requirements. Initial role and permission definitions are inserted when their IDs are absent; existing definitions are retained.

Application Performers

IAuthPerformers integrates the module with application-specific services:

| Method | Return and responsibility | | --- | --- | | sendConfirmation(emailAddress, displayName, isInvited, confirmationLink) | IResponsePromiseVoid; deliver a registration or invitation link. | | sendRecover(emailAddress, recoverLink) | IResponsePromiseVoid; deliver a password recovery link. | | sendAfterPasswordChanged(emailAddress) | IResponsePromiseVoid; deliver a password-change notification. | | afterRegistration(account_id, user_id, account_name, account_email) | IResponsePromise<{ id: string }>; provision application account data and return its ID. | | getTokenPayload<T>(user) | IResponsePromise<T>; supply application data included in standard users' access tokens. |

IAuthEmailLink contains url and title. Generated frontend paths are confirm/?code=..., invitation/?code=..., and recover/?code=.... Performers report failures through rejected HapiBoo operations using ResponseError. These are the callback-based operations from @hapiboo/core, not native Promises; use .then, .catch, .finally, and core composition helpers rather than await or native Promise combinators.

Authentication Services

Access these through auth.services.authentication. Service calls are trusted server-side APIs: the application is responsible for authorizing the caller and validating externally supplied identifiers and values.

Password login, refresh login, and password changes share the same eligibility rule: both the user and credential must have status active. This check precedes password changes and token generation. Newly registered or invited users activate their accounts through the confirmation actions instead.

| Namespace and method | Purpose | | --- | --- | | general.initData(baseURL) | If there are no users, initialize security definitions and invite the configured first administrator. Returns { id }, with an empty ID when users already exist. | | general.testMode() | Toggle process-local test mode and return { testMode }. | | general.get(user_id) | Retrieve a user model. | | me.passwordLogin(name, password) | Authenticate an active user and password credential and return { token, refreshToken }. | | me.refreshTokenLogin(user_id, refreshCode) | Issue tokens using a stored credential refresh code. | | me.changePassword(user_id, credentialName, oldPassword, newPassword) | Request a password change, token generation, and notification. | | me.get(user_id) | Return profile data, account ID, admin flag, and permission IDs. | | me.update(user_id, name) | Update the user's display name. | | user.registerPassword(credentialName, userDisplayName, userEmailAddress, password, account_name, account_email, baseUrl) | Register a pending standard user and invoke account provisioning; returns { user, account }. | | user.invite(account_id, credentialName, displayName, emailAddress, baseUrl) | Invite a standard user into an existing account; returns { id }. | | user.updatePayload(user_id, account_id, payload) | Return a newly signed standard-user access token with application data. | | user.remove(user_id, currentUserId, account_id) | Remove another standard user from the specified account, with associated access records and credentials. | | user.getForAccount(account_id) | Retrieve standard users for an account. | | admin.invite(name, emailAddress, displayName, baseURL) | Invite an administrator; returns { id }. | | admin.remove(id, currentUserId) | Remove another administrator and associated credentials. | | admin.getAll() | Retrieve administrator user models. | | actions.confirmRegistration(code, password) | Confirm a pending registration and return tokens. | | actions.confirmInvitation(code, password) | Accept an invitation with a password and return tokens. | | actions.resendConfirmation(user_id, baseUrl) | Send the existing confirmation or invitation link for a newly created user. | | actions.forgotPassword(credentialName, baseUrl) | Generate a recovery code, store it, and send a recovery link. | | actions.recoverPassword(recovery, password) | Apply a valid recovery code and password and return tokens. |

Application services control account provisioning, transactional boundaries, request throttling, password policy, and public error presentation.

HTTP Routes

auth.routes mounts the following routes under /auth. Install an Express JSON body parser before mounting them. Field names below describe JSON request bodies.

| Method and path | Input | Module middleware | | --- | --- | --- | | PUT /auth/init | No body. Initialize security data and the first administrator when there are no users. | None. | | POST /auth/init/test | No body. Toggle process-local test mode. | None. | | POST /auth/user/register | name, login, password. Login also supplies user/account email; name supplies account name. | None. | | POST /auth/user/confirm | code, password. | None. | | POST /auth/user/invite | code, password. Accepts an existing invitation; does not create one. | None. | | POST /auth/password/login | login, password. | None. | | POST /auth/password/forgot | email, used as the credential name. | None. | | POST /auth/password/recover | recovery, password. | None. | | POST /auth/password/refresh | Refresh token in the authorization header; no body. | refresh. | | POST /auth/password/password | email, oldPassword, newPassword. | simple. | | GET /auth/profile | No body. | simple. | | POST /auth/profile | name. | simple. | | GET /auth/access/roles | No body. | simple. | | GET /auth/access/permissions | No body. | simple. |

Restrict initialization and test-mode endpoints through application-level access controls. allowTestMode controls fixed-code generation; it does not protect the test-mode route. Apply request throttling and your password policy at the public API boundary. Credential-name normalization should be consistent across registration, invitations, login, and password recovery; password login lowercases the supplied name.

Password values are passed as strings to the crypto provider without Base64 decoding by this module. Use the same string representation consistently for registration, login, confirmation, recovery, and password changes.

Authorization Services

Use auth.services.authorization:

| Namespace and method | Purpose | | --- | --- | | general.initializeSecurityModel() | Ensure configured initial permissions and roles exist. | | permission.create(name) / permission.edit(id, name) / permission.all() | Manage permission definitions. | | role.create(name, description, lucide_icon, permissions) | Create a role containing permission IDs. | | role.edit(id, name, description, lucide_icon, permissions) / role.all() | Update or list role definitions. | | access.byRole.assign(account_id, user_id, role_id) | Materialize a role's permissions as user-access records. | | access.byRole.assignList(account_id, user_id, role_ids) | Replace role-derived access records and return the user's resulting access records. | | access.byRole.remove(account_id, user_id) | Remove role-derived access records. | | access.byPermission.assign(user_id, permission_id) / assignList(user_id, permission_ids) | Reserved functions; currently return void without writing records. | | access.byPermission.remove(account_id, user_id) | Returns a successful void operation without changing records. | | access.removeAllForUser(user_id) | Remove all persisted user-access records. | | access.getForAccount(account_id) / access.getForUser(user_id) | Retrieve access records. | | access.split(records) | Separate role-derived and direct permission records, representing each role once. | | middleware.permission(permission_id) | Express middleware requiring the permission or the configured overruling permission. |

Role assignment stores the permission IDs contained in the role at assignment time. Middleware loads permission IDs into a process-local cache on first use. Access-record writes through the authorization service invalidate the affected user's cached permissions in the current process. Subsequent checks reload the records. Results from reads started before invalidation are discarded; failed writes also invalidate the cache because the persisted outcome may be uncertain.

While permissions are loading, middleware checks again every 100 ms using a one-second wait budget measured from the start of each request's permission check. Read failures and cache invalidation do not reset that budget. Once it expires, middleware returns 403 with permission data loading, try again later and stops scheduling checks for that request. Later read completion can populate the cache for subsequent requests but does not resume the expired request.

Each process has its own cache. Applications coordinate invalidation across workers and changes made directly in storage. Editing a role definition does not rewrite previously materialized user-access records.

Security Services and Middleware

Access these through auth.services.security. initialize(secret) sets the JWT secret and clears registered client secrets; auth.init already calls it. registerClient(client_secret) adds a client secret accepted by client middleware.

| Token factory | Result | | --- | --- | | token.data(user_id, account_id, admin, expirationInHours, data) | Signed token containing identity, account, admin flag, and optional application data. | | token.refresh(user_id, refreshCode, expirationInHours) | Signed token containing user_id and refresh code. | | token.client(client) | Signed client token valid for one hour. |

The authentication service issues access tokens valid for 24 hours and refresh tokens valid for 720 hours. Requests use Authorization: Bearer <token>.

| Middleware | Request payload and checks | | --- | --- | | client | Verify a JWT with a client value matching a registered client secret. | | simple | Verify the JWT and populate user_id and admin; sets account_id to undefined. | | account | Verify the JWT and populate user_id, account_id, and admin. | | full<TData> | As above, with optional application data. | | refresh | Verify the JWT and populate user_id and code. | | checkAdmin | Run simple, then require a truthy admin flag. | | checkAccount | Run account, then require a truthy account ID. | | checkData<TData> | Run full, then require truthy application data. |

Basic middleware populates claims; use the appropriate additional checks for the route's contract. Access and refresh tokens share the configured signing secret; basic JWT middleware verifies the signature and expiration without applying a token-purpose discriminator or rereading user and credential status. Applications define their required claim validation and account-state policy.

Permission middleware consumes an existing request.payload, so identity middleware must run first. For example:

import { Router } from 'express';
import { auth } from '@hapiboo/module-auth';
import type { IFluxRequest } from '@hapiboo/module-auth';

const router = Router();
router.get(
    '/reports',
    auth.services.security.middleware.checkAccount,
    auth.services.authorization.middleware.permission('reports.read'),
    (request: IFluxRequest, response) => {
        response.json({ account_id: request.payload!.account_id });
    }
);

IFluxRequest exposes the identity payload; IFluxTokenRequest exposes refresh claims; IFluxDataRequest<TData> adds typed application data. Application routes remain responsible for account-scoping data access and checking resource ownership.

Models and Storage

| Model | Storage table | Main data | | --- | --- | --- | | User | authentication.users | Identity, account ID, standard/admin type, created/active/banned/deleted status, profile, and timestamps. | | Credential | authentication.credentials | Credential name and user ID, password/sso/token type, active/pending/revoked status, password data, confirmation/invitation/recovery codes, and refresh code. | | Permission | authentication.permissions | Permission ID and name. | | Role | authentication.roles | Role ID, name, description, icon name, and permission ID array. | | UserAccess | authentication.user_access | Account ID, user ID, permission ID, and optional role ID. |

Models expose clone and creation/update helpers. Credential includes helpers for password checking, password changes, confirmation, invitation, recovery, refresh-code assignment, and invalidation. UserAccess can represent role-derived or directly assigned permissions. Apply Flux decorators only to final model classes; do not derive new decorated models from these classes.

The provider must persist and reconstruct the complete model, including nested password/profile data and fields without column decorators. Decorated columns also supply separately queryable attributes and index definitions. Do not return raw credential records to public clients; choose explicit response fields.

License

ISC.