Validate SCIM user inputs against Keycloak user profile rules#116
Conversation
…dates - Validate SCIM user create, update, and patch requests using Keycloak's `UserProfileProvider`. - Convert Keycloak user profile validation failures into SCIM-compliant error responses. - Support organization `emailAsUsername` mappings while still providing Keycloak's internal `username` value for profile validation. - Add shared handling for validation errors in realm and organization SCIM user endpoints. - Update tests to assert structured SCIM Error responses. - Ensure integration tests rebuild provider jars before running Keycloak Testcontainers.
…uplicate usernames and emails
|
I've been stumbling about this issue, too. When will it become part of a release? |
nicolamacoir
left a comment
There was a problem hiding this comment.
Great work! Would be good to include this into our upcoming 1.6.0 release.
I have a few points:
-
PATCH desyncs
usernamevsemailwhenemailAsUsername=true.validateForPatchreseeds the map from the existing user, but on PATCHreplace userNamethe SCIM userName→emailmapping overwritesemailonly —usernamekeeps the stale value. Validation runs against(new email, old username). Create/update reseed correctly; PATCH doesn't.
ref:UserProfileValidationService.java:69-78+UsersController.java:374-392 -
UnsupportedUserPathescapes as a 500 instead of a SCIM 400. The new pre-validation pass throwsUnsupportedUserPath(unchecked); thepatchUsertry/catch in both ScimServer subclasses only handlesUnsupportedPatchOperationandUserProfileValidationException.
ref:UsersController.java:300-392,RealmScimServer.java:139-147,OrganizationScimServer.java:146-154 -
Test fixtures relaxed:
editUsernameAllowedflippedfalse→trueon both shared realms to make the new "incorrect username on update" tests pass. Every existing test now runs in a non-default Keycloak config. Use a dedicated test realm instead.
ref:src/test/resources/kc-test.json:34,kc-organizations.json:36 -
userName/emailcollide on the same map key whenemailAsUsername=true— the email block silently wins, so validation runs againstemails[0].valueeven when the client sent a differentuserName. The duplicated"email: error-invalid-email; email: error-invalid-email"in the new test asserts this collision as expected behavior.
ref:UserProfileValidationService.java:120-141 -
validateForPatchre-validates the entire existing user, not just patched attributes. If profile rules tightened after a user was created, an unrelated PATCH ondisplayNamestarts failing with errors onfirstName/lastName/etc. -
PATCH
removewritesnullinto the validation map → REMOVE on a required field is indistinguishable from clearing it; returns a generic error instead ofscimType=mutability. No test coverage. -
Error responses omit
scimType(RFC 7644 §3.12). PR description claims "SCIM-compliant errors"; Okta/Entra branch on this field. -
Validation error detail leaks raw Keycloak attribute names including IDP-mapper attributes (with
unmanagedAttributePolicy=ENABLED). Map back to SCIM paths before serializing. -
Six identical try/catch blocks across
RealmScimServer/OrganizationScimServer/two controllers for create/update/patch — a template method onAbstractScimServerwould collapse this.
Centralize write-path error handling and enrich SCIM error responses
following review comments:
- Route create/update/patch through AbstractScimServer.executeUserOperation,
replacing duplicated per-operation try/catch in RealmScimServer and
OrganizationScimServer; add UserOperation functional interface
- Emit RFC 7644 scimType on error bodies via new ScimErrors helpers
(invalidValue, invalidFilter, invalidPath, invalidSyntax, mutability,
uniqueness) so clients get a machine-readable error category
- Add UserAttributes.findBySourceId to resolve a Keycloak source ID to
its SCIM attribute
- Add validation test realms (kc-validation-test, kc-validation-orgs) with
Realm/OrganizationUserValidationTestsIT suites and shared test bases;
move create/update validation assertions into them
- Fix testPatchUserAdminEvents to patch active instead of userName, which
validation now correctly rejects when editUsernameAllowed=false
|
|
Changes addressed @nicolamacoir |
|
Great work @msegurar02. LGTM! |



Summary
Adds Keycloak user profile validation to SCIM user create, update, and patch flows.
SCIM user input is now validated against the realm's configured Keycloak user profile before users are created or modified, so profile rules such as required attributes, username/email constraints, and field validators are enforced consistently through the SCIM API.
Changes
UserProfileProvider.emailAsUsernamemappings while still providing Keycloak's internalusernamevalue for profile validation.Why
Previously, SCIM user operations could bypass validation configured in the Keycloak user profile. This meant invalid or incomplete user data could be accepted through SCIM even when the same input would be rejected by Keycloak's own user-management APIs.
This change makes SCIM user operations respect the configured Keycloak user profile and return proper SCIM error responses when validation fails.
Validation
./gradlew test.