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

@jwilleke/oidc-auth-server

v0.4.0

Published

OpenID Connect authorization server for Node apps: a thin, hardened wrapper around node-oidc-provider with device authorization (RFC 8628), UserInfo, refresh rotation and PKCE, where the host app keeps sign-in.

Readme

oidc-auth-server

@jwilleke/oidc-auth-server — an OpenID Connect authorization server for Node apps, packaged so any app can embed it.

It is a thin, hardened wrapper around node-oidc-provider (MIT, OpenID Certified). The host app keeps sign-in: it says who the person is, how they signed in (amr, acr), and whether they approved the request. This package issues and checks the tokens.

Status: early, pre-1.0. Published on npm as @jwilleke/oidc-auth-server; release notes under Releases.

npm install @jwilleke/oidc-auth-server

Requires Node.js 20 or later (engines in package.json). Security issues: see SECURITY.md.

What it provides

  • Device authorization grant (RFC 8628) for phones, health apps and bridges that have no browser of their own
  • Authorization code flow with PKCE (S256 only)
  • OIDC UserInfo, discovery and JWKS
  • Refresh-token rotation where the client supports it
  • Dynamic client registration and client ID metadata documents
  • Optional SMART on FHIR v2 scopes
  • An audit hook for every grant, refresh, revocation and refusal
  • A storage adapter interface, with an in-memory adapter for development and the samples

Where the host plugs in

  • Sign-in — an interface the host implements: given a request, return the person, their amr and acr, and whether they approved.
  • Storage — an adapter for grants, sessions and clients.
  • Audit — a callback for each event, so the host writes it to its own audit log.

Usage

What works today: authorization code flow with PKCE, the host sign-in seam, hashed token storage, UserInfo, refresh token rotation with reuse detection, audience-bound tokens for registered APIs, client ID metadata documents (off by default), the device authorization grant (off by default) and the audit hook. The hardening checklist tests are tracked under #13.

import { createServer } from 'node:http';
import { createAuthServer } from '@jwilleke/oidc-auth-server';

const auth = createAuthServer({
  issuer: 'https://auth.example.com',
  jwks: { keys: [privateSigningJwk] },
  cookieKeys: [process.env.OIDC_COOKIE_KEY!],
  adapter: hostAdapterFactory, // node-oidc-provider Adapter; token ids arrive hashed
  clients: [{ client_id: 'app', token_endpoint_auth_method: 'none', redirect_uris: ['…'] }],
  acrValues: ['aal1', 'aal2'],
  interactionUrl: (uid) => `/interaction/${uid}`,
  findAccount: async (accountId, signIn) => users.claimsFor(accountId, signIn), // undefined fails closed
  audit: (event) => auditManager.record(event) // events named in oidc-auth-server.audit.events
});

createServer(async (req, res) => {
  if (!req.url?.startsWith('/interaction/')) return auth.handler(req, res);

  const pending = await auth.interactions.details(req, res);
  if (pending.prompt === 'login') {
    // The host's own sign-in runs here, then reports what it established:
    await auth.interactions.finishLogin(req, res, { accountId: 'alice', amr: ['pwd', 'otp'], acr: 'aal2' });
  } else {
    await auth.interactions.finishConsent(req, res);
  }
}).listen(9000);

createAuthServer throws before anything listens if the options are unsafe: a non-HTTPS issuer, missing private keys, short cookie keys, or no storage adapter outside development: true.

Configuration

Settings follow ngdpbase's convention, under the package's own oidc-auth-server.* keys. config/app-default-config.json ships every default and documents each key. The host overrides them in its app-custom-config.json; maps merge per entry, anything else is replaced. An unknown key refuses the boot rather than being ignored.

Secrets come only from the environment — OIDC_AUTH_SERVER_JWKS and OIDC_AUTH_SERVER_COOKIE_KEYS (see .env.example). Writing either into a config file refuses the boot.

import { createAuthServer, loadConfig, optionsFromConfig } from '@jwilleke/oidc-auth-server';

const config = loadConfig({ customConfigPath: 'data/config/app-custom-config.json' });
const auth = createAuthServer(optionsFromConfig(config, { interactionUrl, findAccount, adapter }));

Options passed directly to createAuthServer take the shipped defaults for anything left unset.

Refusing a stale sign-in

findAccount(accountId, signIn) receives how and when the person signed in: { acr, amr, authTime } (epoch seconds), taken from the code, refresh token or access token the request carries. Return undefined to refuse, and the request fails closed: a refresh answers invalid_grant, UserInfo 401. A host ends app access after a password change this way, by refusing any authTime older than the change. UserInfo still reports only acr and amr.

