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

@mohitebiz/white-labeled-auth

v1.0.16

Published

White-labeled authentication & onboarding SDK. Works across React, Angular, Vue and Vanilla JS.

Downloads

1,118

Readme

@black-ink-technologies/white-labeled-auth

Framework-agnostic SDK for ChainIT Hosted Auth — a fully managed, branded onboarding flow that covers OTP, face liveness, and ID capture in ChainIT-hosted screens. Drop the SDK into any React / Vue / Angular / vanilla-JS app, point it at your backend's client-session proxy, and receive standard OIDC tokens via callback.

Full product documentation: https://develop-sdk.chainit.online/docs/applications/hosted-auth


How it fits together

Browser (your app)              Your backend                ChainIT API
─────────────────               ────────────                ───────────
HostedAuthProvider     ────►    GET /api/hosted-auth/   ──► POST /users/v1/hosted-auth/client-session
  config.session.getSession      client-session              (clientId + clientSecret, server-side)

                       ◄────                            ◄── { sessionId, sessionSignature }

                       ◄────    sanitized session

SDK renders hosted UI  ──────────────────────────────►  Hosted Auth flow

                       ◄──────────────────────────────  accessToken, idToken, refreshToken

callbacks.onSuccess(tokens)

clientSecret never reaches the browser — your backend proxies the client-session call and returns only { sessionId, sessionSignature }.

Prerequisites

  1. A Hosted Auth application created in the Developer Portal (or via POST /dev-portal/v1/oauth-apps with "type": "HOSTED_AUTH").
  2. clientId / clientSecret stored as server-side only environment variables.
  3. A backend route on your side that proxies POST /users/v1/hosted-auth/client-session and returns the sanitized session payload ({ sessionId, sessionSignature }).

Installation

npm install @black-ink-technologies/white-labeled-auth

Peer requirements: react >= 18, axios, @tanstack/react-query, react-hook-form, jwt-decode, uuid, and zod (optional, used by request/response validation).


React usage

import {
  HostedAuthProvider,
  InitiateAuthFlow,
  ReverifyFace,
  type HostedAuthConfiguration,
} from "@black-ink-technologies/white-labeled-auth";

const config: HostedAuthConfiguration = {
  clientId: "<<your_client_id>>",
  session: {
    getSession: async () => {
      const res = await fetch("/api/hosted-auth/client-session", {
        method: "POST",
      });
      if (!res.ok) throw new Error("Session fetch failed");
      return res.json();
    },
  },
  callbacks: {
    onSuccess: (tokens) => {
      logger.log("Auth success:", tokens);
    },
    onError: (error) => {
      logger.error("Auth error:", error);
    },
  },
  config: {
    face: { timeout: 30_000 },
  },
};

export function App() {
  return (
    <HostedAuthProvider config={config}>
      <InitiateAuthFlow initialScreen="signin" />
      {/* Or render only standalone re-verification when the user is already signed in */}
      {/* <ReverifyFace onSuccess={() => {}} onClose={() => {}} /> */}
    </HostedAuthProvider>
  );
}

HostedAuthProvider loads app branding and theme once, then shares it across all hosted-auth children.

Mobile QR face handoff

When the desktop browser cannot use a camera during face liveness, the SDK can show a QR code. Scanning that QR on a phone opens the same integrator page that started the auth flow, with a one-time handoff code in the URL (?h=<code>).

No extra route or consumer wiring is required. HostedAuthProvider automatically detects the handoff code and renders the mobile face-scan handoff surface instead of the app children, so this works whether the phone browser is logged in or logged out. The phone page only displays the face scan; it does not continue the normal login/profile flow.

Handoff query param. The code is carried on a query param named h by default. If h collides with something your app already uses, rename it with handoffParam on HostedAuthConfiguration:

const config: HostedAuthConfiguration = {
  clientId: "<<your_client_id>>",
  session: { getSession },
  handoffParam: "cv", // QR becomes https://your-app.com/login?cv=<code>
};

The same value is sent to the backend when minting the QR, so the QR, the URL, and the SDK's detection all stay in sync. It must be a URL-safe key (letters, digits, _, -; max 32 chars); anything else falls back to h.

⚠️ Reserve this param. The SDK reads (and on success deletes) ?h=<code> — or your custom handoffParam — on every page wrapped by HostedAuthProvider. Do not reuse the handoff param for any other purpose in your app, or a normal page load could be misread as a face handoff.

Security and cleanup:

  • The face scan runs in the embedded-host secondary face iframe.
  • The iframe sends the integrator origin as X-Origin; the backend validates it against the Hosted Auth app's allowlist before resolving the handoff code.
  • Any stale Hosted Auth request token on the integrator origin is cleared before the iframe loads.
  • The resolved request token and QR id are kept in iframe memory only and cleared on completion/cancel.
  • On success, the SDK removes the handoff param from the phone URL; the backend also burns the handoff code.

If you need to handle the handoff yourself, pass disableFaceHandoffAutoMount to HostedAuthProvider and mount FaceHandoff on your own route. Most integrations should leave the default auto-mount behavior enabled.


Plain JS / UMD usage

<div id="hosted-auth-root"></div>
<script src="https://unpkg.com/@black-ink-technologies/white-labeled-auth@latest/dist/sdk/chainit-auth.umd.js"></script>
<script>
  async function getSession() {
    const res = await fetch("/api/hosted-auth/client-session", {
      method: "POST",
    });
    if (!res.ok) throw new Error("Session fetch failed");
    return res.json();
  }

  const hosted = window.HostedAuth.configure({
    clientId: "<<your_client_id>>",
    session: { getSession },
    callbacks: {
      onSuccess: (tokens) => console.log("Auth success:", tokens),
      onError: (error) => console.error("Auth error:", error),
    },
    config: { face: { timeout: 30000 } },
  });

  hosted.initiateAuthFlow("#hosted-auth-root", { screen: "signin" });

  // Logout from anywhere (e.g. a separate Sign-out button)
  // const result = await hosted.logout();
  // if (!result.success) console.warn("Server logout failed:", result.message);

  // Tear down the host element (SPA navigation, mobile WebView dismiss)
  // hosted.destroy("#hosted-auth-root");
</script>

configure() eagerly initialises the SDK's HTTP layer and token storage, so hosted.logout() (or window.HostedAuth.logout()) is callable anywhere on the page — including settings screens or sign-out buttons that live outside #hosted-auth-root. Both hosted and window.HostedAuth expose the same imperative API.

The UMD bundle exposes the SDK as window.HostedAuth (with window.ChainItAuth kept as a backward-compatible alias).


Configuration

interface HostedAuthConfiguration {
  clientId: string;
  session: {
    getSession: () => Promise<{ sessionId: string; sessionSignature: string }>;
  };
  callbacks?: {
    onSuccess?: (tokens: {
      accessToken: string;
      idToken?: string;
      refreshToken?: string;
    }) => void;
    onError?: (error: unknown) => void;
  };
  config?: {
    face?: {
      timeout?: number;
    };
  };
  handoffParam?: string;
}

| Field | Type | Required | Notes | | --------------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------ | | clientId | string | ✅ | OAuth client ID for the application. Sent on every hosted-auth + branding request. | | session.getSession | function | ✅ | Returns { sessionId, sessionSignature } from your backend's /client-session proxy. | | callbacks.onSuccess | function | – | Receives { accessToken, idToken?, refreshToken? } once onboarding completes. | | callbacks.onError | function | – | Receives fatal SDK errors that the built-in retry UI could not recover from. | | config.face.timeout | number | – | Liveness detection timeout in milliseconds. Defaults to 30000. | | handoffParam | string | – | Query-param name for the mobile QR face-handoff code. URL-safe key (max 32 chars); defaults to h. Reserve it for the handoff only. |


