[KEYCLOAK-18723] - Configurable constraints for request object encryption

This commit is contained in:
Pedro Igor 2021-07-12 10:25:30 -03:00 committed by Marek Posolda
parent 730d4e8ac9
commit 396a78bcc4
13 changed files with 253 additions and 28 deletions

View file

@ -74,7 +74,23 @@ public class OIDCAdvancedConfigWrapper {
String algStr = alg==null ? null : alg.toString(); String algStr = alg==null ? null : alg.toString();
setAttribute(OIDCConfigAttributes.REQUEST_OBJECT_SIGNATURE_ALG, algStr); setAttribute(OIDCConfigAttributes.REQUEST_OBJECT_SIGNATURE_ALG, algStr);
} }
public void setRequestObjectEncryptionAlg(String algorithm) {
setAttribute(OIDCConfigAttributes.REQUEST_OBJECT_ENCRYPTION_ALG, algorithm);
}
public String getRequestObjectEncryptionAlg() {
return getAttribute(OIDCConfigAttributes.REQUEST_OBJECT_ENCRYPTION_ALG);
}
public String getRequestObjectEncryptionEnc() {
return getAttribute(OIDCConfigAttributes.REQUEST_OBJECT_ENCRYPTION_ENC);
}
public void setRequestObjectEncryptionEnc(String algorithm) {
setAttribute(OIDCConfigAttributes.REQUEST_OBJECT_ENCRYPTION_ENC, algorithm);
}
public String getRequestObjectRequired() { public String getRequestObjectRequired() {
return getAttribute(OIDCConfigAttributes.REQUEST_OBJECT_REQUIRED); return getAttribute(OIDCConfigAttributes.REQUEST_OBJECT_REQUIRED);
} }

View file

