set request object mandatory for client, restrict delivery mode
handle new attribute in client representation add to UI
This commit is contained in:
parent
917ba90f2c
commit
57f57f5c75
10 changed files with 383 additions and 0 deletions
|
@ -86,6 +86,8 @@ public class OIDCClientRepresentation {
|
||||||
|
|
||||||
private String request_object_encryption_enc;
|
private String request_object_encryption_enc;
|
||||||
|
|
||||||
|
private String request_object_required;
|
||||||
|
|
||||||
private Integer default_max_age;
|
private Integer default_max_age;
|
||||||
|
|
||||||
private Boolean require_auth_time;
|
private Boolean require_auth_time;
|
||||||
|
@ -337,6 +339,14 @@ public class OIDCClientRepresentation {
|
||||||
this.request_object_encryption_enc = request_object_encryption_enc;
|
this.request_object_encryption_enc = request_object_encryption_enc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRequestObjectRequired() {
|
||||||
|
return request_object_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestObjectRequired(String request_object_required) {
|
||||||
|
this.request_object_required = request_object_required;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getDefaultMaxAge() {
|
public Integer getDefaultMaxAge() {
|
||||||
return default_max_age;
|
return default_max_age;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ public class OIDCAdvancedConfigWrapper {
|
||||||
|
|
||||||
private static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg";
|
private static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg";
|
||||||
|
|
||||||
|
private static final String REQUEST_OBJECT_REQUIRED = "request.object.required";
|
||||||
|
|
||||||
private static final String JWKS_URL = "jwks.url";
|
private static final String JWKS_URL = "jwks.url";
|
||||||
|
|
||||||
private static final String USE_JWKS_URL = "use.jwks.url";
|
private static final String USE_JWKS_URL = "use.jwks.url";
|
||||||
|
@ -80,6 +82,14 @@ public class OIDCAdvancedConfigWrapper {
|
||||||
setAttribute(REQUEST_OBJECT_SIGNATURE_ALG, algStr);
|
setAttribute(REQUEST_OBJECT_SIGNATURE_ALG, algStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRequestObjectRequired() {
|
||||||
|
return getAttribute(REQUEST_OBJECT_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestObjectRequired(String requestObjectRequired) {
|
||||||
|
setAttribute(REQUEST_OBJECT_REQUIRED, requestObjectRequired);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUseJwksUrl() {
|
public boolean isUseJwksUrl() {
|
||||||
String useJwksUrl = getAttribute(USE_JWKS_URL);
|
String useJwksUrl = getAttribute(USE_JWKS_URL);
|
||||||
return Boolean.parseBoolean(useJwksUrl);
|
return Boolean.parseBoolean(useJwksUrl);
|
||||||
|
|
|
@ -93,6 +93,11 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
public static final String CLIENT_SECRET_JWT = "client_secret_jwt";
|
public static final String CLIENT_SECRET_JWT = "client_secret_jwt";
|
||||||
public static final String PRIVATE_KEY_JWT = "private_key_jwt";
|
public static final String PRIVATE_KEY_JWT = "private_key_jwt";
|
||||||
|
|
||||||
|
// Request object requirement options
|
||||||
|
public static final String REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI = "request or request_uri";
|
||||||
|
public static final String REQUEST_OBJECT_REQUIRED_REQUEST = "request only";
|
||||||
|
public static final String REQUEST_OBJECT_REQUIRED_REQUEST_URI = "request_uri only";
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc7636#section-4.3
|
// https://tools.ietf.org/html/rfc7636#section-4.3
|
||||||
public static final String CODE_CHALLENGE_PARAM = "code_challenge";
|
public static final String CODE_CHALLENGE_PARAM = "code_challenge";
|
||||||
public static final String CODE_CHALLENGE_METHOD_PARAM = "code_challenge_method";
|
public static final String CODE_CHALLENGE_METHOD_PARAM = "code_challenge_method";
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.services.ErrorPageException;
|
import org.keycloak.services.ErrorPageException;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
@ -31,6 +32,9 @@ import org.keycloak.services.messages.Messages;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -50,6 +54,19 @@ public class AuthorizationEndpointRequestParserProcessor {
|
||||||
throw new RuntimeException("Illegal to use both 'request' and 'request_uri' parameters together");
|
throw new RuntimeException("Illegal to use both 'request' and 'request_uri' parameters together");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String requestObjectRequired = OIDCAdvancedConfigWrapper.fromClientModel(client).getRequestObjectRequired();
|
||||||
|
|
||||||
|
if (REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI.equals(requestObjectRequired)
|
||||||
|
&& requestParam == null && requestUriParam == null) {
|
||||||
|
throw new RuntimeException("Client is required to use 'request' or 'request_uri' parameter.");
|
||||||
|
} else if (REQUEST_OBJECT_REQUIRED_REQUEST.equals(requestObjectRequired)
|
||||||
|
&& requestParam == null) {
|
||||||
|
throw new RuntimeException("Client is required to use 'request' parameter.");
|
||||||
|
} else if (REQUEST_OBJECT_REQUIRED_REQUEST_URI.equals(requestObjectRequired)
|
||||||
|
&& requestUriParam == null) {
|
||||||
|
throw new RuntimeException("Client is required to use 'request_uri' parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
if (requestParam != null) {
|
if (requestParam != null) {
|
||||||
new AuthzEndpointRequestObjectParser(session, requestParam, client).parseRequest(request);
|
new AuthzEndpointRequestObjectParser(session, requestParam, client).parseRequest(request);
|
||||||
} else if (requestUriParam != null) {
|
} else if (requestUriParam != null) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -115,6 +116,17 @@ public class DescriptionConverter {
|
||||||
configWrapper.setRequestObjectSignatureAlg(algorithm);
|
configWrapper.setRequestObjectSignatureAlg(algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clientOIDC.getRequestObjectRequired() != null) {
|
||||||
|
String requestObjectRequired = clientOIDC.getRequestObjectRequired();
|
||||||
|
if (Arrays.asList(
|
||||||
|
OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI,
|
||||||
|
OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST,
|
||||||
|
OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_URI)
|
||||||
|
.contains(requestObjectRequired)) {
|
||||||
|
configWrapper.setRequestObjectRequired(requestObjectRequired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +197,9 @@ public class DescriptionConverter {
|
||||||
if (config.getRequestObjectSignatureAlg() != null) {
|
if (config.getRequestObjectSignatureAlg() != null) {
|
||||||
response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString());
|
response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString());
|
||||||
}
|
}
|
||||||
|
if (config.getRequestObjectRequired() != null) {
|
||||||
|
response.setRequestObjectRequired(config.getRequestObjectRequired());
|
||||||
|
}
|
||||||
if (config.isUseJwksUrl()) {
|
if (config.isUseJwksUrl()) {
|
||||||
response.setJwksUri(config.getJwksUrl());
|
response.setJwksUri(config.getJwksUrl());
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,20 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
|
||||||
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.RS256);
|
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.RS256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRequestObjectRequired() throws Exception {
|
||||||
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
clientRep.setRequestObjectRequired(OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI);
|
||||||
|
|
||||||
|
OIDCClientRepresentation response = reg.oidc().create(clientRep);
|
||||||
|
Assert.assertEquals(OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI, response.getRequestObjectRequired());
|
||||||
|
|
||||||
|
// Test Keycloak representation
|
||||||
|
ClientRepresentation kcClient = getClient(response.getClientId());
|
||||||
|
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
|
||||||
|
Assert.assertEquals(OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI, config.getRequestObjectRequired());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createClientImplicitFlow() throws ClientRegistrationException {
|
public void createClientImplicitFlow() throws ClientRegistrationException {
|
||||||
OIDCClientRepresentation clientRep = createRep();
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
|
|
@ -71,6 +71,10 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.REQUEST_OBJECT_REQUIRED_REQUEST_URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for supporting advanced parameters of OIDC specs (max_age, prompt, ...)
|
* Test for supporting advanced parameters of OIDC specs (max_age, prompt, ...)
|
||||||
*
|
*
|
||||||
|
@ -377,6 +381,281 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
|
||||||
|
|
||||||
// REQUEST & REQUEST_URI
|
// REQUEST & REQUEST_URI
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectNotRequiredNotProvided() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Send request without request object
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectNotRequiredProvidedInRequestParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object in "request" param
|
||||||
|
oauth.request(oidcClientEndpointsResource.getOIDCRequest());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response1 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response1.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response1.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectNotRequiredProvidedInRequestUriParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object reference in "request_uri" param
|
||||||
|
oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response2 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response2.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response2.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredNotProvided() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Send request without request object
|
||||||
|
// Assert that the request is not accepted
|
||||||
|
oauth.openLoginForm();
|
||||||
|
Assert.assertTrue(errorPage.isCurrent());
|
||||||
|
assertEquals("Invalid Request", errorPage.getError());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredProvidedInRequestParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object in "request" param
|
||||||
|
oauth.request(oidcClientEndpointsResource.getOIDCRequest());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response1 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response1.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response1.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredProvidedInRequestUriParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object reference in "request_uri" param
|
||||||
|
oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response2 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response2.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response2.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestParamNotProvided() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Send request without request object
|
||||||
|
// Assert that the request is not accepted
|
||||||
|
oauth.openLoginForm();
|
||||||
|
Assert.assertTrue(errorPage.isCurrent());
|
||||||
|
assertEquals("Invalid Request", errorPage.getError());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestParamProvidedInRequestParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object in "request" param
|
||||||
|
oauth.request(oidcClientEndpointsResource.getOIDCRequest());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response1 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response1.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response1.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestParamProvidedInRequestUriParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object reference in "request_uri" param
|
||||||
|
oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
oauth.openLoginForm();
|
||||||
|
Assert.assertTrue(errorPage.isCurrent());
|
||||||
|
assertEquals("Invalid Request", errorPage.getError());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestUriParamNotProvided() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Send request without request object
|
||||||
|
// Assert that the request is not accepted
|
||||||
|
oauth.openLoginForm();
|
||||||
|
Assert.assertTrue(errorPage.isCurrent());
|
||||||
|
assertEquals("Invalid Request", errorPage.getError());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestUriParamProvidedInRequestParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object in "request" param
|
||||||
|
oauth.request(oidcClientEndpointsResource.getOIDCRequest());
|
||||||
|
// Assert that the request is not accepted
|
||||||
|
oauth.openLoginForm();
|
||||||
|
Assert.assertTrue(errorPage.isCurrent());
|
||||||
|
assertEquals("Invalid Request", errorPage.getError());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestObjectRequiredAsRequestUriParamProvidedInRequestUriParam() throws Exception {
|
||||||
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
// Set request object not required for client
|
||||||
|
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(REQUEST_OBJECT_REQUIRED_REQUEST_URI);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
|
// Set up a request object
|
||||||
|
TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
|
||||||
|
oidcClientEndpointsResource.setOIDCRequest("test", "test-app", oauth.getRedirectUri(), "10", Algorithm.none.toString());
|
||||||
|
|
||||||
|
// Send request object reference in "request_uri" param
|
||||||
|
oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
|
||||||
|
// Assert that the request is accepted
|
||||||
|
OAuthClient.AuthorizationEndpointResponse response1 = oauth.doLogin("test-user@localhost", "password");
|
||||||
|
Assert.assertNotNull(response1.getCode());
|
||||||
|
Assert.assertEquals("mystate2", response1.getState());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Revert requiring request object for client
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectRequired(null);
|
||||||
|
clientResource.update(clientRep);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestParamUnsigned() throws Exception {
|
public void requestParamUnsigned() throws Exception {
|
||||||
oauth.stateParamHardcoded("mystate2");
|
oauth.stateParamHardcoded("mystate2");
|
||||||
|
|
|
@ -309,6 +309,8 @@ user-info-signed-response-alg=User Info Signed Response Algorithm
|
||||||
user-info-signed-response-alg.tooltip=JWA algorithm used for signed User Info Endpoint response. If set to 'unsigned', then User Info Response won't be signed and will be returned in application/json format.
|
user-info-signed-response-alg.tooltip=JWA algorithm used for signed User Info Endpoint response. If set to 'unsigned', then User Info Response won't be signed and will be returned in application/json format.
|
||||||
request-object-signature-alg=Request Object Signature Algorithm
|
request-object-signature-alg=Request Object Signature Algorithm
|
||||||
request-object-signature-alg.tooltip=JWA algorithm, which client needs to use when sending OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', then Request object can be signed by any algorithm (including 'none' ).
|
request-object-signature-alg.tooltip=JWA algorithm, which client needs to use when sending OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', then Request object can be signed by any algorithm (including 'none' ).
|
||||||
|
request-object-required=Request Object Required
|
||||||
|
request-object-required-alg.tooltip=Specifies if the client needs to provide a request object with their authorization requests, and what method they can use for this. If set to "not required", providing a request object is optional. In all other cases providing a request object is mandatory. If set to "request", the request object must be provided by value. If set to "request_uri", the request object must be provided by reference. If set to "request or request_uri", either method can be used.
|
||||||
fine-saml-endpoint-conf=Fine Grain SAML Endpoint Configuration
|
fine-saml-endpoint-conf=Fine Grain SAML Endpoint Configuration
|
||||||
fine-saml-endpoint-conf.tooltip=Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service.
|
fine-saml-endpoint-conf.tooltip=Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service.
|
||||||
assertion-consumer-post-binding-url=Assertion Consumer Service POST Binding URL
|
assertion-consumer-post-binding-url=Assertion Consumer Service POST Binding URL
|
||||||
|
|
|
@ -892,6 +892,13 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
"RS256"
|
"RS256"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$scope.requestObjectRequiredOptions = [
|
||||||
|
"not required",
|
||||||
|
"request or request_uri",
|
||||||
|
"request only",
|
||||||
|
"request_uri only"
|
||||||
|
];
|
||||||
|
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.samlAuthnStatement = false;
|
$scope.samlAuthnStatement = false;
|
||||||
$scope.samlOneTimeUseCondition = false;
|
$scope.samlOneTimeUseCondition = false;
|
||||||
|
@ -1045,6 +1052,9 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
var attrVal2 = $scope.client.attributes['request.object.signature.alg'];
|
var attrVal2 = $scope.client.attributes['request.object.signature.alg'];
|
||||||
$scope.requestObjectSignatureAlg = attrVal2==null ? 'any' : attrVal2;
|
$scope.requestObjectSignatureAlg = attrVal2==null ? 'any' : attrVal2;
|
||||||
|
|
||||||
|
var attrVal3 = $scope.client.attributes['request.object.required'];
|
||||||
|
$scope.requestObjectRequired = attrVal3==null ? 'not required' : attrVal3;
|
||||||
|
|
||||||
if ($scope.client.attributes["exclude.session.state.from.auth.response"]) {
|
if ($scope.client.attributes["exclude.session.state.from.auth.response"]) {
|
||||||
if ($scope.client.attributes["exclude.session.state.from.auth.response"] == "true") {
|
if ($scope.client.attributes["exclude.session.state.from.auth.response"] == "true") {
|
||||||
$scope.excludeSessionStateFromAuthResponse = true;
|
$scope.excludeSessionStateFromAuthResponse = true;
|
||||||
|
@ -1144,6 +1154,14 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.changeRequestObjectRequired = function() {
|
||||||
|
if ($scope.requestObjectRequired === 'not required') {
|
||||||
|
$scope.clientEdit.attributes['request.object.required'] = null;
|
||||||
|
} else {
|
||||||
|
$scope.clientEdit.attributes['request.object.required'] = $scope.requestObjectRequired;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.$watch(function() {
|
$scope.$watch(function() {
|
||||||
return $location.path();
|
return $location.path();
|
||||||
}, function() {
|
}, function() {
|
||||||
|
|
|
@ -409,6 +409,19 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'request-object-signature-alg.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'request-object-signature-alg.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group clearfix block" data-ng-show="protocol == 'openid-connect'">
|
||||||
|
<label class="col-md-2 control-label" for="changeRequestObjectRequired">{{:: 'request-object-required' | translate}}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div>
|
||||||
|
<select class="form-control" id="requestObjectRequired"
|
||||||
|
ng-change="changeRequestObjectRequired()"
|
||||||
|
ng-model="requestObjectRequired"
|
||||||
|
ng-options="sig for sig in requestObjectRequiredOptions">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'request-object-required.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset data-ng-show="protocol == 'openid-connect'">
|
<fieldset data-ng-show="protocol == 'openid-connect'">
|
||||||
|
|
Loading…
Reference in a new issue