From 22cc5e18861dabfd0c5b2dc99ee0abac48dad5a5 Mon Sep 17 00:00:00 2001 From: Marie Daly Date: Wed, 22 Jul 2026 13:58:35 +0100 Subject: [PATCH] Closes #50903, added length check for username Signed-off-by: Marie Daly --- .../browser/AbstractUsernameFormAuthenticator.java | 8 ++++++++ .../directgrant/ValidateUsername.java | 14 +++++++++++++- .../resetcred/ResetCredentialChooseUser.java | 10 ++++++++++ .../browser/OrganizationAuthenticator.java | 10 ++++++++++ .../keycloak/services/validation/Validation.java | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java index 2050169ef2bb..1a20a45df9f1 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java @@ -40,6 +40,7 @@ import static org.keycloak.services.validation.Validation.FIELD_PASSWORD; import static org.keycloak.services.validation.Validation.FIELD_USERNAME; +import static org.keycloak.services.validation.Validation.MAX_USERNAME_LENGTH; /** * @author Bill Burke @@ -173,6 +174,13 @@ private UserModel getUserFromForm(AuthenticationFlowContext context, Multivalued // remove leading and trailing whitespace username = username.trim(); + if (username.length() > MAX_USERNAME_LENGTH) { + context.getEvent().error(Errors.USER_NOT_FOUND); + Response challengeResponse = challenge(context, getDefaultChallengeMessage(context), FIELD_USERNAME); + context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse); + return null; + } + context.getEvent().detail(Details.USERNAME, username); context.getAuthenticationSession().setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java index 510d94e6060e..e920efe1ccee 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java @@ -42,6 +42,7 @@ import org.keycloak.services.managers.AuthenticationManager; import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError; +import static org.keycloak.services.validation.Validation.MAX_USERNAME_LENGTH; /** * @author Bill Burke @@ -54,12 +55,23 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator { @Override public void authenticate(AuthenticationFlowContext context) { String username = retrieveUsername(context); - if (username == null) { + if (username == null || username.isEmpty()) { context.getEvent().error(Errors.USER_NOT_FOUND); Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Missing parameter: username"); context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse); return; } + + // remove leading and trailing whitespace + username = username.trim(); + + if (username.length() > MAX_USERNAME_LENGTH) { + context.getEvent().error(Errors.INVALID_USER_CREDENTIALS); + Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Invalid user credentials"); + context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse); + return; + } + context.getEvent().detail(Details.USERNAME, username); context.getAuthenticationSession().setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java index 2a81f9444247..ae059f52660b 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java @@ -46,6 +46,8 @@ import org.jboss.logging.Logger; +import static org.keycloak.services.validation.Validation.MAX_USERNAME_LENGTH; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -110,6 +112,14 @@ public void action(AuthenticationFlowContext context) { } username = username.trim(); + if (username.length() > MAX_USERNAME_LENGTH) { + context.getEvent().error(Errors.USER_NOT_FOUND); + Response challengeResponse = context.form() + .addError(new FormMessage(Validation.FIELD_USERNAME, Messages.INVALID_USER)) + .createPasswordReset(); + context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse); + return; + } RealmModel realm = context.getRealm(); UserModel user = context.getSession().users().getUserByUsername(realm, username); diff --git a/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java b/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java index 15d116165494..d73547551fee 100644 --- a/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java +++ b/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java @@ -70,6 +70,7 @@ import static org.keycloak.organization.utils.Organizations.getMatchingDomain; import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent; import static org.keycloak.organization.utils.Organizations.resolveHomeBroker; +import static org.keycloak.services.validation.Validation.MAX_USERNAME_LENGTH; import static org.keycloak.utils.StringUtil.isBlank; public class OrganizationAuthenticator extends IdentityProviderAuthenticator { @@ -139,7 +140,16 @@ public void action(AuthenticationFlowContext context) { }); return; } + // remove leading and trailing whitespace + username = username.trim(); + if (username.length() > MAX_USERNAME_LENGTH) { + initialChallenge(context, form -> { + form.addError(new FormMessage(UserModel.USERNAME, Messages.INVALID_USERNAME)); + return form.createLoginUsername(); + }); + return; + } action(context, username); } diff --git a/services/src/main/java/org/keycloak/services/validation/Validation.java b/services/src/main/java/org/keycloak/services/validation/Validation.java index 527c0544076d..506f1dee0d1c 100755 --- a/services/src/main/java/org/keycloak/services/validation/Validation.java +++ b/services/src/main/java/org/keycloak/services/validation/Validation.java @@ -33,6 +33,7 @@ public class Validation { public static final String FIELD_USERNAME = "username"; public static final String FIELD_OTP_CODE = "totp"; public static final String FIELD_OTP_LABEL = "userLabel"; + public static final int MAX_USERNAME_LENGTH = 255; private static final Pattern USERNAME_PATTERN = Pattern.compile("^[\\p{IsLatin}|\\p{IsCommon}]+$");