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

@idmission/auth-modal

v3.3.0

Published

Shared login modal for IDMission Web SDK products.

Readme

@idmission/auth-modal

Shared login modal for IDMission Web SDK products. Drop-in <AuthDialog /> and <AuthButton /> with full session lifecycle management, environment/region selection, and pluggable persistence.

Install

npm install @idmission/auth-modal
# or pnpm add / yarn add

Quick start (Next.js)

1. Mount the catch-all auth route:

// app/api/idmission-auth/[...slug]/route.ts
import { createAuthRouteHandler } from "@idmission/auth-modal/next"
export const { GET, POST } = createAuthRouteHandler()

2. Wrap your app with the provider and import the styles:

// app/layout.tsx
import { AuthProvider } from "@idmission/auth-modal"
import "@idmission/auth-modal/styles.css"

export default function RootLayout({ children }) {
  return (
    <html><body>
      <AuthProvider>{children}</AuthProvider>
    </body></html>
  )
}

3. Drop the dialog, env switcher, and button anywhere:

import { AuthDialog, AuthButton, AuthEnvSwitcher, useAuth } from "@idmission/auth-modal"

function App() {
  return (
    <>
      <header>
        <AuthEnvSwitcher />
      </header>
      <AuthDialog />
      <AuthButton />
    </>
  )
}

function YourComponent() {
  const auth = useAuth()
  if (!auth.isAuthenticated) return null
  return (
    <div>
      Active env: {auth.activeEnvUrl}
      <br />
      Cached envs: {auth.envs.length}
      <br />
      Last verified: {new Date(auth.lastValidatedAt!).toLocaleString()}
    </div>
  )
}

4. Set the encryption key for credential persistence:

Generate a 32-byte key (64 hex chars) and add it to your environment as AUTH_ENCRYPTION_KEY:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

If AUTH_ENCRYPTION_KEY is unset, the package gracefully degrades to "no persistence" — sign-in still works, but credentials don't survive page reloads.

Storybook / Vite consumer

Use the localStorageAuthAdapter and configure your Vite proxy to forward to the IDMission API:

import { AuthProvider, AuthDialog, AuthEnvSwitcher, useAuth, localStorageAuthAdapter } from "@idmission/auth-modal"
import "@idmission/auth-modal/styles.css"

const adapter = localStorageAuthAdapter({
  sessionsApiProxy: "/api/idmission-portal",
})

function App() {
  return (
    <AuthProvider adapter={adapter}>
      <header>
        <AuthEnvSwitcher />
      </header>
      <main>
        <YourApp />
        <AuthDialog />
      </main>
    </AuthProvider>
  )
}

function YourApp() {
  const auth = useAuth()
  if (!auth.isAuthenticated) return null
  return (
    <div>
      Active env: {auth.activeEnvUrl}
      <br />
      Last verified: {new Date(auth.lastValidatedAt!).toLocaleString()}
    </div>
  )
}

Configure your Vite proxy to forward to the IDMission API:

// vite.config.ts
export default {
  server: {
    proxy: {
      "/api/idmission-portal": {
        target: "https://portal-api-dev.idmission.com",
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/api\/idmission-portal/, ""),
      },
    },
  },
}

Security note: the localStorage adapter stores the API key secret in cleartext in the browser. Use only in dev/demo contexts.

Migration to 3.0

Automatic storage migration

v2 stored data is automatically migrated to v3 on first load. No consumer action required.

Breaking changes

  • useAuth().session removed. Credentials are no longer held as an in-memory session. Use useAuth().activeEnvUrl to check the active environment and useAuth().lastValidatedAt for the freshness of the last validation. To validate credentials, call useAuth().revalidateActive().

  • useSessionTimeRemaining hook removed. Session lifecycle is no longer tracked. If you need to detect when credentials expire, poll useAuth().lastValidatedAt or listen to the AuthProvider's state via callback.

  • AuthAdapter interface redesigned. Custom adapters must update:

    • Old: remember(creds), getRemembered({ checkOnly }), forget(), createSession(input) → Session
    • New: loadStore(): Promise<StoredAuthV3 | null> → returns the persisted v3 blob (or null); saveStore(store): Promise<void> → persists the blob; clearStore(): Promise<void> → wipes storage. See src/client/adapters for built-in implementations.
    • createSession({ sessionsServiceUrl, apiKeyId, apiKeySecret }): Promise<void> — validates credentials against the sessions service. Throws on non-2xx with a human-readable message; the returned token is intentionally discarded (sessions are decorative in v3).
  • Next.js route handler endpoints renamed (if using custom fetch calls to the auth API):

    • /api/idmission-auth/remember/api/idmission-auth/save
    • /api/idmission-auth/remembered/api/idmission-auth/load
    • /api/idmission-auth/forget/api/idmission-auth/clear
    • Consumers using createAuthRouteHandler() are unaffected — the factory handles these paths transparently.

New API

  • <AuthEnvSwitcher /> — Standalone component for switching between cached environments. Place it in a toolbar, header, or menu outside the dialog.
  • useAuth().envs — Array of cached environment objects.
  • useAuth().activeEnvUrl — Currently active environment URL.
  • useAuth().lastValidatedAt — Timestamp of the last successful credential validation. Use this to decide when to revalidate.
  • useAuth().switchEnv(url) — Switch to an existing cached environment.
  • useAuth().removeEnv(url) — Remove an environment from the cache.
  • useAuth().forgetAllEnvs() — Clear all cached environments and sign out.
  • useAuth().revalidateActive() — Validate the active environment's credentials against the API.
  • useAuth().signOutActive() — Sign out of the active environment without clearing the cache.

Migration to 3.1

Security fix: apiKeySecret no longer in client state for Next.js consumers

In 3.0, useAuth().apiKeySecret was non-null in the browser for httpAuthAdapter consumers — /api/idmission-auth/load returned the entire stored blob, including the secret. This was a regression vs. v2's behavior, where the secret was kept server-only in the encrypted cookie.

3.1 restores the server-only privilege boundary:

  • /api/idmission-auth/load returns the v3 blob with apiKeySecret: "" for every entry. The cookie itself still holds the secret encrypted server-side.
  • After a successful sign-in, the provider scrubs apiKeySecret from in-memory state once saveStore resolves.
  • useAuth().apiKeySecret is the empty string for httpAuthAdapter consumers after the same-session sign-in form submit.

localStorageAuthAdapter (Storybook / Vite consumers) is unchanged — there's no server, so the secret necessarily stays in client storage.

New: POST /api/idmission-auth/mint-session

For consumers that need a real session id (e.g. embedding it in iframes, hand-off to other services), the Next.js route handler now exposes a server-side mint endpoint:

const res = await fetch("/api/idmission-auth/mint-session", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ sessionsServiceUrl: auth.sessionsServiceUrl }),
})
const { session } = await res.json()
// session.id is the upstream session token

The route decrypts the encrypted cookie, looks up the entry by sessionsServiceUrl, calls upstream, and returns the session payload. Avoid the temptation to mint sessions client-side using useAuth().apiKeySecret — that secret is empty after page reload in 3.1.

Custom AuthAdapter implementations

If you have a hand-rolled adapter, add the keepsSecretsClientSide capability flag:

const myAdapter: AuthAdapter = {
  keepsSecretsClientSide: true, // or false if you have a server-side store
  // ... existing methods
}

The provider uses this to decide whether to scrub state.envs[*].apiKeySecret after a successful saveStore. TypeScript will surface the omission at compile time.

API reference

See src/client/types.ts for the AuthAdapter interface and src/client/provider/AuthProvider.tsx for AuthProviderProps.

License

UNLICENSED — internal use only.