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

@incorta/auth

v1.10.0

Published

Authenticate Incorta users — OAuth 2.0/OIDC against Incorta's built-in authorization server: a server-side handler for Node.js apps with React, Express and TanStack Start integrations, and a headless client for native, desktop and extension apps.

Readme

@incorta/auth

Authenticate Incorta users in any Node.js app via Incorta's built-in OAuth 2.0/OIDC authorization server. See the repository root README for the full guide; this is the API surface.

To read Incorta with the session this package holds — schemas, tables, columns, rows, all filtered to the signed-in user — add @incorta/sdk, released from this repository at the same version.

Entry points

| Import | What's in it | | ----------------------- | ------------------------------------------------------------------- | | @incorta/auth | createIncortaAuth, registerClient, getClient, updateClientRedirectUris, deleteClient, types (IncortaUser, AuthSession, …) | | @incorta/auth/react | IncortaAuthProvider, useAuth, useUser, SignedIn, SignedOut, Protect, RedirectToLogin | | @incorta/auth/express | incortaAuth(auth) middleware, requireAuth(auth, options?) | | @incorta/auth/node | toNodeHandler(auth), toWebRequest, sendWebResponse, getNodeSession | | @incorta/auth/client | createIncortaClient, memoryStore, IncortaAuthError, types (TokenStore, StoredTokens, …) — no server, for native and extension apps |

createIncortaAuth(config?)

Returns { handler, getSession, config }:

  • handler(request: Request): Promise<Response> — serves, under config.basePath (default /auth):
    • GET /login?redirect_to= — 302 into the Incorta authorize flow (or straight back when already signed in), with a PKCE S256 challenge. Sets a sealed state+nonce+verifier cookie (10 min).
    • GET /callback — validates state, exchanges the code with the PKCE verifier (as a public client — client_id in the body — or with client_secret_basic when a secret is configured), verifies the ID token against the tenant JWKS (issuer, audience, nonce), seals the session cookie, 302 to the original page. Failures 302 to {redirect_to}?incorta_auth_error={code}.
    • GET /session — { user, session } or { user: null }. Refreshes the access token (rotating refresh token) when <60s from expiry; slides the session cookie past its half-life.
    • GET|POST /logout — clears the session cookie (GET redirects: to redirect_to when given, else to /signed-out).
    • GET /signed-out — ungated "you're signed out" page with a sign-in link; the react signOut() default destination. Landing anywhere gated would silently re-login via SSO while the Incorta session lives.
    • GET /token — the raw Incorta access token; only with exposeAccessToken: true, otherwise 404.
  • getSession(request): Promise<AuthSession | null> — read-only session lookup for server code (user, accessToken, accessTokenExpiresAt, expiresAt).