@ -21,6 +21,8 @@ public final class OIDCConfigAttributes {
public static final String USER_INFO_RESPONSE_SIGNATURE_ALG = "user.info.response.signature.alg"; public static final String USER_INFO_RESPONSE_SIGNATURE_ALG = "user.info.response.signature.alg";
public static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg"; public static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg";
public static final String REQUEST_OBJECT_ENCRYPTION_ALG = "request.object.encryption.alg";
public static final String REQUEST_OBJECT_ENCRYPTION_ENC = "request.object.encryption.enc";
public static final String REQUEST_OBJECT_REQUIRED = "request.object.required"; public static final String REQUEST_OBJECT_REQUIRED = "request.object.required";
public static final String REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI = "request or request_uri"; public static final String REQUEST_OBJECT_REQUIRED_REQUEST_OR_REQUEST_URI = "request or request_uri";

View file

@ -19,10 +19,13 @@ package org.keycloak.protocol.oidc.endpoints.request;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.jose.JOSEHeader; import org.keycloak.jose.JOSEHeader;
import org.keycloak.jose.JOSE; import org.keycloak.jose.JOSE;
import org.keycloak.jose.jwe.JWE;
import org.keycloak.jose.jwe.JWEHeader;
import org.keycloak.jose.jws.Algorithm; import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
@ -36,28 +39,10 @@ import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
*/ */
public class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser { public class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
private static void validateAlgorithm(JOSE jwt, ClientModel clientModel) {
if (jwt instanceof JWSInput) {
JOSEHeader header = jwt.getHeader();
String headerAlgorithm = header.getRawAlgorithm();
if (headerAlgorithm == null) {
throw new RuntimeException("Request object signed algorithm not specified");
}
Algorithm requestedSignatureAlgorithm = OIDCAdvancedConfigWrapper.fromClientModel(clientModel)
.getRequestObjectSignatureAlg();
if (requestedSignatureAlgorithm != null && !requestedSignatureAlgorithm.name().equals(headerAlgorithm)) {
throw new RuntimeException("Request object signed with different algorithm than client requested algorithm");
}
}
}
private final JsonNode requestParams; private final JsonNode requestParams;
public AuthzEndpointRequestObjectParser(KeycloakSession session, String requestObject, ClientModel client) { public AuthzEndpointRequestObjectParser(KeycloakSession session, String requestObject, ClientModel client) {
this.requestParams = session.tokens().decodeClientJWT(requestObject, client, AuthzEndpointRequestObjectParser::validateAlgorithm, JsonNode.class); this.requestParams = session.tokens().decodeClientJWT(requestObject, client, createRequestObjectValidator(session), JsonNode.class);
if (this.requestParams == null) { if (this.requestParams == null) {
throw new RuntimeException("Failed to verify signature on 'request' object"); throw new RuntimeException("Failed to verify signature on 'request' object");
@ -101,6 +86,48 @@ public class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser
return keys; return keys;
} }
private BiConsumer<JOSE, ClientModel> createRequestObjectValidator(KeycloakSession session) {
return (jwt, clientModel) -> {
if (jwt instanceof JWSInput) {
JOSEHeader header = jwt.getHeader();
String headerAlgorithm = header.getRawAlgorithm();
if (headerAlgorithm == null) {
throw new RuntimeException("Request object signed algorithm not specified");
}
Algorithm requestedSignatureAlgorithm = OIDCAdvancedConfigWrapper.fromClientModel(clientModel)
.getRequestObjectSignatureAlg();
if (requestedSignatureAlgorithm != null && !requestedSignatureAlgorithm.name().equals(headerAlgorithm)) {
throw new RuntimeException(
"Request object signed with different algorithm than client requested algorithm");
}
} else {
String encryptionAlg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel).getRequestObjectEncryptionAlg();
if (encryptionAlg != null) {
if (!encryptionAlg.equals(jwt.getHeader().getRawAlgorithm())) {
throw new RuntimeException("Request object encrypted with different algorithm than client requested algorithm");
}
}
String encryptionEncAlg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel).getRequestObjectEncryptionEnc();
if (encryptionEncAlg != null) {
JWE jwe = (JWE) jwt;
JWEHeader header = (JWEHeader) jwe.getHeader();
if (!encryptionEncAlg.equals(header.getEncryptionAlgorithm())) {
throw new RuntimeException("Request object content encrypted with different algorithm than client requested algorithm");
}
}
session.setAttribute(AuthzEndpointRequestParser.AUTHZ_REQUEST_OBJECT_ENCRYPTED, jwt);
}
};
}
@Override @Override
protected <T> T replaceIfNotNull(T previousVal, T newVal) { protected <T> T replaceIfNotNull(T previousVal, T newVal) {
// force parameters values from request object as per spec any parameter set directly should be ignored // force parameters values from request object as per spec any parameter set directly should be ignored

View file

@ -47,6 +47,7 @@ public abstract class AuthzEndpointRequestParser {
public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 200; public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 200;
public static final String AUTHZ_REQUEST_OBJECT = "ParsedRequestObject"; public static final String AUTHZ_REQUEST_OBJECT = "ParsedRequestObject";
public static final String AUTHZ_REQUEST_OBJECT_ENCRYPTED = "EncryptedRequestObject";
/** Set of known protocol GET params not to be stored into additionalReqParams} */ /** Set of known protocol GET params not to be stored into additionalReqParams} */
public static final Set<String> KNOWN_REQ_PARAMS = new HashSet<>(); public static final Set<String> KNOWN_REQ_PARAMS = new HashSet<>();

View file

@ -63,6 +63,7 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
configuration = new Configuration(); configuration = new Configuration();
configuration.setVerifyNbf(Boolean.TRUE); configuration.setVerifyNbf(Boolean.TRUE);
configuration.setAvailablePeriod(DEFAULT_AVAILABLE_PERIOD); configuration.setAvailablePeriod(DEFAULT_AVAILABLE_PERIOD);
configuration.setEncryptionRequired(Boolean.FALSE);
} else { } else {
configuration = config; configuration = config;
if (config.isVerifyNbf() == null) { if (config.isVerifyNbf() == null) {
@ -71,6 +72,9 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
if (config.getAvailablePeriod() == null) { if (config.getAvailablePeriod() == null) {
configuration.setAvailablePeriod(DEFAULT_AVAILABLE_PERIOD); configuration.setAvailablePeriod(DEFAULT_AVAILABLE_PERIOD);
} }
if (config.isEncryptionRequired() == null) {
configuration.setEncryptionRequired(Boolean.FALSE);
}
} }
} }
@ -84,6 +88,8 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
protected Integer availablePeriod; protected Integer availablePeriod;
@JsonProperty("verify-nbf") @JsonProperty("verify-nbf")
protected Boolean verifyNbf; protected Boolean verifyNbf;
@JsonProperty(SecureRequestObjectExecutorFactory.ENCRYPTION_REQUIRED)
private Boolean encryptionRequired;
public Integer getAvailablePeriod() { public Integer getAvailablePeriod() {
return availablePeriod; return availablePeriod;
@ -100,6 +106,14 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
public void setVerifyNbf(Boolean verifyNbf) { public void setVerifyNbf(Boolean verifyNbf) {
this.verifyNbf = verifyNbf; this.verifyNbf = verifyNbf;
} }
public void setEncryptionRequired(Boolean encryptionRequired) {
this.encryptionRequired = encryptionRequired;
}
public Boolean isEncryptionRequired() {
return encryptionRequired;
}
} }
@Override @Override
@ -229,6 +243,12 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "Invalid parameter. Parameters in 'request' object not matching with request parameters"); throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "Invalid parameter. Parameters in 'request' object not matching with request parameters");
} }
Boolean encryptionRequired = Optional.ofNullable(configuration.isEncryptionRequired()).orElse(Boolean.FALSE);
if (encryptionRequired && session.getAttribute(AuthzEndpointRequestParser.AUTHZ_REQUEST_OBJECT_ENCRYPTED) == null) {
logger.trace("request object's not encrypted.");
throw new ClientPolicyException(INVALID_REQUEST_OBJECT, "Request object not encrypted");
}
logger.trace("Passed."); logger.trace("Passed.");
} }

