Authentication (OIDC / Keycloak)
TracePcap ships with optional OIDC authentication backed by a bundled
Keycloak identity provider. It is off by default — the base stack
(docker compose up -d) runs with no login, unchanged from before. Enable it
by layering the production overlay, which adds Keycloak, gates the backend API
behind a JWT, and rebuilds the frontend with the login flow.
Note
Authentication is disabled in the base stack so single-user, air-gapped, and
automated (e.g. Lanturn) deployments keep working with no login. Only the
docker-compose.prod.yml overlay turns it on.
Enabling authentication
Run the base compose file plus the production overlay, setting
PUBLIC_URL to the exact origin you browse to:
PUBLIC_URL=http://localhost:8888 \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
PUBLIC_URL must be the scheme + host + port you actually load the app from,
e.g. http://localhost:8888, http://192.168.1.10:8888, or a Tailscale
http://100.x.y.z:8888. It pins Keycloak’s token issuer and the
backend’s issuer check, so the browser must reach the app via this same origin.
Warning
PUBLIC_URL does not track NGINX_PORT. It defaults to
http://localhost:8888; for a default local prod run on port 80 you
must set PUBLIC_URL=http://localhost (or your real port), or Keycloak
will reject the redirect_uri / fail the issuer check.
Default demo login
The bundled tracepcap realm (keycloak/realm-export.json) ships with a
demo user. Change these for any real deployment.
Credential |
Default |
Where to change |
|---|---|---|
App login (demo user) |
|
Keycloak admin console → Users, or edit the realm export |
Keycloak admin |
|
|
The Keycloak admin console is served same-origin at /admin.
How it works
Same-origin proxy. nginx proxies Keycloak (the /realms, /resources,
/admin, /js paths) so the browser reaches the identity provider at the
same host:port it loaded the app from. There is no second exposed port and
no CORS. The frontend derives the OIDC authority from window.location at
runtime, so nothing about the host is baked into the build.
Frontend. When VITE_AUTH_ENABLED=true (set by the overlay) the SPA
mounts react-oidc-context, redirects unauthenticated users to Keycloak via
PKCE, and gates the app until login completes. The current route is preserved as
OIDC state and restored after login (deep-link). An avatar/name dropdown
provides logout. The access token is read from oidc-client-ts storage and
attached to every API request through the shared axios client.
Backend. With TRACEPCAP_AUTH_ENABLED=true the API runs as a stateless
OAuth2 resource server: /api is gated behind a Keycloak JWT. The
JwtDecoder validates the token issuer against the public PUBLIC_URL but
fetches JWKS from the internal Keycloak host
(http://keycloak:8080/...), decoupling key retrieval from issuer validation
inside Docker. Issuer validation is fail-closed — a missing or mismatched issuer
is rejected.
HTTPS / TLS
Keycloak runs in start-dev mode (HTTP, no TLS) for offline use or where TLS
is terminated by an upstream reverse proxy. nginx strips Keycloak’s HSTS header
so it cannot poison a plain-HTTP origin into HTTPS-only. For internet-facing
deployments, terminate TLS in front of nginx and set PUBLIC_URL to the
https:// origin. See Production Hardening.