Config falls back to INCORTA_URL, INCORTA_TENANT, INCORTA_CLIENT_ID, INCORTA_AUTH_SECRET, INCORTA_APP_URL, INCORTA_CLIENT_SECRET (optional — only a confidential client has one; Incorta registers apps as public clients), and INCORTA_INTERNAL_URL (split horizon: server-side discovery/token/JWKS calls use it when the public incortaUrl is not routable from the app — e.g. http://host.docker.internal:8080/incorta from a local container; the authorize redirect and issuer validation stay on the public URL).

App access policy (appAccess / INCORTA_APP_ACCESS)

Who may use the app, enforced by the whole-app gate:

  • public — anyone with the URL, signed in or not. The gate is inert for app routes; the auth routes stay mounted, so signing in remains possible and sessions become optional (handle session == null).
  • authenticated (default) — any signed-in Incorta user. Exactly the pre-policy behavior, and what an absent/unknown INCORTA_APP_ACCESS means (unknown values warn and fall back here — never to public).
  • catalog — signed in AND at least VIEW access to this app in the Incorta catalog. The SDK asks GET {incortaUrl}/apps-catalog/check with the session's own access token; Incorta resolves which app the token belongs to from the OAuth client registration's (incorta-app:<id>) name suffix (written once, server-side, at registration), so the endpoint can only ever answer about this app. Verdicts (allow and deny) are cached 30s per user; apps not registered in the catalog stay open to authenticated users (legacy-open). When Incorta cannot answer, the gate fails CLOSED with a distinct 503 "temporarily unavailable" page (failures are not cached).

Enforcement surfaces:

  • auth.gate(request): Promise<Response | null> — the whole-app gate: serve the Response when non-null (auth routes, login redirects, the 403 access-denied page — which names the signed-in user and offers a switch-account logout — or the 503 page), continue to your app on null.
  • @incorta/auth/express: appGate(auth) middleware is auth.gate for Express/Connect. requireAuth(auth) always demands a session regardless of policy (an explicit route guard is the author's promise), and under catalog additionally requires VIEW, attaching the verdict as req.incortaAuth.appAccess.
  • auth.getAppAccess(session): Promise<AppAccess> — the cached verdict (registered, access bitmask, canView, canEdit, allowed) for permission-aware UX in any mode.

GET {basePath}/refresh?redirect_to= supports the catalog gate: it refreshes an expired access token through a browser round trip (persisting the rotated refresh-token cookie) and bounces back — the gate sends navigations there instead of calling Incorta with a token it knows is expired.

Session model

The cookie is an encrypted JWT (JWE dir/A256GCM, key derived from secret), HttpOnly, SameSite=Lax, Secure on HTTPS. Payload: verified user claims (sub, name, email, roles, tenant) + the Incorta access/refresh tokens. Nothing is stored server-side.

Embedded (cross-site iframe) mode: when the app runs inside an iframe on a DIFFERENT site, Lax cookies are never sent — the login flow loops forever. Set cookies: { sameSite: "none", partitioned: true } (or env INCORTA_COOKIE_SAMESITE=none + INCORTA_COOKIE_PARTITIONED=1): the auth cookies become SameSite=None; Secure; Partitioned (CHIPS), which modern Chrome allows in third-party iframes with a per-embedder cookie jar. none forces Secure (accepted on trustworthy plain-http origins like *.localhost); top-level usage keeps working with the same setting.

@incorta/auth/client (native, desktop and extension apps)

A headless sign-in client for apps with no server of their own: mobile (React Native / Expo), desktop (Electron) and browser extensions. It runs the authorization-code flow with PKCE (S256 only) in the system browser (RFC 8252), so Incorta's own login page decides password, LDAP or SSO. Public clients only: there is no client secret option.

import { createIncortaClient } from "@incorta/auth/client";

const incorta = createIncortaClient({
  incortaUrl: "https://acme.cloud.incorta.com/incorta",
  tenant: "demo",
  clientId,                       // a PUBLIC client registered with Incorta
  redirectUri: "incorta.chat://oauth/callback",
  // Open the URL in the system browser; resolve with the URL Incorta
  // redirected to. Reject when the user cancels.
  openAuthSession: (url, redirectUri) => openInSystemBrowser(url, redirectUri),
  store: secureStore,             // optional; defaults to memoryStore()
});

const user = await incorta.signIn();
const token = await incorta.getAccessToken(); // { accessToken, expiresAt } | null
incorta.onSignedOut(() => showSignIn());
await incorta.signOut();
  • signIn() sends state, nonce and an S256 challenge; accepts only a callback to redirectUri (scheme, host and path) carrying the same state, before it looks at error or code; exchanges the code with the verifier (client_id in the body); verifies the ID token (signature, issuer, audience, nonce, expiry); stores user and tokens.
  • getAccessToken({ forceRefresh? }) returns the stored token, refreshing first when under 60 s remain or when forced (after a 401). Concurrent callers share one refresh. A refused refresh (invalid_refresh_token, invalid_grant, …) clears the store and fires onSignedOut. When Incorta is unreachable the session is kept: an automatic caller gets the current token while it is still valid, a forced caller gets the error.
  • signOut() clears the store and cancels a sign-in or refresh in flight: neither can write tokens back afterwards, and a sign-in whose browser step was still open does not exchange its code. It tells onSignedOut listeners only when someone was signed in, even if the store fails to clear (the same holds for a refused refresh). Incorta has no revocation endpoint; the refresh token expires on its own (24 h).
  • A second signIn() while one is running returns the same promise. When openAuthSession never settles (a desktop loopback the user abandoned), signOut() lets a new signIn() start; the abandoned one rejects with sign_in_cancelled if its openAuthSession ever settles. Errors from openAuthSession (the user cancelled) and from the TokenStore reach the caller as they are. Failures of the sign-in itself are an IncortaAuthError whose code is one of sign_in_cancelled, invalid_callback, redirect_mismatch, state_mismatch, missing_code, an OAuth error Incorta sent back, or a token-endpoint or verification code.
  • TokenStore is { get, set, clear }, sync or async, holding one StoredTokens object (issuer, client id, user, access token, refresh token: a few KB). A record from another issuer or client counts as signed out, so get() must return every field set() was given. Back it with the platform's secure storage (Keychain/Keystore, Electron safeStorage, chrome.storage.session), never localStorage. expo-secure-store warns above 2048 bytes on iOS: keep the refresh token there and the rest in memory or in expo-file-system encrypted by a key held in SecureStore, or split the fields across keys. Give each store one client: refresh tokens rotate, so two clients refreshing from one store sign each other out. In an extension, keep the client in the background worker and have other pages ask it for tokens.
  • Every call (discovery, token, JWKS) goes through fetch when given, so an Electron renderer can route them through its main process. Endpoints and keys are cached per Incorta issuer for the process, so a second client for the same Incorta in one process uses the first one's fetch for them: give every client of one Incorta the same fetch.

Runtime needs: fetch, URL with searchParams, TextEncoder, atob/btoa, structuredClone, AbortSignal.timeout, crypto.getRandomValues, and crypto.subtle with digest("SHA-256"), importKey("jwk") for RSA keys and verify("RSASSA-PKCS1-v1_5") (RS256). Browsers, extension pages and Electron have them all. React Native needs polyfills for crypto (e.g. react-native-quick-crypto, which needs a dev build rather than Expo Go), URL (react-native-url-polyfill), structuredClone and AbortSignal.timeout where Hermes lacks them (checked on Expo SDK 54 / React Native 0.81). The token and discovery calls are cross-origin fetches to Incorta: an extension needs host_permissions for the Incorta URL.

CLI

incorta-auth register --url http://localhost:8080/incorta --tenant demo \
  --name "My App" --redirect https://myapp.example.com/auth/callback [--json]

incorta-auth show --url ... --tenant ... --client <id> --rat <token> [--json]

incorta-auth update-redirects --url ... --tenant ... --client <id> --rat <token> \
  --redirect <url> [--redirect ...] [--replace] [--json]

incorta-auth delete --url ... --tenant ... --client <id> --rat <token>

register creates the OAuth client via RFC 7591 dynamic registration and prints the .env entries (keep the registration access token). show and update-redirects manage the registered client's allowed callback URLs — adding by default, replacing the whole set with --replace. delete removes the registration (new logins stop immediately; issued sessions live until they expire).

Testing

pnpm test runs the suite against an in-process replica of Incorta's OAuth endpoints (test/mock-incorta.ts) — full login/callback/refresh/logout flows over real HTTP sockets, no Incorta needed.