X Enterprises

fastify-xauth-jwks

Path-based JWT/JWKS validation for Fastify v5. Protect multiple route prefixes with independent JWKS providers.

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.

OptionTypeDefaultRequiredDescription
pathsobjectYesMap of path name → path configuration. Must contain at least one entry.

Path Configuration

OptionTypeDefaultRequiredDescription
pathPatternstring/<pathName>NoURL prefix to protect. All routes starting with this prefix require a valid JWT.
jwksUrlstringOne of jwksUrl/jwksDataRemote JWKS endpoint URL for token verification.
jwksDataobjectOne of jwksUrl/jwksDataLocal JWKS data ({ keys: [...] }) or a single JWK object. Useful for development and testing.
activebooleantrueNoSet to false to skip registering this path entirely.
excludedPathsstring[][]NoSub-paths that bypass authentication (e.g., ["/health", "/docs"]).
jwksCooldownDurationnumber30000NoMinimum milliseconds between JWKS refetches (remote JWKS only).
jwksCacheMaxAgenumber1800000NoMaximum milliseconds to cache remote JWKS keys.
enablePayloadCachebooleantrueNoCache verified JWT payloads in memory to skip re-verification of repeated tokens.
payloadCacheTTLnumber300000NoTTL in milliseconds for cached JWT payloads.

Request Properties

On authenticated requests the plugin sets:

PropertyTypeDescription
request.userobjectFull JWT payload (all claims).
request.auth.pathstringName of the path that authenticated this request.
request.auth.userIdstringThe sub claim from the JWT.
request.auth.payloadobjectFull 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 Authorization header.
  • 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.userId or request.user.sub.
  • getAuthEndpoint — Get the auth path name from request.auth.path.
  • requireRole — Fastify preHandler factory that replies 403 if the user lacks the role.
  • requirePermission — Fastify preHandler factory that replies 403 if the user lacks the permission.
  • requireEndpoint — Fastify preHandler factory 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 StatusErrorWhen
401Access token requiredNo Authorization: Bearer <token> header on a protected route.
401Invalid tokenToken failed JWKS verification, is expired, or missing the sub claim.
401Authentication failedUnexpected error during authentication (logged server-side).
403Insufficient permissionsrequireRole or requirePermission check failed.
403Must authenticate via <name> endpointrequireEndpoint check — request was authenticated by a different path.

Startup Errors (thrown during registration)

Error MessageCause
xAuth: options object is requiredNo options passed to the plugin.
xAuth: 'paths' option is required and must contain at least one path configurationpaths is empty or missing.
<name>: Either jwksUrl or jwksData is requiredPath config is missing both JWKS source options.
<name>: Cannot specify both jwksUrl and jwksDataPath config specifies both JWKS source options.

Environment Variables

The plugin does not read environment variables directly. Pass JWKS URLs from your environment:

VariableRequiredDescription
ADMIN_JWKS_URLPer pathJWKS endpoint URL for admin path validation.
PORTAL_JWKS_URLPer pathJWKS 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"
Copyright © 2026