Keycloak auth
@worker-manager/auth puts a Keycloak (OpenID Connect) login in front of the dashboard. Browsers
go through the authorization code flow with PKCE and keep an encrypted session cookie; scripts and
other services send a bearer token. It is one framework-agnostic middleware, so the same options
work on Express, Fastify, Koa, NestJS and the CLI.
Configure the Keycloak client
In your realm, create an OpenID Connect client (say worker-manager) with:
- Client authentication on (a confidential client), or off for a public client without a secret.
- Standard flow enabled. Enable Direct access grants only if scripts log in with a password.
- Valid redirect URIs:
https://ops.example.com/queues/auth/callback - Valid post logout redirect URIs:
https://ops.example.com/queues/ - Advanced → Proof Key for Code Exchange Code Challenge Method:
S256
Then create a realm role (say wm-admin) and assign it to the people who should see the board.
Client roles on the worker-manager client work too.
Express
Fastify
The hook lives in the board plugin's own scope, so it guards the board's page, API and assets and nothing else in your app.
NestJS
Pass the same options as auth, typically from ConfigService:
See NestJS for the rest of the module options.
What happens to a request
The middleware also serves, under the board's base path:
GET /auth/mereturns{ strategy, user: { username, name, email, roles }, logoutUrl }. The UI uses it to show who is signed in.GET /auth/login?returnTo=/queues/...starts a login explicitly.GET /auth/callbackis the redirect URI.GET /auth/logoutclears the session and sends the browser to Keycloak's end-session endpoint.
The session cookie
The cookie (wm_session by default) is HttpOnly, SameSite=Lax, scoped to the base path,
Secure when the board is on https, and encrypted with AES-256-GCM using cookie.secret. It holds
the user's claims, the access expiry and, when they fit in the 4 KB cookie limit, the refresh
token and the ID token (used as the logout hint).
Set cookie.secret to a long random value and share it across instances. Without it the
middleware falls back to clientSecret, and failing that to a random per-process key, which logs
everyone out on every restart.
Behind a reverse proxy
The redirect URI must match what Keycloak has registered. Set publicUrl to the board's external
URL, base path included. Without it, the URL is built from the request's X-Forwarded-Proto,
X-Forwarded-Host (or Host) and the base path, so make sure your proxy sends those.
API clients only
bearerOnly: true disables the browser flow: every request without a valid bearer token gets a
401, and /auth/login, /auth/callback and /auth/logout are not served. No cookie secret is
needed.
Extra checks
onAuthenticated(user, req) runs after the role check. Return false to answer 403, for rules
the options cannot express:
Combine with read-only mode
Give viewers a board they cannot break: run a second, read-only board
for a wider role, and keep requiredRoles: ['wm-admin'] on the writable one.