View file

@ -41,11 +41,16 @@ public class SecureRequestObjectExecutorFactory implements ClientPolicyExecutorP
"claim and this claim will be validated", ProviderConfigProperty.BOOLEAN_TYPE, true); "claim and this claim will be validated", ProviderConfigProperty.BOOLEAN_TYPE, true);
public static final String AVAILABLE_PERIOD = "available-period"; public static final String AVAILABLE_PERIOD = "available-period";
public static final String ENCRYPTION_REQUIRED = "encryption-required";
private static final ProviderConfigProperty AVAILABLE_PERIOD_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty AVAILABLE_PERIOD_PROPERTY = new ProviderConfigProperty(
AVAILABLE_PERIOD, "Available Period", "The maximum period in seconds for which the 'request' object used in OIDC authorization request is considered valid. " + AVAILABLE_PERIOD, "Available Period", "The maximum period in seconds for which the 'request' object used in OIDC authorization request is considered valid. " +
"It is used if 'Verify Not-Before' is ON.", ProviderConfigProperty.STRING_TYPE, "3600"); "It is used if 'Verify Not-Before' is ON.", ProviderConfigProperty.STRING_TYPE, "3600");
private static final ProviderConfigProperty ENCRYPTION_REQUIRED_PROPERTY = new ProviderConfigProperty(
ENCRYPTION_REQUIRED, "Encryption Required", "Whether request object encryption is required. If enabled, request objects must be encrypted. Otherwise, encryption is optional.",
ProviderConfigProperty.BOOLEAN_TYPE, Boolean.FALSE);
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {
return new SecureRequestObjectExecutor(session); return new SecureRequestObjectExecutor(session);
@ -75,7 +80,7 @@ public class SecureRequestObjectExecutorFactory implements ClientPolicyExecutorP
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return new ArrayList<>(Arrays.asList(VERIFY_NBF_PROPERTY, AVAILABLE_PERIOD_PROPERTY)); return new ArrayList<>(Arrays.asList(VERIFY_NBF_PROPERTY, AVAILABLE_PERIOD_PROPERTY, ENCRYPTION_REQUIRED_PROPERTY));
} }
} }

View file

@ -320,6 +320,12 @@ public class DescriptionConverter {
if (config.getRequestObjectSignatureAlg() != null) { if (config.getRequestObjectSignatureAlg() != null) {
response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString()); response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString());
} }
if (config.getRequestObjectEncryptionAlg() != null) {
response.setRequestObjectEncryptionAlg(config.getRequestObjectEncryptionAlg());
}
if (config.getRequestObjectEncryptionEnc() != null) {
response.setRequestObjectEncryptionEnc(config.getRequestObjectEncryptionEnc());
}
if (config.isUseJwksUrl()) { if (config.isUseJwksUrl()) {
response.setJwksUri(config.getJwksUrl()); response.setJwksUri(config.getJwksUrl());
} }

