fix(web-app-keycloak): bring SAML clients to existing realms too

SAML clients were created only by the realm import, and the import runs only for a missing realm, so a SAML app added to a running Keycloak never got its client: SuiteCRM's SAML hop answered 400 and Keycloak logged client_not_found. The existing-realm reconcile now also loops over the clientId of every SAML client in the rendered realm dictionary, the same source the import uses, and creates or updates it through kcadm like the application-declared OIDC clients.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-16 18:34:58 +02:00
1 parent 1c7f08c5f8
commit 2b4db90576
3 files changed
+4 -3

No files matched your search

@@ -53,11 +53,11 @@
include_tasks: "{{ role_path }}/tasks/update/08_users/email_verified.yml"
when: KEYCLOAK_USER_EMAILS_VERIFIED | bool
- name: "📂 Ensure application-declared clients (existing realms)"
- name: "📂 Ensure application-declared and SAML clients (existing realms)"
include_tasks: "{{ role_path }}/tasks/update/10_declared_client.yml"
vars:
kc_declared_client_id: "{{ declared_client }}"
loop: "{{ KEYCLOAK_DECLARED_CLIENT_IDS }}"
loop: "{{ KEYCLOAK_DECLARED_CLIENT_IDS + KEYCLOAK_SAML_CLIENT_IDS }}"
loop_control:
loop_var: declared_client
@@ -1,6 +1,6 @@
---
# Realm import only runs for a MISSING realm; this kcadm create-or-update brings
# application-declared clients to existing realms too. Desired state is the
# application-declared and SAML clients to existing realms too. Desired state is the
# import's own client object, so the realm template stays the one SPOT.
- name: "✅ Ensure the '{{ kc_declared_client_id }}' client"
vars:
+1
View File
@@ -127,6 +127,7 @@ KEYCLOAK_MOODLE_ENABLED: "{{ 'web-app-moodle' in group_names }}"
KEYCLOAK_DECLARED_CLIENT_APPS: "{{ lookup('applications') | kc_declared_client_apps(lookup('deployment').groups) }}"
KEYCLOAK_DECLARED_CLIENT_IDS: "{{ lookup('applications') | kc_declared_client_ids(lookup('deployment').groups) }}"
KEYCLOAK_SAML_CLIENT_IDS: "{{ KEYCLOAK_DICTIONARY_REALM.clients | selectattr('protocol', 'equalto', 'saml') | map(attribute='clientId') | list }}"
KEYCLOAK_RECAPTCHA_ENABLED: "{{ lookup('config', application_id, 'services.recaptcha.enabled') }}"
KEYCLOAK_RECAPTCHA_KEY: "{{ lookup('config', application_id, 'secrets.credentials.recaptcha.key') }}"