Component reference

HostedAuthProvider

| Prop | Type | Required | Description | | ----------------------------- | ------------------------- | :------: | ------------------------------------------------------------------------------------------------------ | | config | HostedAuthConfiguration | ✅ | Session resolver + callbacks + optional feature flags. | | children | ReactNode | ✅ | The hosted-auth surface (InitiateAuthFlow / ReverifyFace). | | disableFaceHandoffAutoMount | boolean | – | Opt out of automatic ?h=<code> mobile QR handoff handling. Defaults to false (auto-mount enabled). |

InitiateAuthFlow

Drives the full onboarding journey: sign-in / sign-up → OTP → consents → face liveness → ID scan → token issuance.

| Prop | Type | Default | Description | | --------------- | -------------------------------- | ------------------------------------------ | ------------------------------------- | | initialScreen | 'signin' \| 'signup' \| 'face' | server-resolved (falls back to 'signin') | Deep-link to a specific entry screen. |

ReverifyFace

Step-up face liveness for users who are already signed in. Use for high-value or sensitive actions (payments, account changes) or periodic re-proofing. Requires a stored access token (tokenStorage.setTokens) and HostedAuthProvider in the tree.

| Prop | Type | Required | Description | | ----------- | ------------------------------------ | :------: | ----------------------------------------------------------------------------------------------------------------- | | onSuccess | () => void \| Promise<void> | – | Fires when face liveness is confirmed and the user is re-verified. | | onError | (error: AuthError \| null) => void | – | Fires on a fatal re-verification failure. The SDK shows a built-in retry UI before bubbling unrecoverable errors. | | onClose | () => void | – | Fires when the user dismisses the camera UI or completes verification. |

FaceHandoff

Advanced mobile QR handoff surface. Normally you do not mount this component yourself because HostedAuthProvider auto-mounts it when the page is opened with ?h=<code>. It is exported for integrations that opt out with disableFaceHandoffAutoMount and want to route the handoff manually.

| Prop | Type | Required | Description | | ----------- | ------------------------------------ | :------: | ------------------------------------------------------------------------------------------------------ | | onSuccess | () => void | – | Fires after the phone-side face verification succeeds. The waiting desktop is notified by the backend. | | onError | (error: AuthError \| null) => void | – | Fires on terminal phone-side handoff errors or cancellation. |


Imperative JS API (HostedAuth)

HostedAuth.configure(options) returns an instance with three methods:

| Method | Signature | Purpose | | ------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | initiateAuthFlow | (container?, options?: { screen?: 'signin' \| 'signup' \| 'face' }) => void | Mount the full onboarding flow. container is a CSS selector or HTMLElement; omit to use the SDK's default root. | | reverifyFace | (container?) => void | Mount the standalone face re-verification overlay. | | destroy | (container?) => void | Unmount and clean up. Omitting container also resets internal SDK state. | | logout | (options?: LogoutOptions) => Promise<LogoutResult> | Revoke the active session on the server (/oauth/logout) and clear local tokens. See Logout below. |

The same calls are also available as static methods on HostedAuth for backward compatibility (HostedAuth.initiateAuthFlow(...), HostedAuth.render(...), HostedAuth.destroy(...), HostedAuth.logout(...)).


User profile (UserInfo API)

Fetch the authenticated user's profile from /oauth/userInfo. The SDK's axios interceptor automatically attaches the stored access token — no token parameter needed.

React

import { useUserInfo } from "@black-ink-technologies/white-labeled-auth";

function Profile() {
  const { data: userInfo, isLoading, error, refetch } = useUserInfo();
  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  return <p>Hello, {userInfo?.name ?? userInfo?.email}</p>;
}

Imperative JS / UMD

import {
  HostedAuth,
  fetchUserInfo,
} from "@black-ink-technologies/white-labeled-auth";