View file

@ -1168,7 +1168,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
} }
@Test @Test
public void testSecureRequestObjectExecutor() throws Exception, URISyntaxException, IOException { public void testSecureRequestObjectExecutor() throws Exception {
Integer availablePeriod = Integer.valueOf(SecureRequestObjectExecutor.DEFAULT_AVAILABLE_PERIOD + 400); Integer availablePeriod = Integer.valueOf(SecureRequestObjectExecutor.DEFAULT_AVAILABLE_PERIOD + 400);
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
@ -1362,6 +1362,19 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
registerRequestObject(requestObject, clientId, Algorithm.ES256, false); registerRequestObject(requestObject, clientId, Algorithm.ES256, false);
successfulLoginAndLogout(clientId, clientSecret); successfulLoginAndLogout(clientId, clientSecret);
// update profile : force request object encryption
json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil")
.addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, createSecureRequestObjectExecutorConfig(null, null, true))
.toRepresentation()
).toString();
updateProfiles(json);
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
registerRequestObject(requestObject, clientId, Algorithm.ES256, false);
oauth.openLoginForm();
assertEquals(SecureRequestObjectExecutor.INVALID_REQUEST_OBJECT, oauth.getCurrentQuery().get(OAuth2Constants.ERROR));
assertEquals("Request object not encrypted", oauth.getCurrentQuery().get(OAuth2Constants.ERROR_DESCRIPTION));
} }
@Test @Test

View file

