Authentication
DCM can require authentication for control-plane API access. When authentication is enabled, clients must present a valid JWT bearer token (or use the supported proxy-header path). This page describes how to configure and use authenticated environments. For compose and Helm operator steps, see the control-plane deploy guide.
Overview
By default, local deployments run with authentication disabled
(AUTH_DISABLED=true). Any client that can reach the API can call protected
endpoints without credentials. This keeps getting-started tutorials simple.
For shared or production environments, operators should enable
authentication so every API request is tied to an identity. The control plane
validates JSON Web Tokens (JWTs) issued by an OpenID Connect (OIDC) identity
provider. The reference local stack uses Keycloak
with a pre-imported dcm realm.
The /api/v1alpha1/health endpoint stays unauthenticated whether or not
auth is enabled.
Service providers: Service providers reach the control plane through the environment agent, not direct HTTP calls. End-to-end authentication for the agent and service providers is still in progress (for example environment-agent#38 and environment-agent#35). Enabling auth on the control plane can still affect SP registration and instance workflows until that chain is complete. Use auth for CLI, UI, and direct API access first, or keep auth disabled while exercising full SP flows locally.
How authentication works
- A user or automation obtains an access token from the identity provider (Keycloak in the reference stack).
- The client sends
Authorization: Bearer <access_token>on each API request. - The control plane validates the token (signature, expiry, issuer, audience) using the provider’s JWKS keys, then resolves the caller to a DCM actor.
- On first login, unknown identities are provisioned automatically
(just-in-time provisioning). No manual database setup is required for new
Keycloak users in the
dcmrealm.
Tokens may need an aud claim that matches AUTH_JWT_AUDIENCE. The control
plane does not default this variable: if it is empty, audience validation is
disabled (any client token from the issuer realm can be accepted). For
production-like setups, set AUTH_JWT_AUDIENCE explicitly (the reference
Keycloak realm uses dcm-api in deploy/.env.example when auth is enabled).
An optional proxy-header path accepts X-Forwarded-User and
X-Forwarded-Preferred-Username when X-Auth-Proxy-Secret matches
AUTH_PROXY_SECRET. See
Authentication middleware
in the authentication enhancement. Most users rely on JWT bearer tokens instead.
Prerequisites for authenticated deployments
Before users can log in, the platform operator must:
- Run an OIDC identity provider reachable from both the control plane and
clients (Keycloak with the
authcompose profile in local stacks). - Set control-plane authentication environment variables (see below).
- Ensure JWT clients exist for your callers (reference realm includes
dcm-clifor the CLI device flow anddcm-proxyfor programmatic access). - Set
DCM_ADMIN_SUBJECTto the Keycloaksubof the bootstrap admin user when auth is first enabled.
To start the reference compose stack with authentication:
cd control-plane
cp deploy/.env.example deploy/.env
# Uncomment the "Enable authentication" block in deploy/.env
make compose-up AUTH=trueKeycloak is published at http://localhost:8180 when the auth profile is
active. Lab usernames and passwords are documented in
deploy/RUN.md.
For Kubernetes installs, create the auth secret and set auth.enabled=true as
described in the
Helm chart README.
Control-plane configuration
| Variable | Purpose |
|---|---|
AUTH_DISABLED | When true, auth middleware is bypassed (default for local dev). |
AUTH_ISSUER_URL | OIDC issuer URL for JWT validation (for example http://keycloak:8080/realms/dcm). |
AUTH_JWT_AUDIENCE | Expected aud claim when set; empty disables audience checks (see above). |
AUTH_PROXY_SECRET | Shared secret for the proxy-header auth path (optional). |
AUTH_CACHE_TTL | How long resolved actors are cached (default 60s). |
DCM_ADMIN_SUBJECT | Keycloak subject UUID for the seed admin actor (required when auth is enabled). |
Copy secrets and toggles from deploy/.env.example into deploy/.env.
Warning: Do not commit real production secrets to documentation or source control.
Authenticated user workflows
CLI (interactive)
The DCM CLI authenticates with the OIDC device authorization flow:
- Configure the OIDC issuer (once per environment).
- Run
dcm login. - Open the URL shown in the terminal and approve the request in the browser.
- Run normal
dcmcommands; the CLI attaches a bearer token and refreshes it before expiry.
Local compose: host access to Keycloak
The reference stack sets the Keycloak hostname so the issuer is
http://keycloak:8080/realms/dcm. That name resolves on the compose network,
not on your laptop by default. Before you run dcm login on the host, map
keycloak to the Keycloak container IP (repeat after recreating the container
if the IP changes):
KC_IP=$(podman inspect "$(podman ps -q --filter name=keycloak)" \
--format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
grep -qE '[[:space:]]keycloak$' /etc/hosts \
&& sudo sed -i -E "s/^[0-9.]+[[:space:]]+keycloak$/${KC_IP} keycloak/" /etc/hosts \
|| echo "${KC_IP} keycloak" | sudo tee -a /etc/hosts
getent hosts keycloakConfirm the issuer string matches what the control plane expects:
curl -sf http://keycloak:8080/realms/dcm/.well-known/openid-configuration \
| jq -r .issuerYou should see http://keycloak:8080/realms/dcm. The admin UI is also at
http://localhost:8180, but do not use localhost:8180 as the issuer URL on
stock compose: discovery still advertises keycloak:8080, and the control plane
must use the same issuer as AUTH_ISSUER_URL.
Example login
Example for the reference Keycloak realm after host resolution is in place:
dcm login --issuer-url http://keycloak:8080/realms/dcm \
--control-plane-url http://localhost:8080
dcm catalog item listdcm login saves issuer-url in ~/.dcm/config.yaml. Tokens are stored in the
OS keyring when available, otherwise in ~/.dcm/tokens.json (mode 0600).
To sign out and remove stored tokens:
dcm logoutSee CLI Configuration for flags and environment variables.
Web UI (Backstage)
The DCM UI runs as a Backstage plugin. In authenticated deployments, sign in through whatever identity provider your Backstage (or Red Hat Developer Hub) instance uses. The plugin forwards OAuth2 bearer tokens to the control plane, which validates them the same way as CLI and API tokens.
Configure Backstage SSO to use the same Keycloak realm (or corporate IdP) your
control plane trusts. The local compose dcm-ui image does not configure
Backstage SSO by itself; treat UI login as an operator concern tied to your
Backstage deployment.
Adding users (Keycloak)
In the reference stack, create users in the Keycloak admin console
(http://localhost:8180, realm dcm). New users receive DCM actors on first
authenticated API call. See
Adding users
in the deploy guide.
Obtaining and using tokens
CLI session tokens
After dcm login, you do not pass tokens manually. The CLI reads stored
credentials using issuer-url from config.
Static bearer token (CI and scripts)
For non-interactive use, pass a bearer access token with --token (or set
DCM_TOKEN). Point the CLI at the control plane with --control-plane-url when
it is not the default http://localhost:8080:
dcm catalog item list \
--token "<access-token>" \
--control-plane-url http://localhost:8080Static tokens skip the device flow and do not use the token store. Obtain access tokens from your identity provider (for example Keycloak token endpoint with a client your operator provisioned).
Authenticating API requests
Send the access token on protected API requests:
curl -s \
-H "Authorization: Bearer ${DCM_TOKEN}" \
http://localhost:8080/api/v1alpha1/catalog-itemsWithout a valid token when auth is enabled, the API returns 401 Unauthorized.
If the identity is valid but the DCM actor is suspended or deactivated, the API
returns 403 Forbidden.
Troubleshooting
| Symptom | Things to check |
|---|---|
401 Unauthorized on API or CLI | Auth enabled; token present and not expired; issuer matches AUTH_ISSUER_URL; if AUTH_JWT_AUDIENCE is set, token aud must match. |
dcm login cannot reach issuer | Host maps keycloak (see host access); issuer matches discovery; TLS if using HTTPS. |
| Issuer / JWKS errors in control-plane logs | AUTH_ISSUER_URL reachable from the control-plane container; Keycloak healthy (auth profile running). |
| CLI works but UI fails (or reverse) | Backstage SSO and control plane must trust the same IdP and audience; plugin backend URL points at the control plane. |
403 Forbidden with valid token | Actor suspended or deactivated; wait up to AUTH_CACHE_TTL after status changes. |
| Service provider errors after enabling auth | SP traffic uses the environment agent; auth for agent and SP paths is still landing. See SP callout in Overview. |
| Device flow times out | Complete browser approval within the time shown; retry dcm login. |
Verify Keycloak readiness when using compose:
curl -sf http://localhost:8180/realms/dcm/.well-known/openid-configuration | jq .issuerThe issuer string must match AUTH_ISSUER_URL and the issuer you pass to
dcm login.
For container logs and restarts, see Troubleshooting.