Summary
Identity and credential reconciliation is largely additive. Group membership sync only adds users, client scopes and protocol mappers are added or updated but never removed when dropped from spec, and NebariApp deletion leaves groups, memberships, realm client scopes, and the generated OIDC Secret behind. A hash optimization can also skip provisioning without confirming that Keycloak still matches the desired state.
Affected code
internal/controller/reconcilers/auth/providers/keycloak.go:
- Group membership sync is additive-only (L1020-1073); omitted users are not removed.
syncClientScopes (L749-824) creates and assigns scopes but never unassigns or deletes stale ones.
syncClientProtocolMappers (L833-910) creates/updates mappers by name but never removes ones dropped from spec.
storeClientSecret (L696-745) builds the OIDC Secret with no owner reference, so it is not garbage-collected and is never deleted.
internal/controller/reconcilers/auth/reconciler.go:
CleanupAuth (L265-300) calls CleanupTokenExchange (only when token exchange was enabled, L279) and DeleteClient. It does not remove groups, memberships, realm scopes, or the OIDC Secret.
- The hash skip (L175-211, condition at L179) skips provisioning when
AuthReady=True and the stored AuthConfigHash is unchanged, without reading Keycloak back, so external drift persists until a spec change or the force-reprovision annotation.
Impact
Access can persist after desired membership is removed, realm resources accumulate and can collide with later applications, OIDC client credentials remain in the cluster after app deletion, and security-relevant Keycloak drift is not necessarily repaired.
Remediation
- Decide explicitly whether each identity object is operator-owned or referenced, and store immutable object IDs plus
NebariApp UID ownership.
- Reconcile removals for fully managed resources, and delete or revoke generated credentials on cleanup (give the OIDC Secret an owner reference).
- Periodically verify external desired state rather than trusting only a local hash.
- Provide migration and dry-run tooling before removing shared realm resources.
Acceptance criteria
- Removing a user, scope, mapper, app, or credential produces complete, bounded revocation.
- The OIDC Secret is removed on
NebariApp deletion.
- External Keycloak drift is detected and repaired without requiring a spec change.
References
Line numbers reference commit 4ad2c10.
Summary
Identity and credential reconciliation is largely additive. Group membership sync only adds users, client scopes and protocol mappers are added or updated but never removed when dropped from spec, and
NebariAppdeletion leaves groups, memberships, realm client scopes, and the generated OIDC Secret behind. A hash optimization can also skip provisioning without confirming that Keycloak still matches the desired state.Affected code
internal/controller/reconcilers/auth/providers/keycloak.go:syncClientScopes(L749-824) creates and assigns scopes but never unassigns or deletes stale ones.syncClientProtocolMappers(L833-910) creates/updates mappers by name but never removes ones dropped from spec.storeClientSecret(L696-745) builds the OIDC Secret with no owner reference, so it is not garbage-collected and is never deleted.internal/controller/reconcilers/auth/reconciler.go:CleanupAuth(L265-300) callsCleanupTokenExchange(only when token exchange was enabled, L279) andDeleteClient. It does not remove groups, memberships, realm scopes, or the OIDC Secret.AuthReady=Trueand the storedAuthConfigHashis unchanged, without reading Keycloak back, so external drift persists until a spec change or the force-reprovision annotation.Impact
Access can persist after desired membership is removed, realm resources accumulate and can collide with later applications, OIDC client credentials remain in the cluster after app deletion, and security-relevant Keycloak drift is not necessarily repaired.
Remediation
NebariAppUID ownership.Acceptance criteria
NebariAppdeletion.References
Line numbers reference commit
4ad2c10.