Spike: Keycloak programmatic configuration and architecture validation #133
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "132-spike-keycloak-programmatic-configuratio"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Spike investigation into how Keycloak should be programmatically configured for the landscaping-assistant app. Produces
docs/keycloak-setup.mdcovering the full architecture: Terraform IaC approach, realm/client design, ROPC rejection, secrets wiring, and step-by-step setup instructions.Changes
docs/keycloak-setup.md-- New spike document covering:landscapingrealm with 5 roles (admin, lead, member, client, super_admin)landscaping-assistantclient with Authorization Code + PKCETest Plan
pal-e-services/terraform/keycloak.tfandk3s.tfvarsfor current statepal-e-platform/terraform/modules/keycloak/main.tffor infrastructurebasketball-api/services/keycloak.pyfor ROPC analysispal-e-deployments/overlays/landscaping-assistant/prod/deployment-patch.yamlfor secrets patternvariables.tfReview Checklist
Related Notes
sop-keycloak-client-creation-- current SOP (admin-console path, superseded by Terraform)Related
Closes #132
QA Review
Docs-only spike PR (single file:
docs/keycloak-setup.md, 343 additions). Verified claims against source material.Accuracy Checks
pal-e-platform/terraform/modules/keycloak/main.tfpal-e-services/terraform/k3s.tfvarsbasketball-api/services/keycloak.pylines 39-48westside-app/src/lib/keycloak.jssop-keycloak-client-creationkubectl get secretoutputpal-e-services/terraform/variables.tfFindings
No blocking issues found. One minor nit:
No code changes, no runtime impact, no security concerns beyond what the doc already addresses (ROPC rejection, secrets handling).
VERDICT: APPROVE
Clean spike doc with accurate cross-repo investigation. All claims verified against source files. Proposed Terraform config is syntactically valid against existing variable definitions. ROPC rejection is well-reasoned and backed by platform convention, SOP, and OAuth 2.1 deprecation.
PR #133 Review
DOMAIN REVIEW
Tech stack: Documentation (Markdown spike output). Domain review applies Keycloak/OIDC, Terraform/IaC, and Kubernetes infrastructure expertise.
Technical accuracy -- cross-referenced against
sop-keycloak-client-creation(updated 2026-06-06),docs/user-stories-auth.md, and open issues:mrparkers/keycloakv5.0 -- correct per SOP.pal-e-services/terraform/keycloak.tf+k3s.tfvars-- correct per SOP.quay.io/keycloak/keycloak:26.0.7with H2 dev-file DB -- correct per SOP.docs/user-stories-auth.mdexactly. The role table correctly describes each role's purpose and assignment path.kubectl create secret ... --dry-run=client -o yaml | kubectl apply -f -pattern andsecretKeyRefin deployment-patch.yaml -- matches the SOP's Secrets Wiring section.Quality of the spike output: This is an excellent spike document. The comparison table (Terraform vs Admin API vs Realm Export vs Manual) is clear. The ROPC analysis is the strongest section -- it correctly identifies that the basketball-api's
grant_type=passwordusage is admin-to-admin, not a precedent for user-facing ROPC. The step-by-step phases are logically ordered with clear dependencies.BLOCKERS
1. Missing Network Policy step (SOP gap)
The updated SOP has a "Network Policy" section stating:
The spike doc's step-by-step instructions and cross-repo changes table do not mention this. Without adding
landscaping-assistantto the Keycloak NetworkPolicy allowlist, the app will be unable to reach Keycloak at all. This is a deployment-blocking omission.Fix: Add a step between Phase 1 and Phase 2 (or as a sub-step of Phase 1): "Add
landscaping-assistantnamespace to Keycloak ingress NetworkPolicy inpal-e-platform/terraform/network-policies.tfand runtofu apply." Also add a row to the Cross-Repo Changes table forpal-e-platform.2. Follow-up ticket #6 is stale
The spike doc's last follow-up ticket says:
This has already been done. The SOP was rewritten (updated 2026-06-06) and now documents Terraform as "Path A: Primary" with admin console as "Path B: User Management." The SOP title was also changed to "SOP: Keycloak Client & Realm Management." This follow-up ticket is no longer needed and should be removed or marked as already completed to avoid creating duplicate work.
NITS
Missing
stateparameter / CSRF note: The SOP has a dedicated section onstateparameter validation that the consuming app must implement. The spike doc's Phase 5 (Rails Auth Implementation) lists "Implement callback controller, session management" but does not mention OIDCstatevalidation orcode_verifierlifecycle. Consider adding a note: "The callback controller MUST validate the OIDCstateparameter (most OmniAuth strategies handle this automatically)."Missing
front_channel_logout: The SOP's OIDC Client Config Summary listsFront channel logout = ON (Recommended for SLO). The proposed client config ink3s.tfvarsdoes not include this setting. If the Terraform variable definition supports it, it should be included for single-logout support.brute_force_protectednot in realm config: The rationale section saysbrute_force_protected = true (default)but this field does not appear in the proposedkeycloak_realmsHCL block. If it is truly a Keycloak default, this is fine, but the comment could mislead someone into thinking it is being explicitly set. Clarify: either add it to the HCL block or change the rationale to say "Keycloak enables brute force protection by default at the realm level; no explicit setting needed in tfvars."SOP Status section is now inaccurate: The spike doc says the SOP "has since been superseded by the Terraform approach." This undersells it -- the SOP was fully rewritten to document both paths. Consider changing to: "The SOP has been updated to document Terraform as the primary path (Path A) with admin console as Path B for user management."
PR body says "ROPC rejection": The PR Summary mentions "ROPC rejection" which is accurate but slightly misleading in the PR description since the doc actually recommends Authorization Code + PKCE (the rejection is one section of the doc, not its main purpose). Minor wording nit.
README Roles table out of date: The README shows only four roles (no super_admin). This is not a spike doc issue, but a gap surfaced by the review. The README should be updated to include super_admin, either in this PR or as a follow-up.
SOP COMPLIANCE
132-spike-keycloak-programmatic-configuratio-- truncated but follows the{issue-number}-{kebab-case}pattern)Closes #132)<from-keycloak>, no actual credentials)docs/keycloak-setup.md, directly addresses the spike)PROCESS OBSERVATIONS
VERDICT: NOT APPROVED
Two blockers must be addressed before merge:
pal-e-platform/terraform/network-policies.tfto both the step-by-step instructions and the Cross-Repo Changes table.PR #133 Review (Re-Review)
PREVIOUS BLOCKERS -- RESOLVED
1. NetworkPolicy step was missing -- FIXED
network-policies.tf, add namespace, runtofu apply.pal-e-platformrow added to the cross-repo changes table.pal-e-platform.sop-keycloak-client-creation), which documents this exact requirement under its "Network Policy" section. The spike doc's coverage now matches.2. Stale SOP follow-up -- FIXED
DOMAIN REVIEW
Tech stack: Documentation / Terraform / Kubernetes / Keycloak OIDC
Document accuracy checks performed:
k3s.tfvars(correct keys:display_name,registration_allowed,reset_password_allowed,login_with_email_allowed,roles).pal-enterprisesconfidential client pattern (correct keys:realm_key,client_id,public_client,standard_flow_enabled,direct_access_grants_enabled,pkce_code_challenge_method, etc.).docs/user-stories-auth.mdrole definitions.kubectl create secret generic ... --dry-run=client -o yaml | kubectl apply -f -pattern documented in the SOP.BLOCKERS
None.
NITS
app-architecture.mdline 20 still says"HTTP POST (ROPC auth)"in its Mermaid diagram for the Rails-to-Keycloak connection. This contradicts the spike doc's ROPC rejection. Not introduced by this PR and not in scope, but worth a follow-up to update that diagram label to reflect Authorization Code + PKCE. Consider adding this to ticket #115 or creating a separate doc-fix ticket.brute_force_protected = truelisted as "(default)" in the realm settings rationale, but it does not appear in the proposedk3s.tfvarsblock. If this is truly a Keycloak realm default, that is fine -- but it may be worth a comment in the tfvars block when the actual Terraform change is made to confirm the assumption.Test user table:
lucas-super-adminhas roles "admin + super_admin" butuser-stories-auth.mddescribes super_admin as "separate from business roles" with Lucas having "both admin and super_admin." The table is consistent with the auth doc, but the fact that one test user holds two roles while others hold one could be called out more explicitly (e.g., "compound role assignment for testing cross-role behavior").SOP COMPLIANCE
132-spike-keycloak-programmatic-configuratio-- truncated but clearly issue-132-derived)Closes #132)<from-keycloak>)docs/keycloak-setup.md, 354 additions, 0 deletions)PROCESS OBSERVATIONS
VERDICT: APPROVED
PR #133 Review (Re-review Pass 3)
NIT FIX VERIFICATION
All three nits from the previous review have been properly resolved:
1. ROPC references in app-architecture.md -- FIXED
All 6 ROPC references have been replaced with Authorization Code + PKCE flow:
"HTTP POST (ROPC auth)"replaced with"OIDC (Auth Code + PKCE)"in Mermaid diagramsessions_controller.rbcomment updated from "Login/logout via Keycloak ROPC" to "OmniAuth callback + logout"keycloak_auth_service.rbreplaced with comment "Auth handled by omniauth_openid_connect gem (no custom service needed)"Verdict: Thoroughly fixed. No stale ROPC references remain in either file.
2. brute_force_protected assumption -- FIXED
Lines 85-86 of
keycloak-setup.mdnow contain a verification note:This is followed by a separate bullet confirming the security baseline. The implementer is now prompted to verify the default rather than blindly trusting it.
Verdict: Fixed. The actionable verification clause is present.
3. Compound role assignment for lucas-super-admin -- FIXED
The test users table (line 141) now has an expanded description for
lucas-super-admin:Lines 147-148 add explicit instructions:
Verdict: Fixed. The dual-role assignment is now unambiguous with step-by-step Keycloak console instructions and the Rails-side array semantics explained.
DOMAIN REVIEW
Tech stack: Documentation (Markdown with Mermaid diagrams, HCL code blocks, YAML snippets, Bash commands). Domain expertise applied: Keycloak/OIDC, Terraform/IaC, Kubernetes secrets management, Rails auth patterns.
keycloak-setup.md (new, 357 lines):
k3s.tfvarspatterns described.kubectl create secret generic ... --dry-run=client -o yaml | kubectl apply -f -pattern for secret updates.westside-applegacydirect_access_grants_enabled = trueobservation is accurate and properly contextualized.app-architecture.md (6 changed regions):
keycloak_auth_service.rband updatessessions_controller.rbdescription.BLOCKERS
None. This is a documentation-only PR (spike output + architecture doc updates). No code, no credentials, no security concerns.
NITS
README not updated:
docs/keycloak-setup.mdis not listed in the README Documentation table. Every other doc indocs/has a row there. Suggested row:| [Keycloak Setup](docs/keycloak-setup.md) | Keycloak realm/client configuration, auth flow, secrets wiring |Redundant brute_force_protected bullets (keycloak-setup.md lines 85-86): There are two consecutive bullets about
brute_force_protected-- the first is the verification note (good), the second just restates "brute_force_protected = true(default): security baseline." These could be consolidated into a single bullet that combines both the default assumption and the verification instruction.Client settings rationale parenthetical (keycloak-setup.md line 129): "The user logs in via the Keycloak login page (or a Rails-rendered login page that posts to Keycloak)" -- the parenthetical about a Rails-rendered login page posting to Keycloak is a bit misleading for a standard flow description. In Auth Code flow, a Rails page would initiate a redirect to Keycloak, not POST to it. Minor wording nit.
SOP COMPLIANCE
132-spike-keycloak-programmatic-configuratioreferences issue #132 (truncated but valid)Closes #132present.envfiles in the diff. URLs are Tailscale internal hostnames, not secret.docs/keycloak-setup.md(new) anddocs/app-architecture.md(ROPC cleanup) -- both directly relevant to the spikePROCESS OBSERVATIONS
VERDICT: APPROVED