@authup/server-adapter-node
v1.0.0-beta.72
Published
Authenticate Node http and Express requests with Authup access tokens.
Maintainers
Readme
Why @authup/server-adapter-node?
Every route of an API behind Authup needs the same first step: take the Authorization header, verify the token Authup issued and attach who is calling. This package is that step for anything built on Node's IncomingMessage, with the verification itself done by @authup/server-adapter-kit.
| Concern | Without it | With it |
|---|---|---|
| Header parsing | Split Bearer <token> yourself and handle malformed values | Parsed for you; a malformed header becomes an error passed to next |
| Verification | Fetch keys or call introspection per framework | One TokenVerifier, local or remote, shared with your other transports |
| Tokens outside the header | Hand-written cookie or query fallbacks | tokenByRequest reads a fallback when no Authorization header is sent |
| Bound tokens | Compare certificate thumbprints yourself | certificateThumbprintByRequest feeds the RFC 8705 check |
Installation
npm install @authup/server-adapter-node @authup/server-adapter-kitTokenVerifier comes from @authup/server-adapter-kit, which you import directly. The package is ESM-only, has no peer dependencies and needs Node.js ^22.13.0 || ^23.5.0 || >=24.0.0.
Usage
Middleware
import type { TokenVerificationData } from '@authup/server-adapter-kit';
import { TokenVerifier } from '@authup/server-adapter-kit';
import { createMiddleware } from '@authup/server-adapter-node';
declare module 'node:http' {
interface IncomingMessage {
auth?: TokenVerificationData;
}
}
app.use(createMiddleware({
tokenVerifier: new TokenVerifier({ baseURL: 'https://auth.example.com' }),
tokenVerifierHandler: (req, data) => {
req.auth = data; // data.sub, data.sub_kind, data.realm_name, data.scope
},
}));
app.get('/orders', (req, res) => {
if (!req.auth) {
res.statusCode = 401;
res.end();
return;
}
res.end(`orders of ${req.auth.sub}`);
});The module augmentation types req.auth once for every handler. Express's Request extends IncomingMessage, so it sees the property too.
- A request without a token calls
next()without runningtokenVerifierHandler. Reject it in your route if the route needs a caller. - A malformed header or a token that fails verification calls
next(error); the handler is not run. - An error thrown by
tokenVerifierHandleris passed tonext(error)too.
See @authup/server-adapter-kit for local versus remote verification, caching and permissions.
Verify inside a handler
import { createServer } from 'node:http';
import { TokenVerifier } from '@authup/server-adapter-kit';
import { verifyRequest } from '@authup/server-adapter-node';
const tokenVerifier = new TokenVerifier({ baseURL: 'https://auth.example.com' });
createServer(async (req, res) => {
try {
const data = await verifyRequest(req, { tokenVerifier });
res.statusCode = data ? 200 : 401;
res.end(data ? data.sub : 'unauthenticated');
} catch {
res.statusCode = 401;
res.end('invalid token');
}
});verifyRequest resolves the verification data, resolves undefined when the request carries no token, and throws otherwise.
Read the token from elsewhere
const middleware = createMiddleware({
tokenVerifier,
tokenByRequest: (req) => readCookie(req, 'access_token'),
certificateThumbprintByRequest: (req) => thumbprintOf(req),
tokenVerifierHandler: (req, data) => {
req.auth = data;
},
});tokenByRequest is only called when the request has no Authorization header. Its value may carry a Bearer prefix or not. The adapter parses no cookies itself.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| tokenVerifier | ITokenVerifier | (required) | Usually a TokenVerifier from @authup/server-adapter-kit |
| tokenVerifierHandler | (req, data) => void \| Promise<void> | (required, middleware only) | Runs after a token verified; store the claims here |
| tokenByRequest | (req) => string \| undefined | undefined | Fallback token source when no Authorization header is present |
| certificateThumbprintByRequest | (req) => string \| undefined \| Promise<string \| undefined> | undefined | SHA-256 thumbprint of the client certificate, for certificate-bound tokens |
API
| Export | Kind | Purpose |
|---|---|---|
| createMiddleware | function | Builds the (req, res, next) middleware |
| verifyRequest | function | Verifies one IncomingMessage without the middleware shape |
| MiddlewareOptions, VerifyRequestOptions | types | The option objects above |
| Middleware, Next | types | The middleware and next signatures |
Notes
- Only
Beareris accepted in the header. ABasicor other scheme inAuthorizationis an error, not a missing token, sotokenByRequestis not consulted for it. - Errors carry their codes. A malformed header throws
BearerTokenMalformedError(bearer_token_malformed); a failed verification throws aJWTError(invalid_token,expired_token,inactive_token). Both map to HTTP 401. - A certificate-bound token without a thumbprint fails. Behind a TLS-terminating proxy, read the forwarded certificate in
certificateThumbprintByRequest.
License
Made with 💚
Published under the Apache 2.0 License.
