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

@auths-dev/express

v0.1.3

Published

Drop-in Express middleware that authenticates an Auths-Presentation request and attaches a verified Principal.

Readme

@auths-dev/express

Drop-in Express middleware that authenticates an Auths-Presentation request and attaches a verified Principal. It is the Express analogue of the Axum reference middleware (auths-api's rp_auth): the same injectable verifier, the same single-use challenge store, the same verdict → HTTP-status mapping, and the same extractor-only access to the principal.

A client (an AI agent, a CI job, a service) authenticates a request by presenting proof-of-control of a delegated KERI credential — not a bearer API key. The crypto check is the shipped, pure verifier in @auths-dev/sdk (verifyPresentation).

First-party only. This package is intended for a relying party authenticating its own credentials (issued under roots it pins). It is not a general-purpose third-party OAuth/OIDC server.

Install

npm install @auths-dev/express express @auths-dev/sdk

express and @auths-dev/sdk are peer dependencies.

Quick start

import express from 'express'
import { verifyPresentation } from '@auths-dev/sdk'
import {
  authsAuth,
  challengeHandler,
  ChallengeStore,
  KeriPresentationVerifier,
  RequestWithPrincipal,
} from '@auths-dev/express'

const AUDIENCE = 'api.example.com'
const PINNED_ROOTS = ['did:keri:Eexample_root_aid'] // `.auths/roots` — DID-only

const challenges = new ChallengeStore({ maxLive: 10_000 })

const verifier = new KeriPresentationVerifier({
  audience: AUDIENCE,
  challenges,
  pinnedRoots: PINNED_ROOTS,
  loadInputs: async (credentialSaid) => {
    /* resolve the issuer/subject/delegator KELs, TEL, and receipts from your registry */
  },
  verifyPresentation,
})

const app = express()

// 1. The challenge mint route — clients GET a nonce, sign over it, then present.
app.get('/v1/auth/challenge', challengeHandler({ audience: AUDIENCE, challenges }))

// 2. A guarded route — only a current-key holder with `acme:deploy` reaches the handler.
app.post('/v1/deploy', authsAuth({ verifier, capabilityFor: () => 'acme:deploy' }), (req, res) => {
  const principal = (req as RequestWithPrincipal).principal
  res.json({ deployedBy: principal.subject, caps: principal.caps })
})

How it works

authsAuth(...) returns an Express RequestHandler that, in order:

  1. Strips any client-supplied req.principal (anti-forgery) before doing anything — only the middleware ever sets the verified principal.
  2. Reads the Authorization: Auths-Presentation <token> header; missing or wrong-scheme → 401.
  3. Calls verifier.verify(token, now), which consumes the single-use challenge from the store and runs the crypto check. On failure it sends the denial's status:
    • malformed / missing / expired / revoked / wrong-audience / holder-not-current / replayed → 401
    • missing capability → 403
    • challenge store full → 503
  4. Enforces capabilityFor(req) against the principal's capabilities; missing → 403.
  5. Attaches the verified Principal and calls next().

Unexpected (non-denial) async errors are surfaced via next(err) — never swallowed. The nonce and signature are never logged.

The injectable verifier

The crypto step is injected behind the PresentationVerifier interface, exactly like the Rust PresentationVerifier trait:

interface PresentationVerifier {
  verify(wireToken: string, now: Date): Promise<Principal>
}
  • Production uses KeriPresentationVerifier, which wraps the Node binding's verifyPresentation plus your loadInputs.
  • Tests inject a fake verifier over the real ChallengeStore, so replay and wrong-audience protection are genuinely exercised with no native .node binary.

Typed handlers (RequestWithPrincipal)

The principal is not a global Express.Request augmentation (optional-everywhere proves nothing). Instead, a guarded handler reads it through RequestWithPrincipal (where principal is non-optional) or narrows with the hasPrincipal(req) type guard:

import { hasPrincipal } from '@auths-dev/express'

app.get('/v1/whoami', (req, res) => {
  if (!hasPrincipal(req)) return res.sendStatus(401)
  res.json({ subject: req.principal.subject })
})

A handler typed with a plain Request cannot read req.principal — it is a compile error.

Pinned roots

pinnedRoots is DID-only: it pins who may issue (.auths/roots), never what they may grant. Capabilities always come from the credential and are enforced separately. An empty pinnedRoots set denies everything (fail-closed).

Challenge binding (the default) vs. TTL

Challenge mode is the default and the recommended path. The client fetches a fresh, single-use nonce from /v1/auth/challenge, signs over it, and presents it; the store's remove-on-read consume guarantees the nonce verifies exactly once.

TTL (non-interactive) binding is behind explicit opt-in. Its residual risk: a TTL nonce can be replayed within its TTL window — there is no single-use store entry to burn. Prefer challenge mode unless you cannot run the interactive round-trip.

Single-process caveat

ChallengeStore lives in one process's heap. Behind a load balancer fronting N nodes, a nonce minted on node A is unknown to node B, and the single-use guarantee holds only per node — a within-TTL replay can land on a different node than the original. Single-process or sticky-session deployments are safe; a multi-node deployment must supply a shared store backend (Redis/SQL) implementing the same issue/consume contract.

Client helper

import { fetchChallenge } from '@auths-dev/express'

const { nonce, notAfter } = await fetchChallenge('https://api.example.com/v1/auth/challenge')
// sign over `nonce`, build the `Auths-Presentation` header, then call your guarded route

License

Apache-2.0