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

@gcm-cz/dmon-switcher

v1.3.0

Published

Headless JavaScript library for integrating the DMon user switcher into a relying party (RP).

Readme

@gcm-cz/dmon-switcher

Headless JavaScript library for integrating the DMon user switcher into a relying party (RP).

Lets your application discover which users the browser has already authenticated with DMon and switch between them — without leaving your page, without re-implementing the picker UI, and without touching DMon's cookies directly.

Internally the library embeds a hidden <iframe> that loads a DMon-hosted bridge page. The bridge runs in DMon's first-party context, reads the dmon_auth session cookie, and exposes a postMessage RPC interface. Your application calls listSessions(), renders its own picker, and calls switchTo(). The library renders no UI of its own.

Zero runtime dependencies. Ships as ESM + CJS with full TypeScript declarations.


Prerequisites

  • A DMon instance with the embed bridge feature enabled.
  • A registered DMon client with embed_origins configured to include your application's origin (e.g. https://app.example.com). Without this, the bridge iframe will be blocked by CSP and the library will not function. See the RP Integration Guide for the admin UI walk-through.

Installation

npm install @gcm-cz/dmon-switcher
# or
pnpm add @gcm-cz/dmon-switcher

Quickstart

import { DmonSwitcher } from "@gcm-cz/dmon-switcher";
import type { Session } from "@gcm-cz/dmon-switcher";

// Instantiate once at component mount. A hidden iframe is created immediately.
const switcher = new DmonSwitcher({
    dmonOrigin: "https://accounts.example.com",
    realmSlug: "default",
    clientId: "my-rp",
    mode: "emit",   // "emit" (default) or "redirect" — controls switchTo() behaviour
});

// Register a switch handler before awaiting ready().
switcher.on("switch", ({ loginHint }) => {
    if (loginHint === "__new__") {
        // Force interactive login to add a new session.
        window.location.href = buildAuthorizeUrl({ prompt: "login" });
        return;
    }
    // Attempt silent re-auth; fall back to interactive on login_required.
    performSilentAuth({ loginHint, prompt: "none" })
        .catch(() => { window.location.href = buildAuthorizeUrl({ loginHint }); });
});

// Wait for the bridge to signal ready, then fetch the session list.
await switcher.ready();
const sessions: Session[] = await switcher.listSessions();
renderMyUserMenu(sessions);

// Switch to a user when selected in your menu. Pass `session.sub` (the user id); the
// switcher resolves it to the login_hint (username) via the cached session list.
await switcher.switchTo(selectedSession.sub);

// Build the account-page URL for an anchor — the user decides how to open it.
const accountHref = switcher.accountPageUrl(currentSub);
// <a href={accountHref}>Account settings</a>

// Clean up when the component unmounts.
switcher.destroy();

React example

import { useEffect, useRef, useState } from "react";
import { DmonSwitcher } from "@gcm-cz/dmon-switcher";
import type { Session } from "@gcm-cz/dmon-switcher";

function UserMenu({ currentSub }: { currentSub: string }) {
    const switcherRef = useRef<DmonSwitcher | null>(null);
    const [sessions, setSessions] = useState<Session[]>([]);

    useEffect(() => {
        const switcher = new DmonSwitcher({
            dmonOrigin: "https://accounts.example.com",
            realmSlug: "default",
            clientId: "my-rp",
        });
        switcherRef.current = switcher;

        switcher.on("switch", ({ loginHint }) => {
            // your re-auth logic — loginHint is the username (or "__new__")
        });

        switcher.ready()
            .then(() => switcher.listSessions())
            .then(setSessions);

        return () => switcher.destroy();
    }, []);

    return (
        <menu>
            {sessions.map(s => (
                <li key={s.sub} onClick={() => switcherRef.current?.switchTo(s.sub)}>
                    {s.name}
                </li>
            ))}
            <li>
                <a href={switcherRef.current?.accountPageUrl(currentSub)}>
                    Account settings
                </a>
            </li>
        </menu>
    );
}

API Reference

Session