Mounting under a path

An issuer with a path, such as https://example.com/oidc, serves every endpoint below that path. Mount the handler behind a prefix-stripping router (app.use('/oidc', auth.handler) in Express) or hand it the full path from a plain http server; both work. Cookies are scoped to the issuer's path, so they are never sent with the host's own requests. Point interactionUrl at a host route inside the same path (/oidc/interaction/:uid) and keep that route out of the handler, as the example above does at the root.

PKCE exemption for confidential clients

PKCE (S256) is required from every client. A confidential client — one that authenticates at the token endpoint with a secret or key — may opt out with require_pkce: false in its client metadata, for server-side apps and test tools that do not send a code challenge (the OpenID Connect Playground is one: code flow, client_secret in the token request, no PKCE). A public client (token_endpoint_auth_method: "none") with the exemption refuses the boot. A challenge an exempt client does send is still verified.

{ "client_id": "playground", "client_secret": "…", "token_endpoint_auth_method": "client_secret_post",
  "require_pkce": false, "redirect_uris": ["https://openidconnect.net/callback"] }

Device flow

Set oidc-auth-server.device-flow.enabled to true. The device calls /device/auth; the person opens /device, enters the code, and is sent to the host's interaction route like any sign-in. auth.interactions.details() returns deviceFlow: true there, so the host can require step-up before approving a device. Pass deviceFlow.pages to createAuthServer to render the code pages in the host's own markup.

Samples

Under examples/:

  • node-http — a host on Node's built-in http, with a stand-in sign-in and consent page, two demo accounts and the device flow on. Built: examples/node-http
  • express — an Express host
  • react — a browser client (authorization code + PKCE)
  • angular — a browser client
  • device-cli — a device-flow client

Development

nvm use
npm install
npm run lint
npm run typecheck
npm test
npm run build

Run the example host and drive it end to end over real HTTP:

npm run dev                                   # http://localhost:9000, throwaway development keys
npm run smoke                                 # 10 checks against it: code flow, PKCE, UserInfo,
                                              # refresh rotation and reuse, code reuse, resources,
                                              # denied consent, device flow and slow_down
npm run smoke -- https://oidc.example.com     # the same checks against a deployed example host

The example keeps everything in memory, so it runs only with an http issuer (development mode). Under an https issuer createAuthServer refuses to start until a storage adapter is supplied, as it should; a public deployment needs one, plus OIDC_AUTH_SERVER_JWKS and OIDC_AUTH_SERVER_COOKIE_KEYS.

Deploying a host on bare metal, in Docker or on Kubernetes: docs/deploying.md.

Agent and contributor rules are in AGENTS.md.

Background

The design decisions behind this package were made for ngdpbase and are recorded in its docs/planning/authentication.md.

Acknowledgements

This package is built on, and shaped by, other people's work. No code was copied from any of the projects below; where their work is used, it is used as a dependency or as a published idea, credited here.

  • node-oidc-provider by Filip Skokan (@panva), MIT. The OpenID Certified protocol implementation this package wraps: authorization code flow, PKCE, refresh rotation and reuse detection, RFC 8628, UserInfo, resource indicators, client ID metadata documents, and the special-use-address SSRF guard on outgoing requests. A runtime dependency; every protocol behaviour this package hardens or tests is ultimately its work.
  • activescott/auth by Scott Willeke (@activescott), MIT. Its OAuth server proposal, activescott/auth#83, carries the hardening list this package adopted as its review checklist and proves item by item in src/__tests__/hardening.test.ts: code reuse revokes, S256-only PKCE, audience on every token, refresh reuse detection, single-use consent, hashed tokens at rest, SSRF-guarded client ID metadata documents. The broader review of its design is in ngdpbase's docs/planning/authentication.md.
  • ngdpbase — its guiding framework and configuration conventions (a reserved key namespace, environment-only secrets, an audit event registry named {target}-{action}, fail closed) are followed here, and its authentication plan is the source of this package's design decisions.
  • Specifications: RFC 6749 (OAuth 2.0), RFC 7636 (PKCE), RFC 8628 (device authorization grant), RFC 8707 (resource indicators), RFC 8176 (amr values), OpenID Connect Core 1.0, the OAuth Client ID Metadata Document draft, and NIST SP 800-63B for the assurance levels a host reports as acr.
  • OpenID Connect Playground (Auth0) — testing against it surfaced the per-client PKCE exemption for confidential clients.

License

MIT