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, notrequired)
Keycloak is AGPL-3.0. Like Grafana in the main dev stack, it is local test tooling and is never shipped in release artifacts.
Prerequisites
Section titled “Prerequisites”- Docker with Compose v2
openssl- The main Nexus dev stack (
just dev) or at leastauth+consolewhen exercising browser sign-in /etc/hostsentry so the browser can reach Keycloak on the same hostname the auth service uses:
127.0.0.1 keycloakWithout 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.
Quick start
Section titled “Quick start”From the repository root:
# 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 connectionjust 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:
just keycloak-mtls-downWhat the overlay configures
Section titled “What the overlay configures”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/callbackCertificates
Section titled “Certificates”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 ClientGenerated material is gitignored under certs/.
Smoke script
Section titled “Smoke script”smoke-mtls.sh verifies:
- OIDC discovery succeeds when the custom CA bundle is supplied
- Token requests without a client certificate do not succeed
- Token requests with the client certificate pass client authentication (a fake code yields
invalid_grant, notinvalid_client) - A real mTLS token exchange retrieves
sub,email,email_verified, andnamefrom UserInfo with its access token
Run from the host (Keycloak published on localhost:8443):
KEYCLOAK_BASE_URL=https://localhost:8443 just keycloak-mtls-smokeRun from inside the Compose network (default KEYCLOAK_BASE_URL=https://keycloak:8443):
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.shTroubleshooting
Section titled “Troubleshooting”| 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 |
Relation to PingFederate
Section titled “Relation to PingFederate”| 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 |
