Skip to content

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.