feat(compose): add Keycloak auth mode to dev-compose stack#1821
Conversation
|
Warning Review limit reached
Next review available in: 8 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughThe compose development stack now supports ChangesCompose authentication modes
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Frontend
participant Authenticator
participant Keycloak
participant Redis
participant Gateway
Frontend->>Authenticator: request authentication
Authenticator->>Keycloak: authorize with OIDC
Keycloak-->>Authenticator: authorization code
Authenticator->>Redis: store authenticated session
Gateway->>Authenticator: exchange session cookie
Authenticator-->>Gateway: short-lived gateway JWT
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
deploy/compose/keycloak/gen-realm.py (1)
211-213: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSpecify UTF-8 encoding when writing the JSON file.
Path.write_text()relies on the system default encoding (which can be CP1252 on Windows) if not explicitly specified. Specifyingencoding="utf-8"ensures the JSON artifact is reliably written as UTF-8, preventing decoding errors in Keycloak if the seeded roster contains Unicode characters.♻️ Proposed fix
out_path = Path(args.out) out_path.parent.mkdir(parents=True, exist_ok=True) - out_path.write_text(json.dumps(realm, indent=2, sort_keys=True) + "\n") + out_path.write_text(json.dumps(realm, indent=2, sort_keys=True) + "\n", encoding="utf-8")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@deploy/compose/keycloak/gen-realm.py` around lines 211 - 213, Update the Path.write_text call in the output-writing flow to explicitly use UTF-8 encoding, ensuring the generated realm JSON is consistently encoded across platforms.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@deploy/compose/keycloak/README.md`:
- Around line 3-5: Update the README’s BFF description and authentication
diagram to show the browser redirecting to Keycloak’s authorization endpoint and
then returning to the authenticator callback. Clarify that the browser does not
receive or manage IdP tokens, while the authenticator retains them server-side,
and remove the claim that the browser never communicates with the IdP.
---
Nitpick comments:
In `@deploy/compose/keycloak/gen-realm.py`:
- Around line 211-213: Update the Path.write_text call in the output-writing
flow to explicitly use UTF-8 encoding, ensuring the generated realm JSON is
consistently encoded across platforms.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 261cc9b9-4a11-42eb-8ea8-25049dc14ae5
📒 Files selected for processing (11)
.cf-studio/config/AGENTS.md.env.compose.example.gitignoreCONTRIBUTING.mddeploy/compose/insight-init.shdeploy/compose/keycloak/README.mddeploy/compose/keycloak/gen-realm.pydev-compose.shdocker-compose.ymldocs/components/deployment/specs/DESIGN.mdsrc/backend/services/fakeidp/README.md
7dbbe7c to
4663427
Compare
7c93e10 to
558f3df
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/backend/services/api-gateway/config/keycloak.yaml`:
- Around line 14-20: Remove the obsolete /etc/hosts instruction that adds
127.0.0.1 keycloak from deploy/compose/insight-init.sh, while preserving the
split-horizon issuer_url and jwks_url configuration documented in
src/backend/services/api-gateway/config/keycloak.yaml. Also update the
corresponding stale guidance in .env.compose.example; both sites require direct
changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9ecca940-ce25-4f0b-bd2a-a8da48229bbd
📒 Files selected for processing (12)
.cf-studio/config/AGENTS.md.env.compose.example.gitignoreCONTRIBUTING.mddeploy/compose/insight-init.shdeploy/compose/keycloak/README.mddeploy/compose/keycloak/gen-realm.pydev-compose.shdocker-compose.ymldocs/components/deployment/specs/DESIGN.mdsrc/backend/services/api-gateway/config/keycloak.yamlsrc/backend/services/fakeidp/README.md
🚧 Files skipped from review as they are similar to previous changes (7)
- .gitignore
- .cf-studio/config/AGENTS.md
- src/backend/services/fakeidp/README.md
- docs/components/deployment/specs/DESIGN.md
- CONTRIBUTING.md
- docker-compose.yml
- dev-compose.sh
| # Split-horizon (no /etc/hosts needed): | ||
| # - issuer_url = the PUBLIC browser-facing issuer (localhost:8085). It must | ||
| # equal the token `iss`, which the browser SPA obtains over the published | ||
| # port. | ||
| # - jwks_url = fetched by the gateway over the compose network at | ||
| # keycloak:8085 (Keycloak runs with KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true so | ||
| # it serves the certs endpoint on the internal host). |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify if insight-init.sh still contains the obsolete /etc/hosts check
cat -n deploy/compose/insight-init.sh | grep -A 10 -B 2 'keycloak mode needs a hosts alias'
# Check if README.md still mentions /etc/hosts
rg -n '/etc/hosts' deploy/compose/keycloak/README.md || trueRepository: constructorfabric/insight
Length of output: 804
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== keycloak.yaml ==\n'
cat -n src/backend/services/api-gateway/config/keycloak.yaml | sed -n '1,80p'
printf '\n== .env.compose.example ==\n'
cat -n .env.compose.example | sed -n '40,70p'
printf '\n== README mentions /etc/hosts ==\n'
rg -n '/etc/hosts|keycloak mode needs a hosts alias|127\.0\.0\.1\s+keycloak' deploy/compose/keycloak/README.md deploy/compose/insight-init.sh .env.compose.example src/backend/services/api-gateway/config/keycloak.yaml || trueRepository: constructorfabric/insight
Length of output: 5543
Remove the stale /etc/hosts hint deploy/compose/insight-init.sh:447-451 still tells users to add 127.0.0.1 keycloak, but this split-horizon config already uses localhost:8085 externally and keycloak:8085 internally.
📍 Affects 2 files
src/backend/services/api-gateway/config/keycloak.yaml#L14-L20(this comment).env.compose.example#L51-L51
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/backend/services/api-gateway/config/keycloak.yaml` around lines 14 - 20,
Remove the obsolete /etc/hosts instruction that adds 127.0.0.1 keycloak from
deploy/compose/insight-init.sh, while preserving the split-horizon issuer_url
and jwks_url configuration documented in
src/backend/services/api-gateway/config/keycloak.yaml. Also update the
corresponding stale guidance in .env.compose.example; both sites require direct
changes.
5046a35 to
a17f5ed
Compare
… enforce)
Add an AUTH_MODE (fakeidp default | keycloak) to the docker-compose dev stack:
- fakeidp = auth BYPASS (no login; api-gateway runs no-auth.yaml; the frontend
supplies an impersonated identity). Default; unchanged behavior.
- keycloak = auth ENFORCED (real login form + the api-gateway validates the
Keycloak JWT on every request).
Configured via AUTH_MODE in .env.compose (like FRONTEND_MODE); a per-run --auth
flag overrides it. No /etc/hosts change is needed.
How keycloak mode works:
- Keycloak: single container, embedded H2, --import-realm; realm generated from
the seed roster (deploy/seed/profiles.py) so login personas match the seeded
org. KC_HOSTNAME=http://localhost:8085/kc (+ KC_HOSTNAME_BACKCHANNEL_DYNAMIC)
so the public issuer is localhost — reachable by the host browser via the
published port, no hosts entry.
- Frontend: does the OIDC code+PKCE flow directly (SPA), public client `insight`;
the ghcr image entrypoint injects window.__OIDC_CONFIG__ from OIDC_* env, so
keycloak mode auto-switches FRONTEND_MODE to ghcr (Vite dev / plain-nginx built
can't inject it).
- api-gateway: switched to a fully-resolved keycloak.yaml (auth_disabled:false)
that validates iss=localhost:8085 but fetches JWKS over the compose network at
keycloak:8085 (the orbstack-style issuer/JWKS split). A static file because
compose mounts configs without Helm envsubst and gears doesn't interpolate
${ENV}; hyphenated APP__gears__ env keys are invalid.
- Custom claims tenant_id/org_unit/groups/roles/aud on both clients.
Files: deploy/compose/keycloak/gen-realm.py (seeder-driven realm; --dev-email;
CEO->executive; localhost:3000 redirect), src/backend/services/api-gateway/
config/keycloak.yaml (new, auth-enforcing), docker-compose.yml (keycloak service
+ auth-fakeidp/auth-keycloak profiles + fakeidp depends_on required:false),
dev-compose.sh (--auth/AUTH_MODE mirroring FRONTEND_MODE; per-run realm gen;
frontend-scoped impersonation-off + gateway keycloak.yaml via the generated
override; auto-switch to ghcr; stop the other IdP on an in-place switch),
deploy/compose/insight-init.sh (first-run wizard prompts for auth mode), docs
(keycloak README, CONTRIBUTING, DESIGN, .env.compose.example, fakeidp README).
Verified live, both modes with seeding (25 persons + silver/gold):
- fakeidp: no-auth gateway; no JWT required; caller resolves via the FE header.
- keycloak: JWKS loaded; no/invalid token -> 401; valid Keycloak token -> 200 +
caller resolves; FE serves the localhost-issuer oidc-config; clean mode switch.
Signed-off-by: Konstantin Tursunov <Konstantin.Tursunov@constructor.tech>
a17f5ed to
509e0ff
Compare
What
Adds an
AUTH_MODE(fakeidpdefault |keycloak) to the docker-compose dev stack so local runs can use a real Keycloak login — a login form, real tokens, and custom claims — instead of thefakeidpbypass. Default (fakeidp) behavior is unchanged.Why
fakeidpis great for fast, no-login dev, but it can't exercise the real OIDC login form, real token claims, or admin-console behavior. Keycloak mode gives a customer-IdP-like experience locally while keeping the stack lightweight (a single container, embedded H2,--import-realm).How
deploy/compose/keycloak/gen-realm.py— generates the Keycloakinsightrealm from the seed roster (deploy/seed/profiles.py) so login personas match the seeded org: 25 users, clientsinsight+insight-authenticator, custom-claim mappers (tenant_id/org_unit/groups/roles/aud), CEOteam=None → "executive". A--dev-emailarg decouples the roster anchor fromVITE_DEV_USER_EMAIL.docker-compose.yml—keycloakservice behind profileauth-keycloak;fakeidpmoved behindauth-fakeidp;authenticator.depends_on.fakeidpgainsrequired: false(so--auth=keycloakdoesn't fail on the inactive profile);KC_HOSTNAMEincludes/kcso the advertised issuer ishttp://keycloak:8085/kc/realms/insight.dev-compose.sh—--auth=fakeidp|keycloakflag +AUTH_MODE(mirrorsFRONTEND_MODE). Keycloak branch generates the realm beforeup, repoints the authenticator OIDC env, warns on a missing/etc/hostsentry, stops the other mode's IdP on an in-place switch, and disables frontend dev-impersonation via a per-service override (scoped so the seed step and roster keepVITE_DEV_USER_EMAIL).deploy/compose/insight-init.sh— first-run wizard prompts for the auth mode.deploy/compose/keycloak/README.md, a distinct CONTRIBUTING "Local dev auth backend" section (the api-gateway real-OIDC section is left untouched), a DESIGNAUTH_MODErow, a fakeidp README pointer, and a.env.compose.examplestanza.One-time setup for keycloak mode
Add to
/etc/hosts(browser + containers must resolve Keycloak at the same host):Then:
./dev-compose.sh up --auth=keycloak— log in as any seeded persona (email_*@company.nonpresent/insight-dev); admin consolehttp://keycloak:8085/kc/admin/(admin/admin).Test plan (verified locally, both modes, with seeding)
main's git-silver models.http://keycloak:8085/kc/realms/insight, direct-grant token carriestenant_id/org_unit/groups/roles/aud=insight, dev lead logs in as a real Keycloak user, caller resolves (HTTP 200).fakeidp ⇄ keycloakviaup): only the active IdP stays running.docker compose configvalid for bothauth-fakeidpandauth-keycloak.Notes / scope
no-auth.yaml(auth_disabled) in both modes by design — keycloak mode delivers real login + real tokens/claims at the authenticator; making the gateway enforce the token is a separate change (the production OIDC swap).main(incl. thebootstrap-dband git-silver changes); no conflicts, no script changes required.Summary by CodeRabbit
--auth=fakeidp|keycloak), with Fake IdP as the default.AUTH_MODE/--authbehavior, Keycloak setup, and the Keycloak token-claims/custom-claims contract.