// Option A — static method (requires configure() first)
const userInfo = await HostedAuth.fetchUserInfo();

// Option B — standalone function (same constraint)
const userInfo = await fetchUserInfo();

| Field | Type | Description | | :----------------------- | :-------- | :----------------------------- | | name | string | Full display name | | email | string | Email address | | picture | string | Profile picture URL | | preferred_username | string | Preferred username | | user_id / id / sub | string | Unique user identifier | | phone_number | string | Phone number | | email_verified | boolean | Whether the email is confirmed | | phone_number_verified | boolean | Whether the phone is confirmed | | updated_at | string | ISO timestamp of last update |


Token storage helpers

After onSuccess fires, the SDK persists tokens in browser storage. Apps that need to react to token changes can use the exported helpers:

import {
  tokenStorage,
  getStoredAccessTokenSnapshot,
  subscribeStoredAccessToken,
  STORED_TOKENS_CHANGED_EVENT,
  type StoredTokens,
} from "@black-ink-technologies/white-labeled-auth";

const current = getStoredAccessTokenSnapshot();
const unsubscribe = subscribeStoredAccessToken(
  (tokens: StoredTokens | null) => {
    // tokens === null when the user signs out / tokens are cleared.
  },
);

STORED_TOKENS_CHANGED_EVENT is the underlying DOM event name if you need to subscribe outside the helper (e.g. from a non-React layer).


Logout

logout() revokes the active refresh-token chain on POST /oauth/logout, blacklists the stored access token via Authorization: Bearer, and clears local token storage. Local storage is always cleared — even if the server call fails — so the user is signed out locally regardless of network outcome.

type LogoutOptions = {
  refreshToken?: string; // defaults to tokenStorage.getRefreshToken()
  idTokenHint?: string; // defaults to tokenStorage.getIdToken()
  postLogoutRedirectUri?: string;
};

type LogoutResult = { success: boolean; message: string };

React

import { useLogout } from "@black-ink-technologies/white-labeled-auth";

function SignOutButton() {
  const logout = useLogout();

  const handleClick = async () => {
    const result = await logout();
    if (!result.success) {
      console.warn(
        "Server logout failed; tokens cleared locally:",
        result.message,
      );
    }
  };

  return <button onClick={handleClick}>Sign out</button>;
}

Imperative JS / UMD

import { HostedAuth } from "@black-ink-technologies/white-labeled-auth";

HostedAuth.configure({
  clientId: "<<your_client_id>>",
  session: { getSession },
});

await HostedAuth.logout();

performHostedAuthLogout is also exported for non-React callers that prefer a plain function.


Cross-framework navigation

Imperative helpers safe to call from React, Vue, Angular, vanilla JS, or axios interceptors:

import {
  navigate,
  HOSTED_AUTH_NAVIGATE_EVENT,
  OnboardingScreen,
} from "@black-ink-technologies/white-labeled-auth";

navigate(OnboardingScreen.VerifyOtp);

See useOnboardingNavigation JSDoc in the source for the full contract.


Backend endpoints used by the SDK

