OidcHttpMiddleware
OidcHttpMiddleware
OidcHttpMiddleware extends BaseMiddleware and guards Fastify HTTP routes. It verifies the Authorization: Bearer <token> header and injects the resolved user context onto the request object.
Registration
import { OidcHttpMiddleware } from "ioserver-oidc";
server.addController({
name: "profile",
controller: ProfileController,
middlewares: [OidcHttpMiddleware],
});
The middleware applies to all routes of the controller. To leave some routes unprotected, split them across two controllers — one with the middleware, one without.
Request flow
- Extracts the Bearer token from the
Authorizationheader - Resolves
OidcConfigfromappHandle.oidcConfig.getConfig()(or falls back toprocess.env) - Calls
verifyOidcToken(token, config)— verifies signature,iss,aud, and expiry via remote JWKS - If
appHandle.users.findOrCreate(sub, { email, name })is available, provisions the local user record on first access - Rejects accounts where
isDisabled === truewith403 - Injects auth context onto the request object and calls the route handler
Injected context
After successful authentication the following properties are available on the Fastify request object:
| Property | Type | Description |
|---|---|---|
request.sub | string | OIDC sub claim — stable auth-service user identifier |
request.userId | string | Local DB user ID (from users.findOrCreate; falls back to sub when no users manager) |
request.userRole | string | Primary role — roles[0] or "user" when roles is empty |
request.roles | string[] | Full roles array from the JWT roles claim |
request.permissions | string[] | Permission strings from the JWT permissions claim |
request.features | Record<string, unknown> | Feature flags/limits from the JWT features claim |
In TypeScript, cast the request to any or augment the Fastify type declarations in your app to access these properties without type errors:
import type { FastifyRequest, FastifyReply } from "fastify";
class ProfileController extends BaseController {
async getMe(request: FastifyRequest, reply: FastifyReply) {
const req = request as any;
reply.send({
userId: req.userId,
role: req.userRole,
permissions: req.permissions,
});
}
}
Error responses
| Code | HTTP status | Condition |
|---|---|---|
ERR_AUTH_TOKEN_REQUIRED | 401 | Missing or malformed Authorization header |
ERR_AUTH_TOKEN_INVALID | 401 | JWT signature/claims verification failure |
ERR_USER_DISABLED | 403 | User account isDisabled === true in the local DB |
ERR_USER_PROVISION_FAILED | 500 | users.findOrCreate threw an error |
Response body format:
{
"statusCode": 401,
"error": "Unauthorized",
"code": "ERR_AUTH_TOKEN_REQUIRED",
"message": "Bearer token required."
}
Config caching
The OidcConfig is resolved on the first request and cached on the middleware instance for the lifetime of the server process. Restarting the server clears the cache and re-reads environment variables.