diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/device/endpoints/DeviceEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/device/endpoints/DeviceEndpoint.java
index 9efa157cc5..662cb1c090 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/grants/device/endpoints/DeviceEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/device/endpoints/DeviceEndpoint.java
@@ -111,6 +111,12 @@ public class DeviceEndpoint extends AuthorizationEndpointBase implements RealmRe
AuthorizationEndpointRequest request = AuthorizationEndpointRequestParserProcessor.parseRequest(event, session, client,
httpRequest.getDecodedFormParameters());
+ if (request.getInvalidRequestMessage() != null) {
+ event.error(Errors.INVALID_REQUEST);
+ throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT,
+ request.getInvalidRequestMessage(), Response.Status.BAD_REQUEST);
+ }
+
if (!TokenUtil.isOIDCRequest(request.getScope())) {
ServicesLogger.LOGGER.oidcScopeMissing();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuth2DeviceAuthorizationGrantTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuth2DeviceAuthorizationGrantTest.java
index 692124e878..a2367ec03c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuth2DeviceAuthorizationGrantTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuth2DeviceAuthorizationGrantTest.java
@@ -51,7 +51,18 @@ import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.keycloak.util.BasicAuthHelper;
+
import java.util.List;
+import java.util.LinkedList;
+
+import java.io.UnsupportedEncodingException;
/**
* @author Hiroyuki Wada
@@ -576,6 +587,17 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
}
}
+ @Test
+ public void testDuplicatedRequestParams() throws Exception {
+ oauth.realm(REALM_NAME);
+ oauth.clientId(DEVICE_APP_PUBLIC);
+ OAuthClient.DeviceAuthorizationResponse response = doDeviceAuthorizationWithDuplicatedParams(DEVICE_APP_PUBLIC, null);
+
+ Assert.assertEquals(400, response.getStatusCode());
+ Assert.assertEquals("invalid_grant", response.getError());
+ Assert.assertEquals("duplicated parameter", response.getErrorDescription());
+ }
+
@Test
public void testDeviceCodeLifespanPerClient() throws Exception {
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm(REALM_NAME), DEVICE_APP);
@@ -825,4 +847,31 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
private void openVerificationPage(String verificationUri) {
driver.navigate().to(verificationUri);
}
+
+ private OAuthClient.DeviceAuthorizationResponse doDeviceAuthorizationWithDuplicatedParams(String clientId, String clientSecret) throws Exception {
+ try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+ HttpPost post = new HttpPost(oauth.getDeviceAuthorizationUrl());
+
+ List parameters = new LinkedList<>();
+ if (clientSecret != null) {
+ String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+ post.setHeader("Authorization", authorization);
+ } else {
+ parameters.add(new BasicNameValuePair("client_id", clientId));
+ }
+
+ parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, "profile"));
+ parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, "foo"));
+
+ UrlEncodedFormEntity formEntity;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ post.setEntity(formEntity);
+
+ return new OAuthClient.DeviceAuthorizationResponse(client.execute(post));
+ }
+ }
}