@ -105,6 +105,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.jose.jwe.JWEConstants.RSA_OAEP; import static org.keycloak.jose.jwe.JWEConstants.RSA_OAEP;
import static org.keycloak.jose.jwe.JWEConstants.RSA_OAEP_256; import static org.keycloak.jose.jwe.JWEConstants.RSA_OAEP_256;
import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
@ -1296,8 +1297,84 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
events.expectLogin().assertEvent(); events.expectLogin().assertEvent();
} }
@Test
public void testWrongEncryptionAlgorithm() throws Exception {
try {
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm(oauth.getRealm()), oauth.getClientId());
ClientRepresentation clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionAlg(RSA_OAEP_256);
clientResource.update(clientRep);
oauth.request(createEncryptedRequestObject(RSA_OAEP));
oauth.doLogin("test-user@localhost", "password");
fail("Should fail due to invalid encryption algorithm");
} catch (Exception ignore) {
assertTrue(errorPage.isCurrent());
oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
oauth.doLogin("test-user@localhost", "password");
assertTrue(appPage.isCurrent());
} finally {
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm(oauth.getRealm()), oauth.getClientId());
ClientRepresentation clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionAlg(null);
clientResource.update(clientRep);
}
oauth.openLogout();
oauth = oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
oauth.doLogin("test-user@localhost", "password");
assertTrue(appPage.isCurrent());
}
@Test
public void testWrongContentEncryptionAlgorithm() throws Exception {
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm(oauth.getRealm()), oauth.getClientId());
ClientRepresentation clientRep = clientResource.toRepresentation();
try {
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionAlg(RSA_OAEP_256);
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionEnc(JWEConstants.A192GCM);
clientResource.update(clientRep);
clientRep = clientResource.toRepresentation();
assertEquals(JWEConstants.A192GCM, OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).getRequestObjectEncryptionEnc());
oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
oauth.doLogin("test-user@localhost", "password");
fail("Should fail due to invalid content encryption algorithm");
} catch (Exception ignore) {
assertTrue(errorPage.isCurrent());
oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionEnc(JWEConstants.A256GCM);
clientResource.update(clientRep);
clientRep = clientResource.toRepresentation();
assertEquals(JWEConstants.A256GCM, OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).getRequestObjectEncryptionEnc());
oauth.doLogin("test-user@localhost", "password");
assertTrue(appPage.isCurrent());
} finally {
clientResource = ApiUtil.findClientByClientId(adminClient.realm(oauth.getRealm()), oauth.getClientId());
clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionAlg(null);
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectEncryptionEnc(null);
clientResource.update(clientRep);
}
oauth.openLogout();
oauth = oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
oauth.doLogin("test-user@localhost", "password");
assertTrue(appPage.isCurrent());
clientRep = clientResource.toRepresentation();
assertNull(OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).getRequestObjectEncryptionAlg());
assertNull(OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).getRequestObjectEncryptionEnc());
}
@Test @Test
public void testSignedAndEncryptedRequestObject() throws IOException, JWEException { public void testSignedAndEncryptedRequestObject() throws IOException, JWEException {
oauth = oauth.request(createEncryptedRequestObject(RSA_OAEP_256));
oauth.doLogin("test-user@localhost", "password");
events.expectLogin().assertEvent();
}
private String createEncryptedRequestObject(String encAlg) throws IOException, JWEException {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
OIDCConfigurationRepresentation representation = SimpleHttp OIDCConfigurationRepresentation representation = SimpleHttp
.doGet(getAuthServerRoot().toString() + "realms/" + oauth.getRealm() + "/.well-known/openid-configuration", .doGet(getAuthServerRoot().toString() + "realms/" + oauth.getRealm() + "/.well-known/openid-configuration",
@ -1308,22 +1385,21 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
String keyId = null; String keyId = null;
if (keyId == null) { if (keyId == null) {
KeysMetadataRepresentation.KeyMetadataRepresentation encKey = KeyUtils.getActiveEncKey(testRealm().keys().getKeyMetadata(), KeysMetadataRepresentation.KeyMetadataRepresentation encKey = KeyUtils
org.keycloak.crypto.Algorithm.PS256); .getActiveEncKey(testRealm().keys().getKeyMetadata(),
org.keycloak.crypto.Algorithm.PS256);
keyId = encKey.getKid(); keyId = encKey.getKid();
} }
PublicKey decryptionKEK = keysForUse.get(keyId); PublicKey decryptionKEK = keysForUse.get(keyId);
JWE jwe = new JWE() JWE jwe = new JWE()
.header(new JWEHeader(RSA_OAEP_256, JWEConstants.A256GCM, null)) .header(new JWEHeader(encAlg, JWEConstants.A256GCM, null))
.content(createAndSignRequestObject().getBytes()); .content(createAndSignRequestObject().getBytes());
jwe.getKeyStorage() jwe.getKeyStorage()
.setEncryptionKey(decryptionKEK); .setEncryptionKey(decryptionKEK);
oauth = oauth.request(jwe.encodeJwe()); return jwe.encodeJwe();
oauth.doLogin("test-user@localhost", "password");
events.expectLogin().assertEvent();
} }
} }

View file

@ -168,9 +168,14 @@ public final class ClientPoliciesUtil {
} }
public static SecureRequestObjectExecutor.Configuration createSecureRequestObjectExecutorConfig(Integer availablePeriod, Boolean verifyNbf) { public static SecureRequestObjectExecutor.Configuration createSecureRequestObjectExecutorConfig(Integer availablePeriod, Boolean verifyNbf) {
return createSecureRequestObjectExecutorConfig(availablePeriod, verifyNbf, false);
}
public static SecureRequestObjectExecutor.Configuration createSecureRequestObjectExecutorConfig(Integer availablePeriod, Boolean verifyNbf, Boolean encryptionRequired) {
SecureRequestObjectExecutor.Configuration config = new SecureRequestObjectExecutor.Configuration(); SecureRequestObjectExecutor.Configuration config = new SecureRequestObjectExecutor.Configuration();
if (availablePeriod != null) config.setAvailablePeriod(availablePeriod); if (availablePeriod != null) config.setAvailablePeriod(availablePeriod);
if (verifyNbf != null) config.setVerifyNbf(verifyNbf); if (verifyNbf != null) config.setVerifyNbf(verifyNbf);
if (encryptionRequired != null) config.setEncryptionRequired(encryptionRequired);
return config; return config;
} }

View file