interface Session {
    sub: string;          // OIDC subject — the user id; pass to switchTo() / accountPageUrl()
    name: string;         // Username — used by the switcher as the login_hint (resolved from sub)
    given_name?: string;
    family_name?: string;
    picture?: string;     // Profile picture URL
}

listSessions() returns only sessions the realm's can_authorize policy permits. Users authenticated in DMon but blocked by policy are silently omitted.

DmonSwitcherOptions

interface DmonSwitcherOptions {
    dmonOrigin: string;   // Base URL of your DMon instance
    realmSlug: string;    // Realm slug the RP is registered in
    clientId: string;     // OAuth 2.0 client_id of the RP
    mode?: "emit" | "redirect";  // default "emit"
    debug?: boolean;      // Log internal steps via console.debug; default false
}

debug — when true, every internal step is logged via console.debug under the [DmonSwitcher] prefix: construction, ready handshake, RPC send/receive, dropped messages, switch, account-page URL construction, and teardown. Off by default. Useful during integration and troubleshooting; do not enable in production.

DmonSwitcher

constructor(opts: DmonSwitcherOptions)

Creates a hidden <iframe>, appends it to document.body, and starts the bridge handshake. Does not throw — errors are deferred and surfaced via ready() never resolving.

ready(): Promise<void>

Resolves when the bridge iframe sends its ready message. Also resolves immediately if construction failed (e.g. DOM unavailable) or after destroy() is called — in both cases the instance is inoperative and subsequent calls no-op.

listSessions() and switchTo() in "redirect" mode wait for ready() internally before sending an RPC, so you do not have to await ready() before calling them. switchTo() in "emit" mode fires local listeners immediately without waiting for the bridge. Awaiting ready() explicitly is recommended when you want timeout-based error detection:

const TIMEOUT = 5000;
await Promise.race([
    switcher.ready(),
    new Promise((_, reject) => setTimeout(() => reject(new Error("bridge timeout")), TIMEOUT)),
]);

listSessions(): Promise<Session[]>

Sends a list_sessions RPC to the bridge. Returns [] when there are no authenticated sessions, when the cookie is absent, or when all sessions are policy-blocked.

switchTo(sub: string, opts?: { returnTo?: string }): Promise<void>

Initiates a user switch. sub is the target account's Session.sub (the user id), or the "__new__" sentinel. The switcher resolves sub to the OIDC login_hint (the username, Session.name) via the most recent listSessions() result; unresolved values are passed through unchanged.

  • "emit" mode — fires all switch listeners with { loginHint } (the resolved username) and resolves immediately. The RP is responsible for triggering OIDC re-authentication.
  • "redirect" mode — sends redirect_authorize to the bridge, which top-level-navigates to the DMon /authorize endpoint with login_hint=<resolved username>, prompt=none, and redirect_uri=opts.returnTo ?? window.location.href.

__new__ sentinel: switchTo("__new__") uses prompt=login to force fresh credential entry and append a new session to the cookie.

accountPageUrl(sub: string): string

Returns (does not open) the DMon account-page URL for the given account's Session.sub (the user id), resolved to a login_hint (the username) via the cached listSessions() result:

{dmonOrigin}/account?login_hint={encodeURIComponent(resolvedUsername)}

Pure and side-effect free — safe to call during render. Put the result in an anchor href so the user controls how it opens. DMon will silently mount the account page if the cookie already contains a session for that user; otherwise it falls back to the standard login flow.

destroy(): void

Removes the iframe, deregisters window message listeners, rejects all pending RPC promises with AbortError, and resolves any pending ready() waiters so they do not hang. Idempotent — safe to call multiple times, subsequent calls are no-ops. Call at component unmount.

on(event: "switch", handler: (payload: { loginHint: string }) => void): void

Registers a handler fired by switchTo() in "emit" mode.

on(event: "ready", handler: () => void): void

Registers a handler called when the bridge sends its ready message.


switchTo modes

| Mode | What switchTo() does | Who triggers re-auth | |---|---|---| | "emit" (default) | Fires the switch event | Your code | | "redirect" | Bridge navigates the page to DMon /authorize | DMon → OIDC code callback |

"emit" is recommended for SPAs — it keeps your application in control of the authorization flow and lets you discard tokens before starting the new grant.