diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 4641702..872fd4c 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -555,7 +555,7 @@ public class TokenEndpoint {
UserModel user = authSession.getAuthenticatedUser();
if (user.getRequiredActions() != null && user.getRequiredActions().size() > 0) {
event.error(Errors.RESOLVE_REQUIRED_ACTIONS);
- throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid user credentials", Response.Status.UNAUTHORIZED);
+ throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Account is not fully set up", Response.Status.BAD_REQUEST);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 23bc245..167cbf4 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -615,10 +615,23 @@ public class AccessTokenTest extends AbstractKeycloakTest {
userResource.update(userRepresentation);
}
+ // good password is 400 => Account is not fully set up
+ try (Response response = executeGrantAccessTokenRequest(grantTarget)) {
+ assertEquals(400, response.getStatus());
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode jsonNode = objectMapper.readTree(response.readEntity(String.class));
+ assertEquals("invalid_grant", jsonNode.get("error").asText());
+ assertEquals("Account is not fully set up", jsonNode.get("error_description").asText());
+ }
- Response response = executeGrantAccessTokenRequest(grantTarget);
- assertEquals(401, response.getStatus());
- response.close();
+ // wrong password is 401 => Invalid user credentials
+ try (Response response = executeGrantAccessTokenRequestWrongPassword(grantTarget)) {
+ assertEquals(401, response.getStatus());
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode jsonNode = objectMapper.readTree(response.readEntity(String.class));
+ assertEquals("invalid_grant", jsonNode.get("error").asText());
+ assertEquals("Invalid user credentials", jsonNode.get("error_description").asText());
+ }
{
UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
@@ -1018,6 +1031,10 @@ public class AccessTokenTest extends AbstractKeycloakTest {
return executeGrantRequest(grantTarget, username, password);
}
+ protected Response executeGrantAccessTokenRequestWrongPassword(WebTarget grantTarget) {
+ return executeGrantRequest(grantTarget, "test-user@localhost", "bad-password");
+ }
+
protected Response executeGrantRequest(WebTarget grantTarget, String username, String password) {
String header = BasicAuthHelper.createHeader("test-app", "password");
Form form = new Form();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
index e84dbce..aa2f072 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
@@ -331,10 +331,10 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "password");
- assertEquals(401, response.getStatusCode());
+ assertEquals(400, response.getStatusCode());
assertEquals("invalid_grant", response.getError());
- assertEquals("Invalid user credentials", response.getErrorDescription());
+ assertEquals("Account is not fully set up", response.getErrorDescription());
events.expectLogin()
.client("resource-owner")
@@ -348,6 +348,36 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
UserManager.realm(realmResource).username("test-user@localhost").removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL.toString());
}
+
+ @Test
+ public void grantAccessTokenVerifyEmailInvalidPassword() throws Exception {
+
+ RealmResource realmResource = adminClient.realm("test");
+ RealmManager.realm(realmResource).verifyEmail(true);
+
+ oauth.clientId("resource-owner");
+
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "bad-password");
+
+ assertEquals(401, response.getStatusCode());
+
+ assertEquals("invalid_grant", response.getError());
+ assertEquals("Invalid user credentials", response.getErrorDescription());
+
+ events.expectLogin()
+ .client("resource-owner")
+ .session((String) null)
+ .detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
+ .removeDetail(Details.CODE_ID)
+ .removeDetail(Details.REDIRECT_URI)
+ .removeDetail(Details.CONSENT)
+ .error(Errors.INVALID_USER_CREDENTIALS)
+ .assertEvent();
+
+ RealmManager.realm(realmResource).verifyEmail(false);
+ UserManager.realm(realmResource).username("test-user@localhost").removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL.toString());
+
+ }
@Test
public void grantAccessTokenExpiredPassword() throws Exception {
@@ -362,10 +392,10 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "password");
- assertEquals(401, response.getStatusCode());
+ assertEquals(400, response.getStatusCode());
assertEquals("invalid_grant", response.getError());
- assertEquals("Invalid user credentials", response.getErrorDescription());
+ assertEquals("Account is not fully set up", response.getErrorDescription());
setTimeOffset(0);
@@ -382,6 +412,40 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
.removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
}
}
+
+ @Test
+ public void grantAccessTokenExpiredPasswordInvalidPassword() throws Exception {
+
+ RealmResource realmResource = adminClient.realm("test");
+ RealmManager.realm(realmResource).passwordPolicy("forceExpiredPasswordChange(1)");
+
+ try {
+ setTimeOffset(60 * 60 * 48);
+
+ oauth.clientId("resource-owner");
+
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "bad-password");
+
+ assertEquals(401, response.getStatusCode());
+
+ assertEquals("invalid_grant", response.getError());
+ assertEquals("Invalid user credentials", response.getErrorDescription());
+
+ events.expectLogin()
+ .client("resource-owner")
+ .session((String) null)
+ .detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
+ .removeDetail(Details.CODE_ID)
+ .removeDetail(Details.REDIRECT_URI)
+ .removeDetail(Details.CONSENT)
+ .error(Errors.INVALID_USER_CREDENTIALS)
+ .assertEvent();
+ } finally {
+ RealmManager.realm(realmResource).passwordPolicy("");
+ UserManager.realm(realmResource).username("test-user@localhost")
+ .removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString());
+ }
+ }
@Test
public void grantAccessTokenInvalidUserCredentials() throws Exception {
@@ -392,6 +456,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
assertEquals(401, response.getStatusCode());
assertEquals("invalid_grant", response.getError());
+ assertEquals("Invalid user credentials", response.getErrorDescription());
events.expectLogin()
.client("resource-owner")