KEYCLOAK-3400 OIDC request with missing response_type should respond with error

This commit is contained in:
mposolda 2016-08-08 16:11:50 +02:00
parent 64c2077c0b
commit 65e2f127c9
9 changed files with 120 additions and 45 deletions

View file

@ -271,12 +271,10 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
} }
private Response checkResponseType() { private Response checkResponseType() {
OIDCResponseMode defaultResponseMode = client.isImplicitFlowEnabled() ? OIDCResponseMode.FRAGMENT : OIDCResponseMode.QUERY;
if (responseType == null) { if (responseType == null) {
logger.missingParameter(OAuth2Constants.RESPONSE_TYPE); logger.missingParameter(OAuth2Constants.RESPONSE_TYPE);
event.error(Errors.INVALID_REQUEST); event.error(Errors.INVALID_REQUEST);
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Missing parameter: response_type"); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Missing parameter: response_type");
} }
event.detail(Details.RESPONSE_TYPE, responseType); event.detail(Details.RESPONSE_TYPE, responseType);
@ -289,7 +287,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
logger.error(iae.getMessage()); logger.error(iae.getMessage());
event.error(Errors.INVALID_REQUEST); event.error(Errors.INVALID_REQUEST);
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, null); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, null);
} }
OIDCResponseMode parsedResponseMode = null; OIDCResponseMode parsedResponseMode = null;
@ -298,7 +296,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
logger.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM); logger.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
event.error(Errors.INVALID_REQUEST); event.error(Errors.INVALID_REQUEST);
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Invalid parameter: response_mode"); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Invalid parameter: response_mode");
} }
event.detail(Details.RESPONSE_MODE, parsedResponseMode.toString().toLowerCase()); event.detail(Details.RESPONSE_MODE, parsedResponseMode.toString().toLowerCase());
@ -307,7 +305,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) { if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) {
logger.responseModeQueryNotAllowed(); logger.responseModeQueryNotAllowed();
event.error(Errors.INVALID_REQUEST); event.error(Errors.INVALID_REQUEST);
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Response_mode 'query' not allowed for implicit or hybrid flow"); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Response_mode 'query' not allowed for implicit or hybrid flow");
} }
if ((parsedResponseType.hasResponseType(OIDCResponseType.CODE) || parsedResponseType.hasResponseType(OIDCResponseType.NONE)) && !client.isStandardFlowEnabled()) { if ((parsedResponseType.hasResponseType(OIDCResponseType.CODE) || parsedResponseType.hasResponseType(OIDCResponseType.NONE)) && !client.isStandardFlowEnabled()) {

View file

@ -144,33 +144,6 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
assertCode(codeId, response.getCode()); assertCode(codeId, response.getCode());
} }
@Test
public void authorizationRequestImplicitFlowDisabled() throws IOException {
oauth.responseType("token id_token");
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth, true);
Assert.assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertEquals(errorResponse.getErrorDescription(), "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "token id_token").assertEvent();
}
@Test
public void authorizationRequestMissingResponseType() throws IOException {
oauth.responseType(null);
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
Assert.assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.INVALID_REQUEST);
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().assertEvent();
}
@Test @Test
public void authorizationRequestInvalidResponseType() throws IOException { public void authorizationRequestInvalidResponseType() throws IOException {
oauth.responseType("tokenn"); oauth.responseType("tokenn");

View file

@ -17,13 +17,18 @@
package org.keycloak.testsuite.oidc.flows; package org.keycloak.testsuite.oidc.flows;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List; import java.util.List;
import javax.ws.rs.core.UriBuilder;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.jose.jws.Algorithm; import org.keycloak.jose.jws.Algorithm;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
@ -34,6 +39,7 @@ import org.keycloak.testsuite.TestRealmKeycloakTest;
import org.keycloak.testsuite.admin.AbstractAdminTest; import org.keycloak.testsuite.admin.AbstractAdminTest;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -81,6 +87,21 @@ public abstract class AbstractOIDCResponseTypeTest extends TestRealmKeycloakTest
} }
@Test
public void authorizationRequestMissingResponseType() throws IOException {
oauth.responseType(null);
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
// Always read error from the "query"
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth, false);
org.junit.Assert.assertTrue(errorResponse.isRedirected());
org.junit.Assert.assertEquals(errorResponse.getError(), OAuthErrorException.INVALID_REQUEST);
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().assertEvent();
}
protected void validateNonceNotUsedErrorExpected() { protected void validateNonceNotUsedErrorExpected() {
oauth.nonce(null); oauth.nonce(null);
driver.navigate().to(oauth.getLoginFormUrl()); driver.navigate().to(oauth.getLoginFormUrl());
@ -97,6 +118,45 @@ public abstract class AbstractOIDCResponseTypeTest extends TestRealmKeycloakTest
} }
protected void validateErrorImplicitFlowNotAllowed() throws Exception {
// Disable implicit flow for client
clientManagerBuilder().implicitFlow(false);
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
Assert.assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertEquals(errorResponse.getErrorDescription(), "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().assertEvent();
// Revert
clientManagerBuilder().implicitFlow(true);
}
protected void validateErrorStandardFlowNotAllowed() throws Exception {
// Disable standard flow for client
clientManagerBuilder().standardFlow(false);
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
Assert.assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertEquals(errorResponse.getErrorDescription(), "Client is not allowed to initiate browser login with given response_type. Standard flow is disabled for the client.");
events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().assertEvent();
// Revert
clientManagerBuilder().standardFlow(true);
}
protected EventRepresentation loginUser(String nonce) { protected EventRepresentation loginUser(String nonce) {
if (nonce != null) { if (nonce != null) {
oauth.nonce(nonce); oauth.nonce(nonce);
@ -112,4 +172,8 @@ public abstract class AbstractOIDCResponseTypeTest extends TestRealmKeycloakTest
} }
protected abstract List<IDToken> retrieveIDTokens(EventRepresentation loginEvent); protected abstract List<IDToken> retrieveIDTokens(EventRepresentation loginEvent);
protected ClientManager.ClientManagerBuilder clientManagerBuilder() {
return ClientManager.realm(adminClient.realm("test")).clientId("test-app");
}
} }

View file

@ -39,7 +39,7 @@ public class OIDCBasicResponseTypeCodeTest extends AbstractOIDCResponseTypeTest
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(true).implicitFlow(false); clientManagerBuilder().standardFlow(true).implicitFlow(false);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.CODE); oauth.responseType(OIDCResponseType.CODE);
@ -68,4 +68,9 @@ public class OIDCBasicResponseTypeCodeTest extends AbstractOIDCResponseTypeTest
Assert.assertNull(idToken.getNonce()); Assert.assertNull(idToken.getNonce());
} }
} }
@Test
public void errorStandardFlowNotAllowed() throws Exception {
super.validateErrorStandardFlowNotAllowed();
}
} }

