fastify-xauth-jwks
fastify-xauth-jwks
Path-based JWT validation using JWKS (JSON Web Key Sets). Register once and protect any number of URL prefixes — each with its own JWKS endpoint, caching policy, and excluded sub-paths.
Installation
npm install @xenterprises/fastify-xauth-jwks
Quick Start
import Fastify from "fastify";
import xAuth from "@xenterprises/fastify-xauth-jwks";
const fastify = Fastify();
await fastify.register(xAuth, {
paths: {
admin: {
pathPattern: "/admin",
jwksUrl: process.env.ADMIN_JWKS_URL,
excludedPaths: ["/health"],
},
portal: {
pathPattern: "/portal",
jwksUrl: process.env.PORTAL_JWKS_URL,
},
},
});
// Protected — requires valid JWT
fastify.get("/admin/dashboard", (request) => {
return { userId: request.auth.userId, user: request.user };
});
// Excluded — no auth required
fastify.get("/admin/health", () => ({ status: "ok" }));
await fastify.listen({ port: 3000 });
Options
The plugin accepts a single paths object. Each key is a logical name for the path and each value is its configuration.
| Option | Type | Default | Required | Description |
|---|---|---|---|---|
paths | object | — | Yes | Map of path name → path configuration. Must contain at least one entry. |
Path Configuration
| Option | Type | Default | Required | Description |
|---|---|---|---|---|
pathPattern | string | /<pathName> | No | URL prefix to protect. All routes starting with this prefix require a valid JWT. |
jwksUrl | string | — | One of jwksUrl/jwksData | Remote JWKS endpoint URL for token verification. |
jwksData | object | — | One of jwksUrl/jwksData | Local JWKS data ({ keys: [...] }) or a single JWK object. Useful for development and testing. |
active | boolean | true | No | Set to false to skip registering this path entirely. |
excludedPaths | string[] | [] | No | Sub-paths that bypass authentication (e.g., ["/health", "/docs"]). |
jwksCooldownDuration | number | 30000 | No | Minimum milliseconds between JWKS refetches (remote JWKS only). |
jwksCacheMaxAge | number | 1800000 | No | Maximum milliseconds to cache remote JWKS keys. |
enablePayloadCache | boolean | true | No | Cache verified JWT payloads in memory to skip re-verification of repeated tokens. |
payloadCacheTTL | number | 300000 | No | TTL in milliseconds for cached JWT payloads. |
Request Properties
On authenticated requests the plugin sets:
| Property | Type | Description |
|---|---|---|
request.user | object | Full JWT payload (all claims). |
request.auth.path | string | Name of the path that authenticated this request. |
request.auth.userId | string | The sub claim from the JWT. |
request.auth.payload | object | Full JWT payload (same as request.user). |
Methods
Validator Object (fastify.xAuth.validators.<name>)
Each registered path creates a validator accessible at fastify.xAuth.validators.<name>:
- verifyJWT — Verify a JWT string against this path's JWKS. Returns the payload or
null. - clearPayloadCache — Clear the in-memory JWT payload cache for this path.
- getPayloadCacheStats — Get cache statistics (
size,enabled,ttl) for monitoring.
Utility Exports (@xenterprises/fastify-xauth-jwks/utils)
- extractToken — Extract Bearer token from the
Authorizationheader. - hasRole — Check if a JWT payload contains any of the specified roles.
- hasPermission — Check if a JWT payload contains any of the specified permissions.
- getUserId — Get the user ID from
request.auth.userIdorrequest.user.sub. - getAuthEndpoint — Get the auth path name from
request.auth.path. - requireRole — Fastify
preHandlerfactory that replies 403 if the user lacks the role. - requirePermission — Fastify
preHandlerfactory that replies 403 if the user lacks the permission. - requireEndpoint — Fastify
preHandlerfactory that replies 403 if the request was not authenticated by the named path. - decodeToken — Decode JWT payload without verification.
- decodeHeader — Decode JWT header without verification.
Error Reference
Runtime Errors (HTTP responses)
| HTTP Status | Error | When |
|---|---|---|
| 401 | Access token required | No Authorization: Bearer <token> header on a protected route. |
| 401 | Invalid token | Token failed JWKS verification, is expired, or missing the sub claim. |
| 401 | Authentication failed | Unexpected error during authentication (logged server-side). |
| 403 | Insufficient permissions | requireRole or requirePermission check failed. |
| 403 | Must authenticate via <name> endpoint | requireEndpoint check — request was authenticated by a different path. |
Startup Errors (thrown during registration)
| Error Message | Cause |
|---|---|
xAuth: options object is required | No options passed to the plugin. |
xAuth: 'paths' option is required and must contain at least one path configuration | paths is empty or missing. |
<name>: Either jwksUrl or jwksData is required | Path config is missing both JWKS source options. |
<name>: Cannot specify both jwksUrl and jwksData | Path config specifies both JWKS source options. |
Environment Variables
The plugin does not read environment variables directly. Pass JWKS URLs from your environment:
| Variable | Required | Description |
|---|---|---|
ADMIN_JWKS_URL | Per path | JWKS endpoint URL for admin path validation. |
PORTAL_JWKS_URL | Per path | JWKS endpoint URL for portal path validation. |
How It Works
At registration the plugin iterates paths and creates a PathValidator for each active entry. Remote JWKS endpoints are wrapped with jose.createRemoteJWKSet (configurable cooldown and max-age); local JWKS data is wrapped with jose.createLocalJWKSet. An onRequest hook checks request.url.startsWith(pathPattern), skips any excludedPaths, extracts the Bearer token, verifies it against the path's JWKS, and populates request.user and request.auth. Two caching layers reduce latency: the jose library caches remote JWKS keys, and an optional in-memory Map caches verified JWT payloads keyed by raw token string with a configurable TTL.
AI Context
package: "@xenterprises/fastify-xauth-jwks"
type: fastify-plugin
use-when: Path-based JWT validation against remote or local JWKS — multiple route prefixes each with their own JWKS provider
decorator: fastify.xAuth (validators.<name>.verifyJWT, clearPayloadCache, getPayloadCacheStats)
request-decorators: request.user (full JWT payload), request.auth.userId, request.auth.path, request.auth.payload
utility-exports: extractToken, hasRole, hasPermission, getUserId, getAuthEndpoint, requireRole, requirePermission, requireEndpoint, decodeToken, decodeHeader
import-utils: "@xenterprises/fastify-xauth-jwks/utils"
