@keyring-dev/express
v0.1.1
Published
Express and Connect middleware for Keyring. Sets req.keyring; verification is CPU-only and makes no network call.
Readme
@keyring-dev/express
Express and Connect middleware for Keyring. MIT.
import express from 'express';
import { keyring } from '@keyring-dev/express';
const app = express();
app.use(
keyring({ resources: { orders: { live: liveOrders, test: testOrders } } }),
);
app.get('/v1/orders', (req, res) =>
res.json({
tenant: req.keyring?.tenantId,
env: req.keyring?.env,
orders: req.keyring?.orders,
}),
);req.keyring is typed by this package — @types/express needs no help from you
— and it is KeyringContext | null, the same shape @keyring-dev/fastify
decorates, so the ?. above is not defensive style: the middleware sets it on
every request it sees, and leaves it null on a route configured skip: true
and on a request it refused. This block is compiled as a TypeScript consumer
would, against the published dist/, and that check pins req.keyring's
exact type.
The guarantee starts at the middleware, and the type cannot say so. Fastify's
decorateRequest puts the field on every request at creation, so
@keyring-dev/fastify's promise is unconditional. Nothing decorates an Express
request: req.keyring is an assignment this middleware makes, so a request
that never reached it has no property at all and reads undefined. That is
every mount above keyring() — a request logger, express.static, your own
session authentication — which is exactly what the mounting guidance below
invites. So read it with ?., never with === null: a !== null guard
compiles and then throws there, and TypeScript will not stop you, because the
augmentation promises a property the middleware has not assigned yet.
secretKey, projectId and baseUrl come from KEYRING_SECRET_KEY,
KEYRING_PROJECT_ID and KEYRING_BASE_URL; none of the three has a default.
A runnable version of exactly this is exercised against a stub control plane
in the source repository's own test suite — the quickstart is tested, not
asserted.
Everything the middleware does is in @keyring-dev/sdk; read its README for
req.keyring, the three onUnavailable modes, the three ergonomic levels and
the telemetry buffer.
Where to mount it
- Before your routes, and before any body parser. Verification needs the
Authorizationheader and nothing else. Making it wait for a body means your JSON parser runs on a 5 MB payload from an unauthenticated caller before anyone has looked at their key. - After
express.static, or the middleware verifies your favicon. - Beside, not instead of, your own authentication. Keyring answers "which tenant of yours is calling, in which environment". It does not answer "which of your staff is calling". Mount both, in whichever order matches which one owns the route.
- Your error handler stays last. This middleware either calls
next()or writes its own 401/403/503; it never throws into yours.
A route configured skip: true leaves req.keyring null and emits no event.
Everything else gets a context, and the request is wrapped in the environment
scope, so currentEnv() works anywhere downstream — including in a job the
handler queues.
