Keycloak: paldocs (OIDC Client, Themes, Secrets Wiring)
Keycloak: paldocs
OIDC authentication for paldocs, following the established pattern from landscaping-assistant (the reference implementation for Rails + Keycloak on this platform).
Diagram
Components
| Component | Purpose | Notes |
|---|---|---|
| Keycloak Server | Identity provider (OIDC) | quay.io/keycloak/keycloak:26.0.7, <code>keycloak</code> namespace, URL: <code>https://keycloak.tail5b443a.ts.net</code>. H2 dev-file DB with 2Gi PVC. |
| Keycloak Realm (TBD) | Auth boundary for paldocs users | Each project gets its own realm (platform convention). Managed via <code>keycloak_realms</code> map in <code>pal-e-services/terraform/k3s.tfvars</code>. |
| OIDC Client (TBD) | paldocs client registration in Keycloak | Confidential, Auth Code + PKCE (S256), realm roles mapper enabled, refresh tokens enabled. Managed via <code>keycloak_clients</code> map in <code>k3s.tfvars</code>, applied by <code>keycloak.tf</code> for_each. |
| Login Theme | CSS-only branded login page | Three-repo pattern: source in paldocs repo (<code>keycloak-theme/</code>), ConfigMap in pal-e-platform (<code>modules/keycloak/main.tf</code>), <code>login_theme</code> on realm in pal-e-services. Extends base <code>keycloak</code> parent theme. |
| OmniAuth (Rails) | Server-side OIDC integration | <code>omniauth-openid-connect</code> gem. Callback: <code>/auth/keycloak/callback</code>. Session stores user info from ID token. |
| NetworkPolicy | Network access from paldocs to keycloak namespace | Keycloak has default-deny ingress. Currently allows: tailscale, westside-ai-assistant, pal-enterprises, landscaping-assistant, monitoring. Paldocs must be added. |
Key Decisions
- Confidential client, not public: Rails is server-rendered. The client secret stays on the server and never reaches the browser. Matches pal-enterprises and landscaping-assistant patterns.
- Auth Code + PKCE, not ROPC: Direct Access Grants (ROPC) is prohibited by OAuth 2.1 (RFC 9700). Landscaping-assistant tried ROPC and immediately reverted (PR #158). PKCE S256 is defense-in-depth even for confidential clients.
- Refresh tokens enabled: Required for long-lived mobile sessions (turbo-ios). Users expect to stay logged in on their phones.
- Realm roles in tokens:
include_realm_roles_mapper = trueputs roles atrealm_access.rolesin the ID/access token. Rails reads roles directly from the token without an extra Keycloak API call. - CSS-only theme: No custom FreeMarker templates, no Docker image rebuild. CSS overrides PatternFly defaults. Design tokens mirror the paldocs
application.css. - Terraform-managed, not manual: Adding a client is a
k3s.tfvarsentry +tofu apply. The old SOP for manual admin console creation is superseded.
Related
arch-infra-- Public domain affects Keycloak redirect URIs (paldocs.app/auth/keycloak/callback)arch-k8s-- Deployment overlay needs KEYCLOAK_* env vars from k8s secretarch-rails-db-- Shared database where user/session data livesstory-paldocs-ios-testflight-- Auth is on the critical path for iOS- paldocs #26 -- Keycloak TF client creation
- paldocs #82 -- Login theme
- pal-e-deployments #224 -- KEYCLOAK env vars in deployment overlay
- pal-e-platform #303 -- Keycloak client tracking (platform-level)
arch-keycloak-- pal-enterprises Keycloak note (same platform pattern, different project)landscaping-assistant/docs/keycloak-setup.md-- Reference: complete walkthrough of the same patternlandscaping-assistant/docs/auth-strategy.md-- Reference: Auth Code + PKCE rationalepal-e-services/terraform/keycloak.tf-- OIDC client resources (for_each on keycloak_clients)