Skip to content

Identity provider quickstart: Keycloak ​

The Live Host has one login: your OIDC identity provider (ADR 0007). This page gets you a working one in minutes: Keycloak, shipped as a compose profile with a realm that already carries everything the Live Host needs. Evaluate with it as is; for production keep the realm and run Keycloak the way you run Keycloak (real database, TLS, start instead of start-dev).

Evaluating without any login at all? That is LIVE_AUTH=none (operating modes), no IdP involved.

What the realm carries ​

deploy/keycloak/realm-designiq.json, imported on Keycloak's first start:

ItemWhat / why
realm designiqone realm for the platform; self-registration off
client designiq-webthe browser login: public client, PKCE S256, redirect http://localhost:8301/auth/oidc/callback
client designiq-mcpMCP clients: public client, PKCE S256, redirect http://localhost:8765/callback (Claude Code: --client-id designiq-mcp --callback-port 8765)
two protocol mappers on each clientaudience: a fixed aud: designiq (Keycloak does not honor RFC 8707 resource indicators, so the Live Host is told the fixed value: LIVE_OIDC_AUDIENCE=designiq); github_login: the user attribute as a claim in the access token (the login claim, LIVE_OIDC_LOGIN_CLAIM's default)
user profile attribute github_logindeclared, editable by admins only: the claim decides which repositories a person may write, so the person must never be able to set it (the Live Host refuses tokens without it, and never falls back to preferred_username)
identity provider github (disabled)the production path: GitHub as the login method behind Keycloak, with a mapper that copies the verified GitHub login into github_login at every sign-in (sync mode FORCE). Enable it with your own GitHub OAuth App, see below
users petra/petra, nobody/nobodydemo users: petra carries github_login = petra; nobody carries no claim and is refused fail-closed. Try both

1. Start Keycloak ​

bash
cd deploy
docker compose --profile keycloak up -d keycloak

Admin console: http://localhost:8080 (admin / admin, override with KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORD). The realm is imported on the first start; the container keeps no state, so a down -v gives you a fresh realm. A quickstart set up before 5.0 has other realm, client and audience names: see upgrading-to-5.md.

2. Point the Live Host at it ​

oidc mode needs the IdP and a GitHub App: per-repo authorization runs on the App's installation token, an identity alone can write nothing. Register the App first (github-app-setup.md); to try the login without one, run the stub provider the E2E uses (see "Verified" below).

From source, host on your machine, Keycloak in Docker:

bash
LIVE_OIDC_ISSUER=http://localhost:8080/realms/designiq \
LIVE_OIDC_JWKS_URL=http://localhost:8080/realms/designiq/protocol/openid-connect/certs \
LIVE_OIDC_AUDIENCE=designiq \
LIVE_OIDC_CLIENT_ID=designiq-web \
LIVE_OIDC_LOGIN_LABEL=Keycloak \
pnpm live-host            # plus GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY_FILE / GITHUB_APP_SLUG

Both in containers: write deploy/.env first: LIVE_PUBLIC_URL=http://localhost:8301 (the realm's redirect URI is exactly that; the template's https://design.example.com placeholder would be refused by Keycloak), the GitHub App values, and the OIDC block from .env.example uncommented as is. Then run docker compose --profile keycloak up -d. The block differs from the source-run values in one respect: the Live Host reaches Keycloak over the compose network, so JWKS and the token endpoint go via http://keycloak:8080 (LIVE_OIDC_JWKS_URL, LIVE_OIDC_TOKEN_URL), while the issuer and the authorize URL (LIVE_OIDC_AUTHORIZE_URL) stay what the browser sees. KC_HOSTNAME is pinned in the compose file so a token minted over the container network carries the same issuer.

3. Sign in ​

Open http://localhost:8301 → Sign in with Keycloak → petra / petra. The overview lists the repositories petra may write, as decided by your GitHub App, so for real repositories set github_login on your own Keycloak user to your real GitHub login (admin console → Users → the user → Attributes; only admins can).

Try nobody / nobody: Keycloak signs them in, the Live Host refuses the callback with auth/missing-claim: a person without a verified git-provider login gets no session.

4. Connect an MCP client ​

bash
claude mcp add --transport http designiq http://localhost:8301/mcp \
  --client-id designiq-mcp --callback-port 8765

The client hits /mcp → 401 + WWW-Authenticate → reads the protected-resource metadata → discovers Keycloak → runs the code+PKCE flow on the pre-registered designiq-mcp client → presents an access token with aud: designiq and github_login. Other clients and their redirect URIs: extending/mcp-idp-setup.md. Register each client's callback on designiq-mcp (or one client per surface).

5. Your real users: GitHub as the login behind Keycloak ​

The demo users are local accounts with an admin-set attribute. For real people, make GitHub the login method so the claim is populated by GitHub itself:

  1. Create a GitHub OAuth App (org → Settings → Developer settings → OAuth Apps) with the authorization callback http://localhost:8080/realms/designiq/broker/github/endpoint (Keycloak's broker endpoint; your Keycloak URL in production).
  2. Admin console → Identity providers → GitHub: paste client id + secret, enable.
  3. The mapper github_login from the GitHub profile is already there: at every sign-in it writes the verified GitHub login into the attribute (sync mode FORCE: a renamed GitHub account updates on the next login).
  4. Optionally remove the username/password form from the browser flow (Authentication → browser) so GitHub is the only way in, the identity contract the Live Host assumes (ADR 0005).

This path is configuration; the automated verification below covers the local users (it has no GitHub credentials).

6. Beyond localhost ​

  • Realm: Clients → designiq-web → redirect URI and web origin = your LIVE_PUBLIC_URL; designiq-mcp → the callbacks of the MCP clients you allow.
  • Compose: KEYCLOAK_PUBLIC_URL (the issuer origin every token carries) and KEYCLOAK_ADMIN_PASSWORD in .env; LIVE_OIDC_ISSUER / LIVE_OIDC_AUTHORIZE_URL follow it.
  • Keycloak itself: a database, TLS and start, as in Keycloak's own production guide. The realm export is yours to keep; nothing in it is specific to dev mode.

Verified ​

apps/live-host/test/keycloak-e2e.sh (needs Docker; not part of pnpm test) boots this exact compose profile, a GitHub stub and a fresh Live Host in oidc mode, then proves: the prerequisite check, the browser login through Keycloak's form, an MCP bearer from the designiq-mcp code+PKCE flow at /api/me and /mcp, a release on that identity (PR at the stub, the human as git author), and fail-closed refusal of a token without github_login at /api/me, /mcp and the browser callback.

Other identity providers ​

Any OIDC provider that issues audience-bound JWTs with the login claim works. The Live Host side is pure configuration. The requirements table and the verified WorkOS AuthKit recipe: extending/mcp-idp-setup.md; the seam for anything beyond OIDC: extending/sso.md.