@ -403,6 +403,10 @@ 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', 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', Request object can be signed by any algorithm (including 'none' ).
request-object-required=Request Object Required request-object-required=Request Object Required
request-object-required.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. request-object-required.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.
request-object-encryption-alg=Request Object Encryption Algorithm
request-object-encryption-alg.tooltip=JWE algorithm, which client needs to use when sending OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', encryption is optional and any algorithm is allowed.
request-object-encryption-enc=Request Object Content Encryption Algorithm
request-object-encryption-enc.tooltip=JWE algorithm, which client needs to use when encrypting the content of the OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', any algorithm is allowed.
ciba-backchannel-auth-request-signing-alg=CIBA Backchannel Authentication Request Signature Algorithm ciba-backchannel-auth-request-signing-alg=CIBA Backchannel Authentication Request Signature Algorithm
ciba-backchannel-auth-request-signing-alg.tooltip=JWA algorithm, which client needs to use when sending CIBA backchannel authentication request specified by 'request' or 'request_uri' parameters. ciba-backchannel-auth-request-signing-alg.tooltip=JWA algorithm, which client needs to use when sending CIBA backchannel authentication request specified by 'request' or 'request_uri' parameters.
request-uris=Valid Request URIs request-uris=Valid Request URIs

View file

@ -1314,6 +1314,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro
var attrVal5 = $scope.client.attributes['ciba.backchannel.auth.request.signing.alg']; var attrVal5 = $scope.client.attributes['ciba.backchannel.auth.request.signing.alg'];
$scope.cibaBackchannelAuthRequestSigningAlg = attrVal5==null ? 'none' : attrVal5; $scope.cibaBackchannelAuthRequestSigningAlg = attrVal5==null ? 'none' : attrVal5;
var attrVal6 = $scope.client.attributes['request.object.encryption.alg'];
$scope.requestObjectEncryptionAlg = attrVal6==null ? 'any' : attrVal6;
var attrVal7 = $scope.client.attributes['request.object.encryption.enc'];
$scope.requestObjectEncryptionEnc = attrVal7==null ? 'any' : attrVal7;
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;
@ -1527,6 +1533,22 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro
} }
}; };
$scope.changeRequestObjectEncryptionAlg = function() {
if ($scope.requestObjectEncryptionAlg === 'any') {
$scope.clientEdit.attributes['request.object.encryption.alg'] = null;
} else {
$scope.clientEdit.attributes['request.object.encryption.alg'] = $scope.requestObjectEncryptionAlg;
}
};
$scope.changeRequestObjectEncryptionEnc = function() {
if ($scope.requestObjectEncryptionEnc === 'any') {
$scope.clientEdit.attributes['request.object.encryption.enc'] = null;
} else {
$scope.clientEdit.attributes['request.object.encryption.enc'] = $scope.requestObjectEncryptionEnc;
}
};
$scope.changePkceCodeChallengeMethod = function() { $scope.changePkceCodeChallengeMethod = function() {
$scope.clientEdit.attributes['pkce.code.challenge.method'] = $scope.pkceCodeChallengeMethod; $scope.clientEdit.attributes['pkce.code.challenge.method'] = $scope.pkceCodeChallengeMethod;
}; };

View file

@ -559,6 +559,34 @@
</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="requestObjectEncryptionAlg">{{:: 'request-object-encryption-alg' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="requestObjectEncryptionAlg"
ng-change="changeRequestObjectEncryptionAlg()"
ng-model="requestObjectEncryptionAlg">
<option value="any">any</option>
<option ng-repeat="provider in serverInfo.listProviderIds('cekmanagement')" value="{{provider}}">{{provider}}</option>
</select>
</div>
</div>
<kc-tooltip>{{:: 'request-object-encryption-alg.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'openid-connect'">
<label class="col-md-2 control-label" for="requestObjectEncryptionEnc">{{:: 'request-object-encryption-enc' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="requestObjectEncryptionEnc"
ng-change="changeRequestObjectEncryptionEnc()"
ng-model="requestObjectEncryptionEnc">
<option value="any">any</option>
<option ng-repeat="provider in serverInfo.listProviderIds('contentencryption')" value="{{provider}}">{{provider}}</option>
</select>
</div>
</div>
<kc-tooltip>{{:: 'request-object-encryption-enc.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'openid-connect'"> <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> <label class="col-md-2 control-label" for="changeRequestObjectRequired">{{:: 'request-object-required' | translate}}</label>
<div class="col-sm-6"> <div class="col-sm-6">