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

@danainnovations/sonance-gate

v0.4.0

Published

Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)

Downloads

563

Readme

@danainnovations/sonance-gate

Sonance Okta SSO for any web app on Vercel — one file, any framework (Next.js, Vite, Astro, SvelteKit, static HTML).

How it behaves

| Environment | Behavior | |---|---| | Vercel production | Real Okta sign-in required for every route | | Vercel preview | Legacy-open by default; internal/data-bearing apps opt into real Okta | | Local dev | Local-only Dev User adapter; no Okta credentials |

Set enforceInPreview: true for every new internal or company-data-bearing app. Existing apps retain their current preview posture until they are audited and migrated. Sessions last 12 hours; re-auth is silent while your Okta session is alive.

Install

npm install @danainnovations/sonance-gate

Create middleware.ts at your project root (NOT inside src/ or app/):

import { createGate } from '@danainnovations/sonance-gate'

export default createGate({
  // Required for internal/company-data-bearing applications.
  enforceInPreview: true,
  // Available only while running locally; selection still enters the standard
  // server-side identity path used by the app's authorization checks.
  devUsers: [
    { sub: 'admin', name: 'Admin', email: '[email protected]' },
    { sub: 'manager', name: 'Manager', email: '[email protected]' },
  ],
  // Paths reachable without sign-in. '/prefix/*' matches the whole subtree;
  // a bare '/prefix' must be listed exactly (on its own) to be public.
  publicPaths: ['/api/webhook/*'],
  // End the Okta session at sign-out too. Needs a registered sign-out
  // redirect URI first — see "Signing out" below.
  oktaLogout: true,
})

export const config = {
  runtime: 'nodejs',
  matcher: ['/((?!_next/static|_next/image|favicon.ico|assets/).*)'],
}

Next.js note: on Next.js ≤15 the framework owns root middleware.ts — compose by calling the gate first inside your existing middleware and returning its Response when defined. On Next.js 16+ use proxy.ts for framework logic; middleware.ts belongs to the gate.

Environment variables (Vercel production only)

OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_ISSUER — pushed by the Technology team from your app's Okta registration. SESSION_SECRET — any random string ≥32 chars. Nothing is needed locally.

If any are missing in an enforcing deployed environment the gate fails closed with a 500 naming the missing key names. Deployed AUTH_BYPASS, DISABLE_AUTH, and NEXT_PUBLIC_DISABLE_AUTH variables are rejected whenever present, regardless of value; they are not escape hatches.

Local Dev User Switcher

Local development needs no IdP credentials. GET /auth/dev/users returns the allowlisted devUsers; POST /auth/dev/switch with { "sub": "..." } selects one through an httpOnly local session cookie. The gate then injects that selected principal through the same server-side getUser() path used in deployment.

The adapter runs only for loopback hosts (localhost, 127.0.0.1, or ::1); a local-mode request addressed to any other host fails closed. These are test principals backed by local/isolated development data. Do not use this adapter to reach production data or treat it as a security control: local source and processes are developer-controlled. The endpoints return 404 in every deployed Vercel environment, including legacy-open previews.

Showing who's signed in

The gate serves GET /auth/me → { sub, name, email } (a fake Dev User outside production). Framework-free widget:

<div id="whoami"></div>
<script>
  fetch('/auth/me').then(r => r.ok ? r.json() : null).then(user => {
    if (!user) return
    document.getElementById('whoami').innerHTML =
      `${user.name} · <a href="/auth/signout">Sign out</a>`
  })
</script>

Endpoints: /auth/signin (optionally ?returnTo=/path), /auth/signout, /auth/me, /auth/callback (Okta's redirect URI — register https://<your-domain>/auth/callback).

Identity in backend code (v0.2+)

On every request the gate lets through, it injects an x-sonance-user request header (spoof-proof: any client-supplied value is stripped and replaced). Read it with the bundled helper — works in any route handler or server function:

import { getUser } from '@danainnovations/sonance-gate'

export async function POST(req: Request) {
  const user = getUser(req)          // { sub, name, email } | null
  if (!user) return new Response('unauthenticated', { status: 401 })
  // ... write rows keyed by user.email, etc.
}

In local development it returns the selected dev principal, so backend code exercises the same authorization path. Legacy-open previews return the default Dev User until they are migrated; enforceInPreview: true uses the real Okta session. On publicPaths routes it returns null unless the visitor happens to have a session.

Signing out

By default /auth/signout clears the app session and forces a fresh Okta credential prompt on the next visit, while your Okta org session for other apps stays alive. Because that Okta session survives, some orgs re-authenticate the browser without a visible prompt, which reads to users as "sign-out did nothing".

oktaLogout: true ends the Okta session as well (OIDC RP-initiated logout): /auth/signout clears the app cookies and hands the browser to Okta's end-session endpoint, which signs the user out and returns them to your app's root.

It is opt-in per app because it needs one thing from the Technology team first:

Add https://<your-domain>/ to Sign-out redirect URIs on this app's Okta registration.

Turn the option on only once that is registered — Okta rejects the logout otherwise. Sign-out degrades to the local-only behaviour, never an error page, whenever the Okta session cannot be ended (no session to prove, a session issued before this upgrade, a Teams-issued session, or Okta unreachable).

Microsoft Teams mode

Apps packaged as Teams tabs authenticate silently — no Okta prompt inside Teams. Enable per app:

export default createGate({
  publicPaths: [],
  teams: true,
})

Production env additionally uses ENTRA_CLIENT_ID and ENTRA_TENANT_ID (non-secret, pushed by the Technology team alongside the Okta keys).

teams: true is safe to deploy before the Entra registration exists. If the Entra keys are absent, only the Teams lane degrades — the browser/Okta lane enforces exactly as normal (no 500). An ?inTeams=true visit falls back to the Okta redirect instead of the bootstrap page, and POST /auth/teams returns a 503 naming the missing key names (names only, no secrets). The Teams lane activates automatically once both keys are present on the next deploy — no code change and no strict "Entra keys before teams: true" ordering. (Missing OKTA_*/SESSION_SECRET keys still fail the whole app closed, as before.)

How it works: the Teams manifest's contentUrl carries ?inTeams=true. For an unauthenticated HTML request with that hint (or the __Host-sg_teams marker cookie), the gate serves a static bootstrap page that calls authentication.getAuthToken() (Teams JS SDK) and POSTs the Entra ID JWT to /auth/teams. The gate validates it against Microsoft's JWKS (issuer https://login.microsoftonline.com/{tenant}/v2.0, audience = client ID or api://{host}/{clientId}, tenant pinned), then mints the ordinary session. Server code stays lane-blind: getUser(request) works identically.

  • ?inTeams=true is a routing hint, never a bypass: outside Teams the bootstrap falls back to the normal Okta redirect. Fail closed always.
  • Teams-lane cookies use SameSite=None; Secure; Partitioned (CHIPS) so they survive the cross-site iframe. Browser-lane cookies stay SameSite=Lax.
  • Foreign-tenant users get a friendly "Sonance employees only" page (403).
  • Identity: key on email. sub is lane-specific (Okta subject vs Entra object ID) — do not use it as a cross-lane key.