View file

@ -28,7 +28,6 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -40,7 +39,7 @@ public class OIDCHybridResponseTypeCodeIDTokenTest extends AbstractOIDCResponseT
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(true).implicitFlow(true); clientManagerBuilder().standardFlow(true).implicitFlow(true);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN); oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN);
@ -72,4 +71,14 @@ public class OIDCHybridResponseTypeCodeIDTokenTest extends AbstractOIDCResponseT
public void nonceNotUsedErrorExpected() { public void nonceNotUsedErrorExpected() {
super.validateNonceNotUsedErrorExpected(); super.validateNonceNotUsedErrorExpected();
} }
@Test
public void errorStandardFlowNotAllowed() throws Exception {
super.validateErrorStandardFlowNotAllowed();
}
@Test
public void errorImplicitFlowNotAllowed() throws Exception {
super.validateErrorImplicitFlowNotAllowed();
}
} }

View file

@ -28,7 +28,6 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -40,7 +39,7 @@ public class OIDCHybridResponseTypeCodeIDTokenTokenTest extends AbstractOIDCResp
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(true).implicitFlow(true); clientManagerBuilder().standardFlow(true).implicitFlow(true);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN + " " + OIDCResponseType.TOKEN); oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN + " " + OIDCResponseType.TOKEN);
@ -73,4 +72,14 @@ public class OIDCHybridResponseTypeCodeIDTokenTokenTest extends AbstractOIDCResp
public void nonceNotUsedErrorExpected() { public void nonceNotUsedErrorExpected() {
super.validateNonceNotUsedErrorExpected(); super.validateNonceNotUsedErrorExpected();
} }
@Test
public void errorStandardFlowNotAllowed() throws Exception {
super.validateErrorStandardFlowNotAllowed();
}
@Test
public void errorImplicitFlowNotAllowed() throws Exception {
super.validateErrorImplicitFlowNotAllowed();
}
} }

