Connecting your identity provider
A self-hosted ConfigHub instance signs users in through Keycloak, which you run and which federates to your own identity provider over OpenID Connect or SAML. This page covers what ConfigHub needs from that setup: the configuration the server reads, the claims it expects in a token, and how to check a running instance.
An evaluation install needs none of this. cub server install creates an instance with one
local administrator who signs in with a generated key, which is enough to try ConfigHub and
enough to run it alone. Add an identity provider when other people need to sign in.
How the pieces fit
your users ──► your IdP (Okta, Entra ID, Google Workspace, Auth0, …)
│ OIDC or SAML federation
▼
Keycloak ──► realm, organization, clients
│ tokens
▼
ConfigHub ──► resolves the user and their organization from the token
Keycloak is the broker, and ConfigHub talks only to it. Your identity provider stays where it is, and the accounts, groups and lifecycle in it remain yours. ConfigHub never holds a user's password.
A user's organization matters as much as their identity. ConfigHub resolves the organization from the token, and someone who belongs to none can sign in and will then be told their account is pending approval. In Keycloak that membership comes from an organization whose email domain matches theirs, which is why the organization and the federation are set up together.
The quickest path
The cub server plugin can install Keycloak
alongside an instance and wire the two together:
cub server keycloak install # Keycloak, a realm, and the clients ConfigHub needs
cub server keycloak idp -i # federate it to your identity provider
The second command asks for your organization's name and email domain, then for your provider's OpenID discovery URL and the client id and secret of an application registered for ConfigHub at their end. It finishes by printing a redirect URI to register with your provider; sign-in fails on the way back without it.
The bundled Keycloak runs in development mode with an embedded database, which is what lets
it come up without a certificate or DNS. An instance serving real users wants a Keycloak of
its own, deployed and backed up like any other stateful component. For one you run, three
commands set up the realm and write the changes to the server's configuration:
cub server keycloak bootstrap, configure and idp. Single sign-on
walks through them. The rest of this page is a reference to what ConfigHub needs from
Keycloak, however it was set up.
What the server reads
ConfigHub takes its identity configuration from environment variables. With neither of the first two set, the instance runs with local identities only; setting one without the other is refused at startup rather than treated as a fallback.
| Variable | Required | What it is |
|---|---|---|
KEYCLOAK_REALM |
yes | The realm your users live in. |
KEYCLOAK_AUTH_URL |
yes | Where a browser reaches Keycloak. Every token names this as its issuer, so it must be the address people really use. |
KEYCLOAK_INTERNAL_URL |
when they differ | Where the server reaches Keycloak, if that is not the browser's address. Running Keycloak in the same cluster usually means it is not. |
KEYCLOAK_CLIENT_ID |
yes | The client the server authenticates as. |
KEYCLOAK_DEVICE_CLIENT_ID |
no | The public client the CLI's device login runs through. Defaults to cub. |
CONFIGHUB_AUTH_ISSUER |
for browser apps | The issuer a browser app runs OIDC against: your realm's URL. Advertised as AuthIssuer in /api/info. |
CONFIGHUB_TOKEN_EXCHANGE_AUDIENCE |
with the issuer | The audience this instance requires in a token it will accept. Any stable string; tokens must carry it in aud. Advertised as TokenExchangeAudience in /api/info. |
CONFIGHUB_UI_URL |
for the web UI | Where the instance's web UI is served. Advertised as UIURL in /api/info, so clients can link to pages of the UI. |
The web UI runs as its own container (ghcr.io/confighub/ui) and reads its own settings.
On an instance with an identity provider it needs CONFIGHUB_UI_OAUTH_CLIENT_ID, the OAuth
client it signs in as, alongside CONFIGHUB_URL, where a browser reaches the API.
The server authenticates to Keycloak either with a client secret (KEYCLOAK_CLIENT_SECRET)
or, preferably, by signing an assertion with a private key
(KEYCLOAK_CLIENT_PRIVATE_KEY_JWK) whose public half sits on the Keycloak client with
clientAuthenticatorType: client-jwt. With the key, the deployment holds no shared secret
and no Keycloak admin password. cub server key keycloak-client generates a usable pair.
Set CONFIGHUB_TOKEN_SIGNING_KEY_JWK as well. It is the key ConfigHub signs its own tokens
with, and without it a fresh one is generated at every start: sessions end on each restart,
and two replicas reject each other's tokens. cub server key signing generates one. Servers
before v0.9.5 read it as JWT_PRIVATE_KEY_JWK.
What the realm must contain
Clients. A confidential client for the server, a public client for the CLI's device
login, and a public client for each browser app, including ConfigHub's own web UI. Browser
clients hold no secret, so they use PKCE with exact redirect URIs. cub oauthclient create
registers browser apps for you, with the right posture, and is the supported way to add one.
An organization, with ids in the token. Keycloak's organization mapper emits bare
aliases unless Add organization ID is enabled on the realm's organization client
scope. ConfigHub matches on the id, so a realm that emits only aliases produces a sign-in
that fails with subject_token carries no organization, even though the user is a member.
This is the single most common misconfiguration.
An audience mapper. Tokens have to carry whatever CONFIGHUB_TOKEN_EXCHANGE_AUDIENCE names in
their aud claim. Clients registered through cub oauthclient create get this
automatically; a client made by hand needs it added.
Realm roles for organization roles. A user's organization role is read from their
Keycloak realm roles: admin, manager, editor, user, viewer, creator or none.
Create the ones you use as realm roles and assign them to your users. A
user with none of them gets creator.
Checking a running instance
GET /api/info states what the instance offers, and is the same thing clients read to
decide how to sign in:
curl -s https://confighub.example.com/api/info | jq '{AuthIssuer, TokenExchangeEndpoint, TokenExchangeAudience}'
An empty AuthIssuer means browser-app sign-in is not configured. With both fields set, an
app runs OIDC against the issuer and exchanges the result for a ConfigHub token.
To check the organization claim, decode a user's access token and look at organization.
The map form carries ids and is what ConfigHub needs:
{ "organization": { "acme": { "id": "ec86668c-0287-40ee-9f2b-26f221b9e090" } } }
A bare list ("organization": ["acme"]) is the mapper setting described above.
Building your own apps against the instance
Everything above also makes the instance usable by applications your teams write: internal dashboards, deployment portals, anything that needs a ConfigHub session belonging to the person using it. Those apps register as OAuth clients of your instance and use the same sign-in path as the ConfigHub UI, which is covered in Custom UI Apps.
Getting help
Deployment shapes differ, and identity is where they differ most. If you are setting this up for a production deployment, contact us and we will work through it with you.