| Endpoint | Method | Purpose | | ------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------- | | /users/v1/hosted-auth/client-session | POST | Server-to-server session bootstrap (your backend calls this; the browser does not). | | /users/v1/hosted-auth/app-config | POST | Load app configuration after session bootstrap. | | /users/v1/hosted-auth/branding | GET | Load tenant branding (logo, colors, screen copy). | | /users/v1/hosted-auth/initiate | POST | Start the OTP challenge. | | /users/v1/hosted-auth/resend-otp | POST | Resend the OTP for the active session. | | /users/v1/hosted-auth/verify | POST | Verify the OTP. | | /users/v1/hosted-auth/consents | POST | Capture terms / privacy acceptance. | | /users/v1/hosted-auth/face/session | GET | Start face liveness session. | | /users/v1/hosted-auth/face/verify | POST | Verify face liveness. | | /users/v1/hosted-auth/face/reverification | GET / POST | Standalone face re-verification (used by ReverifyFace). | | /users/v1/hosted-auth/liveness/qr-session | POST | Mint a QR handoff when the desktop cannot use a camera. | | /users/v1/hosted-auth/liveness/qr-session/resolve | GET | Resolve a scanned one-time handoff code on the phone; validates X-Origin. | | /users/v1/hosted-auth/liveness/qr-session/status | GET | Poll QR handoff status while the desktop waits. | | /users/v1/hosted-auth/idscan/verify | POST | Verify ID document scan. | | /users/v1/hosted-auth/init-user-vdt-and-device-vdt-v2 | POST | Issue final OIDC tokens. | | /oauth/logout | POST | Revoke the active session (used by useLogout / HostedAuth.logout). | | /oauth/userInfo | GET | Fetch the authenticated user's profile (used by fetchUserInfo / useUserInfo). |

All requests carry the rotating Authorization: Bearer <requestToken> issued by client-session; the browser never sees clientSecret.


Handling the response

The onSuccess callback receives standard OIDC tokens:

  • accessToken — pass to the SDK's built-in fetchUserInfo() to fetch the user's profile, or call the ChainIT UserInfo API directly.
  • idToken — identity claims. Validate via JWKS.
  • refreshToken — opaque ({jti}.{secret}); send to your backend's refresh proxy to mint a new accessToken / idToken.

Error handling

The SDK ships a built-in error surface for every step:

  • Session-bootstrap and identity-verification failures render an inline error UI with a Retry button so the user can re-attempt the failed step without restarting the whole journey.
  • After the user-visible retry path is exhausted, errors bubble to callbacks.onError.
  • ReverifyFace mirrors the same behaviour — built-in retry first, then onError.

Branding

Branding (logo, primary / secondary colors, brand name, font, per-screen copy) is loaded automatically from the hosted-auth branding endpoint based on the app behind your client-session. Manage it via the Developer Portal Branding tab or the /dev-portal/v1/branding API — see Branding customization.

The SDK applies the colors as Tailwind / CSS theme tokens at runtime, so no frontend changes are required when the brand updates.


Public API (named exports)

| Export | Type | | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | HostedAuthProvider, InitiateAuthFlow, ReverifyFace, FaceHandoff | React components | | HostedAuthProviderProps, InitiateAuthFlowProps, ReverifyFaceProps, FaceHandoffProps | React component prop types | | HostedAuth, ChainItAuth | Imperative JS / UMD API (and its instance type HostedAuthInstance) | | HostedAuthConfiguration, HostedAuthSessionConfig, HostedAuthCallbacksConfig, HostedAuthFeatureConfig, HostedAuthOptions | Config types | | tokenStorage, getStoredAccessTokenSnapshot, subscribeStoredAccessToken, STORED_TOKENS_CHANGED_EVENT, StoredTokens | Token-storage helpers | | useLogout, performHostedAuthLogout, LogoutOptions, LogoutResult | Server-backed logout (revokes refresh token, blacklists access token, clears local storage) | | navigate, HOSTED_AUTH_NAVIGATE_EVENT, HostedAuthNavigateOptions, OnboardingScreen | Cross-framework navigation helpers | | fetchUserInfo, useUserInfo, UserInfo | User profile API — fetch user data from /oauth/userInfo |

Internal modules are intentionally not exported to keep the SDK surface small.


Legacy compatibility

The previous OnboardingProvider entry point is preserved for in-flight integrations and now lives in OnboardingProvider.tsx (renamed from App.tsx). New integrations should use HostedAuthProvider + InitiateAuthFlow to get the latest features (branding context, error retry UI, opaque refresh tokens, resend-otp endpoint, etc.).


Development

This package is built as a library (no local Vite app). Use the standard scripts:

npm run lint
npm run typecheck
npm run build      # builds lib + sdk + types
npm run preview