View file

@ -17,7 +17,6 @@
package org.keycloak.testsuite.oidc.flows; package org.keycloak.testsuite.oidc.flows;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -28,7 +27,6 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -40,7 +38,7 @@ public class OIDCHybridResponseTypeCodeTokenTest extends AbstractOIDCResponseTyp
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(true).implicitFlow(true); clientManagerBuilder().standardFlow(true).implicitFlow(true);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.TOKEN); oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.TOKEN);
@ -66,4 +64,14 @@ public class OIDCHybridResponseTypeCodeTokenTest extends AbstractOIDCResponseTyp
super.validateNonceNotUsedErrorExpected(); super.validateNonceNotUsedErrorExpected();
} }
@Test
public void errorStandardFlowNotAllowed() throws Exception {
super.validateErrorStandardFlowNotAllowed();
}
@Test
public void errorImplicitFlowNotAllowed() throws Exception {
super.validateErrorImplicitFlowNotAllowed();
}
} }

View file

@ -27,7 +27,6 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -39,7 +38,7 @@ public class OIDCImplicitResponseTypeIDTokenTest extends AbstractOIDCResponseTyp
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(false).implicitFlow(true); clientManagerBuilder().standardFlow(false).implicitFlow(true);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.ID_TOKEN); oauth.responseType(OIDCResponseType.ID_TOKEN);
@ -66,4 +65,10 @@ public class OIDCImplicitResponseTypeIDTokenTest extends AbstractOIDCResponseTyp
super.validateNonceNotUsedErrorExpected(); super.validateNonceNotUsedErrorExpected();
} }
@Test
public void errorImplicitFlowNotAllowed() throws Exception {
super.validateErrorImplicitFlowNotAllowed();
}
} }

View file

@ -28,7 +28,6 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -40,7 +39,7 @@ public class OIDCImplicitResponseTypeIDTokenTokenTest extends AbstractOIDCRespon
@Before @Before
public void clientConfiguration() { public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").standardFlow(false).implicitFlow(true); clientManagerBuilder().standardFlow(false).implicitFlow(true);
oauth.clientId("test-app"); oauth.clientId("test-app");
oauth.responseType(OIDCResponseType.ID_TOKEN + " " + OIDCResponseType.TOKEN); oauth.responseType(OIDCResponseType.ID_TOKEN + " " + OIDCResponseType.TOKEN);
@ -68,4 +67,9 @@ public class OIDCImplicitResponseTypeIDTokenTokenTest extends AbstractOIDCRespon
public void nonceNotUsedErrorExpected() { public void nonceNotUsedErrorExpected() {
super.validateNonceNotUsedErrorExpected(); super.validateNonceNotUsedErrorExpected();
} }
@Test
public void errorImplicitFlowNotAllowed() throws Exception {
super.validateErrorImplicitFlowNotAllowed();
}
} }