Keycloak quickstart
This page runs EdgeQuake and the shipped Keycloak on one machine. The realm is edgequake, and the image is Keycloak 26.8.0 or later. The last section lists the settings for a production HTTPS setup.
Topology
Section titled “Topology”The browser and the API must reach Keycloak under the same host name, because EdgeQuake compares the issuer string exactly.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#E0E7FF","primaryBorderColor":"#6366F1","primaryTextColor":"#1E1B4B","secondaryColor":"#D1FAE5","secondaryBorderColor":"#10B981","secondaryTextColor":"#064E3B","tertiaryColor":"#FEF3C7","tertiaryBorderColor":"#F59E0B","tertiaryTextColor":"#6B7A90","lineColor":"#7A889C","clusterBkg":"rgba(99,102,241,0.07)","clusterBorder":"#A5B4FC","noteBkgColor":"#FEF9C3","noteTextColor":"#422006","textColor":"#6B7A90","titleColor":"#6B7A90","signalColor":"#7A889C","signalTextColor":"#6B7A90","loopTextColor":"#6B7A90","edgeLabelBackground":"#F1F5F9","actorLineColor":"#94A3B8"}}}%%
%% eq-theme:v1
flowchart LR
B["Browser"] -- "http://keycloak:8081 via hosts file" --> K["Keycloak realm edgequake"]
B -- "http://localhost:8080" --> A["EdgeQuake API"]
A -- "Issuer discovery and token exchange" --> K
K -- "Back-channel logout via EQ_API_INTERNAL_URL" --> A
K --> KD[("Keycloak database")]
A --> P[("PostgreSQL with pgvector and AGE")]
%% eq-classes
classDef eqActor fill:#FCE7F3,stroke:#EC4899,color:#500724
classDef eqStore fill:#D1FAE5,stroke:#10B981,color:#064E3B
class B eqActor
class KD,P eqStore
Read it as two paths. The browser reaches Keycloak through the host name keycloak. Keycloak calls back into the API at its internal URL when a session ends.
1. One-time host setup
Section titled “1. One-time host setup”The browser must resolve the same issuer host as the API container. Add this line once:
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts2. Start the stack and run the smoke test
Section titled “2. Start the stack and run the smoke test”make dev-sso # docker-compose.quickstart.yml + docker-compose.keycloak.ymlmake keycloak-smoke # headless auth-code + PKCE login, org claim, roles, API handoffWith no overrides, make keycloak-smoke targets the isolated proof stack (Keycloak on port 18081, API on 18080, web UI on 13010). For the dev-sso stack, set the endpoints explicitly:
EQ_SSO_KC=http://keycloak:8081 \EQ_SSO_API=http://localhost:8080 \EQ_SSO_REDIRECT=http://localhost:8080/api/v1/auth/oidc/callback \EQ_WEB_PUBLIC_URL=http://localhost:3000 \make keycloak-smokeTo also prove back-channel logout, run KEYCLOAK_SMOKE_DEEP=1 make keycloak-smoke. Keycloak must be able to reach the API at EQ_API_INTERNAL_URL.
| Service | URL | Credentials (dev defaults) |
|---|---|---|
| Keycloak admin | http://keycloak:8081 | admin / KC_ADMIN_PASSWORD (default admin_dev_only) |
| EdgeQuake API | http://localhost:8080 | Bootstrap admin: username admin unless EDGEQUAKE_BOOTSTRAP_ADMIN_USERNAME is set; password EDGEQUAKE_BOOTSTRAP_ADMIN_PASSWORD (default Admin-dev-only-change-me-1) |
| Demo users | alice, bob, carol |
EQ_KC_DEMO_PASSWORD (default demo-password-change-me) |
Every default in the overlay is for development only. Set your own values before you share the stack.
3. Create the tenants
Section titled “3. Create the tenants”Organization aliases in Keycloak must match tenant slugs in EdgeQuake. EdgeQuake never creates a tenant from a claim, so an unknown organization is denied with org_unknown. Create the tenants with the bootstrap admin first:
TOKEN=$(curl -s http://localhost:8080/api/v1/auth/login \ -H 'content-type: application/json' \ -d '{"username":"'"${EDGEQUAKE_BOOTSTRAP_ADMIN_USERNAME:-admin}"'","password":"'"$EDGEQUAKE_BOOTSTRAP_ADMIN_PASSWORD"'"}' \ | jq -r .access_token)
for org in acme globex; do curl -s http://localhost:8080/api/v1/tenants \ -H "authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ -d "{\"name\":\"$org\",\"slug\":\"$org\"}"done4. Sign in
Section titled “4. Sign in”Open the web UI login page and choose Continue with Single sign-on. Type the organization alias, for example acme, if the login page asks for one. A user who belongs to several organizations and gives no hint gets a picker (org_ambiguous).
The realm has three demo users:
| User | Organizations | Realm roles |
|---|---|---|
alice |
acme | eq-user, eq-admin |
bob |
globex | eq-user |
carol |
acme and globex | eq-user |
What the realm contains
Section titled “What the realm contains”- Organizations
acmeandglobex, and the confidential clientedgequake-webwith PKCES256. - Realm roles
eq-user,eq-adminandeq-owner. Map them withEDGEQUAKE_OIDC_ROLE_MAP. - Back-channel logout to
${EQ_API_INTERNAL_URL}/api/v1/auth/oidc/backchannel-logout. - Brute-force protection on.
- Realm import is create-only. An existing realm is skipped, so edit a live realm in the Admin console (or with
kcadm) instead of changing the JSON.
Production configuration
Section titled “Production configuration”Use this section when Keycloak and EdgeQuake have public HTTPS names. The laptop overlay (EQ_KC_SSL_REQUIRED=none and the demo users) must not ship.
- Run Keycloak 26.8.0 or later with
KC_HOSTNAMEset to the public host. SetsslRequired=externalon the realm, orEQ_KC_SSL_REQUIRED=externalon the overlay. - Create EdgeQuake tenants whose slugs match the Keycloak organization aliases before anyone signs in.
- Point the API at the issuer exactly as Keycloak reports it in discovery:
EDGEQUAKE_AUTH_ENABLED=trueEDGEQUAKE_STRICT_TENANT_BIND=trueEDGEQUAKE_OIDC_ENABLED=trueEDGEQUAKE_OIDC_KIND=keycloakEDGEQUAKE_OIDC_SLUG=keycloakEDGEQUAKE_OIDC_DISPLAY_NAME="Sign in with SSO"EDGEQUAKE_OIDC_ISSUER_URL=https://sso.example.com/realms/edgequakeEDGEQUAKE_OIDC_CLIENT_ID=edgequake-webEDGEQUAKE_OIDC_CLIENT_SECRET=<from secret manager>EDGEQUAKE_OIDC_REDIRECT_URI=https://app.example.com/api/v1/auth/oidc/callbackEDGEQUAKE_OIDC_SUCCESS_REDIRECT_URL=https://app.example.com/auth/callbackEDGEQUAKE_OIDC_ROLE_MAP='{"eq-owner":"owner","eq-admin":"admin","eq-user":"member"}'EDGEQUAKE_OIDC_MAX_ROLE=owner # only if the IdP may grant ownersThe default EDGEQUAKE_OIDC_MAX_ROLE is admin. An eq-owner user is capped at admin unless you set EDGEQUAKE_OIDC_MAX_ROLE=owner deliberately. The role rules are in Tenants, organizations and roles.
Allow Keycloak to reach POST /api/v1/auth/oidc/backchannel-logout through the ingress or a NetworkPolicy.
For the Kubernetes overlay, see values-sso-keycloak.yaml.example. For every variable, see env-reference.md. For the startup gates and the full checklist, see production-hardening.md.