Skip to content

feat(keycloak): modular client import via post-start pipeline#2

Open
michaelstingl wants to merge 1 commit into
mainfrom
feature/modular-keycloak-clients
Open

feat(keycloak): modular client import via post-start pipeline#2
michaelstingl wants to merge 1 commit into
mainfrom
feature/modular-keycloak-clients

Conversation

@michaelstingl
Copy link
Copy Markdown
Owner

Description

Extract OpenCloud clients from the monolithic realm JSON into individual files in config/keycloak/clients/. A numbered post-start pipeline imports them via kcadm.sh partialImport after Keycloak starts.

Adding a new OIDC client = drop a .json + .scopes file in clients/. No realm JSON editing required.

Post-start pipeline:

Step Script What it does
0 00-wait-for-keycloak.sh Wait for Keycloak, authenticate kcadm.sh
1 10-import-clients.sh partialImport each clients/*.json (SKIP existing)
2 11-assign-client-scopes.sh Assign scopes from *.scopes sidecars

Step 2 is a workaround for keycloak#16289 (partialImport ignores defaultClientScopes). Can be removed when Keycloak fixes the issue.

Related Issue

  • Ref coworking-nuernberg/opencloud-deploy#95

Motivation and Context

The two realm .dist.json files (LDAP + autoprovisioning) are ~85-95KB monoliths with ~90% overlap. Client definitions are duplicated between the monolith realm JSON and the unused clients/ directory. This PR activates the existing clients/*.json files and removes the duplicated client definitions from the realm JSONs.

Benefits:

  • Modular: add/remove clients without editing a 3000-line JSON
  • Community-friendly: custom clients (monitoring SSO, internal tooling) via Compose override mount
  • Less duplication: clients maintained once in clients/, not twice in two realm JSONs
  • Debuggable: each pipeline step runs standalone via docker exec

This uses a zero-dependency shell approach (kcadm.sh ships inside the Keycloak container). No extra images or services needed. For larger-scale deployments, keycloak-config-cli is the established alternative.

How Has This Been Tested?

  • test environment: local throwaway Keycloak 26.5.6 containers (Docker for Mac)
  • test case 1: LDAP realm — full realm comparison (clients, client-scopes, roles, groups, realm settings) between original monolith and modular approach → all identical
  • test case 2: Autoprovisioning realm — same comparison → all identical
  • test case 3: Cyberduck client imported as new client from clients/ directory → works
  • validation script: bash config/keycloak/validate-modular-clients.sh downloads the original upstream realm JSONs from GitHub, starts two containers per variant, exports and diffs with jq

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • Code changes
  • Unit tests added
  • Acceptance tests added
  • Documentation added (config/keycloak/README.md)

Extract OpenCloud clients (web, Android, iOS, Desktop) from the
monolithic realm JSON into individual files in config/keycloak/clients/.

A numbered post-start pipeline imports them after Keycloak starts:

  00-wait-for-keycloak.sh  — wait + kcadm.sh authenticate
  10-import-clients.sh     — partialImport each clients/*.json
  11-assign-client-scopes.sh — assign scopes from *.scopes sidecars

Each step is standalone and can be run manually for debugging:

  docker exec keycloak /bin/sh /opt/keycloak/bin/10-import-clients.sh

Adding a new OIDC client = drop a .json + .scopes file in clients/.
No realm JSON editing required.

The .scopes sidecar files are a workaround for keycloak#16289
(partialImport ignores defaultClientScopes from client JSONs).
When Keycloak fixes this, the .scopes files and step 11 can be removed.

Changes:
- docker-entrypoint-override.sh: slim orchestrator, forks pipeline
- 00-wait-for-keycloak.sh: wait + authenticate (reusable)
- 10-import-clients.sh: partialImport with {{OC_URL}} resolution
- 11-assign-client-scopes.sh: reads .scopes sidecars, assigns via kcadm.sh
- clients/*.scopes: defaultClientScopes per client (one line, csv)
- opencloud-realm*.dist.json: removed 4 OC clients + roles.client entries
- ldap-keycloak.yml, external-keycloak.yml: mount pipeline scripts + clients/
- README.md: admin & dev guide (adding clients, custom clients via
  Compose override, debugging)
- validate-modular-clients.sh: jq-based full realm comparison
  (clients, client-scopes, roles, groups, realm settings) against
  original monolith — downloads upstream, starts throwaway containers

Tested: both LDAP and autoprovisioning produce identical realms.

Ref: coworking-nuernberg/opencloud-deploy#95
@michaelstingl michaelstingl force-pushed the feature/modular-keycloak-clients branch from 28c1e91 to c7c53f6 Compare May 18, 2026 06:09
@michaelstingl
Copy link
Copy Markdown
Owner Author

Rebased onto upstream/main (2026-05-18)

Branch is now on current upstream opencloud-eu/opencloud-compose:main (29 commits caught up, 0 conflicts).

Notable upstream changes since the original branch point (2026-04-03):

Re-validation post-rebase

validate-modular-clients.sh against KC 26.5.6 throwaway containers (using rebased branch + current upstream realm JSONs):

  • LDAP realm: 11 clients / 14 scopes / 7 roles / 10 groups / 102 realm settings — all identical, + Cyberduck as new client
  • Autoprovisioning realm: 11 clients / 13 scopes / 7 roles / 9 groups / 104 realm settings — all identical, + Cyberduck as new client

PR ready for upstream submission.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant