Skip to content
↑↓Navigate↵SelectescClose

Keycloak mutual-TLS OIDC fixture

Exercise federated sign-in using a local Keycloak fixture with mutual Transport Layer Security (TLS)

Dev-only IdP that mirrors the PingFederate posture this branch adds:

  • HTTPS served from a custom CA (not in any system trust store)
  • Token-endpoint client authentication via TLS client certificate (client-x509)
  • Browser login still uses username/password (https-client-auth=request, not required)

Keycloak is AGPL-3.0. Like Grafana in the main dev stack, it is local test tooling and is never shipped in release artifacts.

  • Docker with Compose v2
  • openssl
  • The main Nexus dev stack (just dev) or at least auth + console when exercising browser sign-in
  • /etc/hosts entry so the browser can reach Keycloak on the same hostname the auth service uses:
127.0.0.1 keycloak

Without this entry, discovery and token exchange from the auth container still work (it resolves keycloak on the Compose network), but the browser redirect after sign-in will fail.

From the repository root:

Terminal window
# 1. Issue the standalone test PKI (writes deploy/compose/keycloak-mtls/certs/)
just keycloak-mtls-certs
# 2. Bring up Keycloak and reconfigure auth for the mTLS connection
just keycloak-mtls-up
# 3. Headless transport/protocol checks (discovery + token endpoint mTLS)
just keycloak-mtls-smoke
# 4. Browser sign-in
# - ensure `just dev` (or at least console + auth) is running
# - open http://localhost:14449/sign-in
# - click "Sign in with Keycloak"
# - accept the custom-CA warning in the browser (or import ca.crt locally)
# - log in as demo / demo (demo@corp.example)

Tear down only Keycloak:

Terminal window
just keycloak-mtls-down

deploy/compose/docker-compose.keycloak-mtls.yml merges with the main dev Compose files and:

Component Setting
Keycloak Production start --optimized, https-client-auth=request, custom server cert, test CA in conf/truststores/
Realm mtls User demo / demo, client nexus-console with client-x509 authenticator
auth AUTH_OIDC_KEYCLOAK_* with _CLIENT_AUTH=tls, CA bundle + client identity mounts

Issuer URL (auth service): https://keycloak:8443/realms/mtls

Redirect URI registered at Keycloak:

http://localhost:14449/api/session/oidc/keycloak/callback

generate-certs.sh writes:

File Purpose
certs/ca.crt AUTH_OIDC_CA_BUNDLE_PATH — trust the Keycloak HTTPS chain
certs/client.crt / certs/client.key Nexus client identity for token-endpoint mTLS
certs/server.crt / certs/server.key Keycloak HTTPS listener
certs/truststore.p12 Optional passwordless PKCS#12 copy of the CA (the image uses conf/truststores/nexus-test-ca.crt)

Client certificate subject DN (must match realm-mtls.json):

O=Nexus Test CA,OU=Nexus Dev,CN=Nexus OIDC Client

Generated material is gitignored under certs/.

smoke-mtls.sh verifies:

  1. OIDC discovery succeeds when the custom CA bundle is supplied
  2. Token requests without a client certificate do not succeed
  3. Token requests with the client certificate pass client authentication (a fake code yields invalid_grant, not invalid_client)
  4. A real mTLS token exchange retrieves sub, email, email_verified, and name from UserInfo with its access token

Run from the host (Keycloak published on localhost:8443):

Terminal window
KEYCLOAK_BASE_URL=https://localhost:8443 just keycloak-mtls-smoke

Run from inside the Compose network (default KEYCLOAK_BASE_URL=https://keycloak:8443):

Terminal window
docker compose -f deploy/compose/docker-compose.yml \
-f deploy/compose/docker-compose.keycloak-mtls.yml \
exec -T auth deploy/compose/keycloak-mtls/smoke-mtls.sh
Symptom Likely cause
auth fails at startup with CA/identity path errors Run just keycloak-mtls-certs before just keycloak-mtls-up
Browser cannot open Keycloak after clicking sign-in Missing 127.0.0.1 keycloak in /etc/hosts
TLS warning in browser Expected — import certs/ca.crt as a trusted root or proceed manually
invalid_client at token exchange Client cert DN mismatch or auth missing identity mounts
Discovery TLS error from auth AUTH_OIDC_CA_BUNDLE_PATH not mounted or wrong ca.crt
keycloak healthcheck never passes First start can take ~60s; the healthcheck only verifies the HTTPS port is listening
auth still shows PingOne after keycloak-mtls-up Recreate failed (often a dev-image rebuild); run docker compose … up -d --no-build --force-recreate auth with the Keycloak overlay
Keycloak logs Failed to load https-trust-store Use the image as built (PEM CA in conf/truststores/); do not point at a password-protected PKCS#12
Keycloak logs Key material not provided Regenerate certs (chmod 755 on certs/) and rebuild the Keycloak image
PingFederate This fixture
Corporate CA bundle certs/ca.crt → AUTH_OIDC_CA_BUNDLE_PATH
Public cert registered for Nexus client certs/client.crt
mTLS token endpoint (often secondary port) Same listener; use _TOKEN_ENDPOINT if Ping publishes mtls_endpoint_aliases
tls_client_auth client auth Keycloak client-x509 + _CLIENT_AUTH=tls