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

@dloizides/auth-web

v1.5.0

Published

Themeable, branded auth UI for the dloizides.com portfolio. Native LoginForm / ForgotPasswordForm / ResetPasswordForm components, headless hooks, the same-origin BffAuthClient, and a role-based post-login router. Built on @dloizides/auth-client; talks onl

Readme

@dloizides/auth-web

Themeable, branded auth UI for the dloizides.com portfolio — the frontend half of the unified-auth plan.

Every app gets a native, branded login experience: the login / forgot / reset forms live inside the app's own frontend; the user is never redirected to Keycloak's hosted login UI. All credential exchange happens server-side in a per-app BFF (bff-katalogos, bff-erevna, ...). This package talks only to a same-origin /bff/*no secrets, no token handling, no Keycloak calls in the browser.

Built on @dloizides/auth-client, which provides the lower-level BffAuthClient.

Install

npm install @dloizides/auth-web

Peer dependencies: react, react-native (optional), @tanstack/react-query, @dloizides/auth-client (>=3.0.0).

Two ways to consume it

1. Ready-made themeable components

import {
  AuthThemeProvider,
  LoginForm,
  createBffAuthClient,
  resolvePostLoginRoute,
} from '@dloizides/auth-web';

import { katalogosAuthTheme } from './theme';      // your AuthTheme token bag
import { roleRoutes } from './roleRoutes';          // your RoleRouteTable
import { authLabels } from './authLabels';          // your localised labels

const client = createBffAuthClient();               // same-origin /bff/*

function LoginScreen() {
  const router = useRouter();
  return (
    <AuthThemeProvider theme={katalogosAuthTheme}>
      <LoginForm
        client={client}
        labels={authLabels.login}
        onForgotPassword={() => router.push('/forgot-password')}
        onSuccess={(user) => {
          const route = resolvePostLoginRoute(user, roleRoutes);
          router.replace(route ?? '/no-access');
        }}
      />
    </AuthThemeProvider>
  );
}

<ForgotPasswordForm> and <ResetPasswordForm> follow the same shape.

Email-OTP login — <OtpForm>

A native, branded "sign in with a code" surface — the user is never bounced to Keycloak's hosted UI. It is a two-step form: step 1 collects the email and asks the BFF to email a one-time code; step 2 collects the code, verifies it, and offers "resend code" / "use a different email".

import { OtpForm, createBffAuthClient, resolvePostLoginRoute } from '@dloizides/auth-web';

const client = createBffAuthClient();

function OtpLoginScreen() {
  const router = useRouter();
  return (
    <OtpForm
      client={client}
      labels={authLabels.otp}
      onSuccess={(user) => {
        const route = resolvePostLoginRoute(user, roleRoutes);
        router.replace(route ?? '/no-access');
      }}
    />
  );
}

<OtpForm> POSTs to the same-origin /bff/otp/request and /bff/otp/verify endpoints (added in Bff.AspNetCore). The BFF runs the OTP direct-grant against Keycloak server-side; the browser receives only the httpOnly session cookie.

Event-PIN login — <PinForm>

A native, branded "sign in with your event PIN" surface for operational staff (door / DJ / media on Kefi). It is a single-step form: a PIN field + a "sign in" button. The eventExternalId is a prop — the event context comes from the route/page, never typed by the user. The (event, pin) pair alone identifies the staff member; no username/password ever leaves the browser.

import { PinForm, createBffAuthClient, resolvePostLoginRoute } from '@dloizides/auth-web';

const client = createBffAuthClient();

function PinLoginScreen({ eventExternalId }: { eventExternalId: string }) {
  const router = useRouter();
  return (
    <PinForm
      client={client}
      eventExternalId={eventExternalId}
      labels={authLabels.pin}
      onSuccess={(user) => {
        const route = resolvePostLoginRoute(user, roleRoutes);
        router.replace(route ?? '/no-access');
      }}
    />
  );
}

<PinForm> POSTs to the same-origin /bff/pin/login endpoint (added in Bff.AspNetCore). The BFF runs the event-scoped PIN direct-grant against Keycloak server-side; the browser receives only the httpOnly session cookie.

2. Headless hooks (custom layout)

import { useBffAuth, createBffAuthClient } from '@dloizides/auth-web';

const client = createBffAuthClient();

function CustomLogin() {
  const { login, isSubmitting, error } = useBffAuth({ client, probeOnMount: false });
  // ...render your own form, call login({ username, password })
}

For a custom OTP layout, useOtpLogin exposes the two-step machine:

import { useOtpLogin, OtpLoginStep, createBffAuthClient } from '@dloizides/auth-web';

const client = createBffAuthClient();

function CustomOtpLogin() {
  const otp = useOtpLogin({ client });
  // step 1: otp.requestCode(email)  → otp.step becomes OtpLoginStep.EnterCode
  // step 2: otp.verifyCode(code)    → resolves to the signed-in BffUser
  //         otp.resend() / otp.reset() for the step-2 affordances
}

For a custom PIN layout, usePinLogin exposes the single-step flow:

import { usePinLogin, createBffAuthClient } from '@dloizides/auth-web';

const client = createBffAuthClient();

function CustomPinLogin({ eventExternalId }: { eventExternalId: string }) {
  const pin = usePinLogin({ client, eventExternalId });
  // pin.submit(pinValue) → resolves to the signed-in BffUser
  // pin.reset()          → clears the error
}

Theming

The package owns no brand. Each app maps its own theme system onto the flat AuthTheme token bag (colors, radii, spacing, typography) and supplies it via <AuthThemeProvider> or a theme prop on an individual component. Precedence: prop → context → defaultAuthTheme.

import { defaultAuthTheme, type AuthTheme } from '@dloizides/auth-web';

export const katalogosAuthTheme: AuthTheme = {
  ...defaultAuthTheme,
  colors: { ...defaultAuthTheme.colors, primary: '#c2410c' },
};

Because all three forms share one useAuthStyles token-to-style mapping, re-theming <LoginForm> automatically re-themes the others.

Internationalisation

@dloizides/auth-web ships no i18n framework. Every user-facing string is supplied through a typed labels prop. Apps pass strings already localised with their own FM() / t(). Each label bag is partial — unspecified keys fall back to the English DEFAULT_* constants.

Role-based post-login routing

import { resolvePostLoginRoute, type RoleRouteTable } from '@dloizides/auth-web';

const roleRoutes: RoleRouteTable = {
  routes: [
    { role: 'superUser', route: '/admin/super' },
    { role: 'admin',     route: '/admin' },
    { role: 'user',      route: '/dashboard' },
  ],
  fallback: '/no-access',
};

// The first table entry whose role the user holds wins — list most privileged first.
const route = resolvePostLoginRoute(user, roleRoutes);

API surface

| Export | Kind | |--------|------| | LoginForm, ForgotPasswordForm, ResetPasswordForm, OtpForm, PinForm | Components | | AuthThemeProvider, useAuthTheme, defaultAuthTheme, AuthTheme | Theming | | DEFAULT_LOGIN_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS | Label bags | | useBffAuth, useBffForgotPassword, useBffResetPassword, useResetPasswordForm, useOtpLogin, usePinLogin | Headless hooks | | OtpLoginStep | OTP step enum | | createBffAuthClient, BffAuthClient (re-export) | Client | | resolvePostLoginRoute, collectUserRoles, RoleRouteTable | Router | | validatePasswordPolicy, isPasswordValid, PasswordPolicyError | Password policy | | AuthTestIds, withTestIdPrefix | Test IDs |

License

MIT