Change error to 400 for unknown user (#40939)

Closes #39079

Signed-off-by: Keshav Deshpande <keshavprashantdeshpande@gmail.com>
This commit is contained in:
Keshav Deshpande
2025-07-31 10:23:14 +02:00
committed by GitHub
parent 1f608fae6e
commit bee7e4b335
3 changed files with 36 additions and 0 deletions

View File

@@ -42,6 +42,7 @@ public interface Errors {
String DIFFERENT_USER_AUTHENTICATING = "different_user_authenticating";
String DIFFERENT_USER_AUTHENTICATED = "different_user_authenticated";
String USER_DELETE_ERROR = "user_delete_error";
String INVALID_USER = "invalid_user";
String USERNAME_MISSING = "username_missing";
String USERNAME_IN_USE = "username_in_use";

View File

@@ -79,6 +79,15 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
return;
}
if (user.getServiceAccountClientLink() != null) {
AuthenticatorUtils.dummyHash(context);
context.getEvent().detail(Details.REASON, "User is a service account");
context.getEvent().error(Errors.INVALID_USER);
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
return;
}
String bruteForceError = getDisabledByBruteForceEventError(context, user);
if (bruteForceError != null) {
AuthenticatorUtils.dummyHash(context);

View File

@@ -32,6 +32,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.common.Profile;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
@@ -775,6 +776,31 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
assertNull(response.getRefreshToken());
}
@Test
public void grantAccessTokenServiceAccountUserOfOtherClient() throws Exception {
ClientManager.realm(adminClient.realm("test")).clientId("resource-owner").setServiceAccountsEnabled(true);
oauth.client("resource-owner-refresh", "secret");
AccessTokenResponse response = oauth.doPasswordGrantRequest("service-account-resource-owner", "password");
assertEquals(401, response.getStatusCode());
assertEquals("invalid_grant", response.getError());
assertEquals("Invalid user credentials", response.getErrorDescription());
events.expectLogin()
.client("resource-owner-refresh")
.session((String) null)
.user((String) null)
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
.detail(Details.REASON, "User is a service account")
.removeDetail(Details.CODE_ID)
.removeDetail(Details.REDIRECT_URI)
.removeDetail(Details.CONSENT)
.error(Errors.INVALID_USER)
.assertEvent();
ClientManager.realm(adminClient.realm("test")).clientId("resource-owner").setServiceAccountsEnabled(false);
}
private int getAuthenticationSessionsCount() {
return testingClient.testing().cache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME).size();
}