KEYCLOAK-18833 FAPI-CIBA-ID1 : need to only accept confidential client on Backchannel Authentication endpoint
This commit is contained in:
parent
6bd7420907
commit
84e19f1c57
2 changed files with 76 additions and 1 deletions
|
@ -45,7 +45,9 @@ public class ConfidentialClientAcceptExecutor implements ClientPolicyExecutorPro
|
||||||
switch (context.getEvent()) {
|
switch (context.getEvent()) {
|
||||||
case AUTHORIZATION_REQUEST:
|
case AUTHORIZATION_REQUEST:
|
||||||
case TOKEN_REQUEST:
|
case TOKEN_REQUEST:
|
||||||
checkIsConfidentialClient();
|
case BACKCHANNEL_AUTHENTICATION_REQUEST:
|
||||||
|
case BACKCHANNEL_TOKEN_REQUEST:
|
||||||
|
checkIsConfidentialClient();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -94,6 +94,7 @@ import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||||
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
|
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
|
||||||
import org.keycloak.services.clientpolicy.condition.ClientRolesConditionFactory;
|
import org.keycloak.services.clientpolicy.condition.ClientRolesConditionFactory;
|
||||||
import org.keycloak.services.clientpolicy.condition.ClientUpdaterContextConditionFactory;
|
import org.keycloak.services.clientpolicy.condition.ClientUpdaterContextConditionFactory;
|
||||||
|
import org.keycloak.services.clientpolicy.executor.ConfidentialClientAcceptExecutorFactory;
|
||||||
import org.keycloak.services.clientpolicy.executor.HolderOfKeyEnforcerExecutorFactory;
|
import org.keycloak.services.clientpolicy.executor.HolderOfKeyEnforcerExecutorFactory;
|
||||||
import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtExecutorFactory;
|
import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtExecutorFactory;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
@ -1862,6 +1863,78 @@ public class CIBATest extends AbstractClientPoliciesTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConfidentialClientAcceptExecutorExecutor() throws Exception {
|
||||||
|
String clientPublicId = generateSuffixedName("public-app");
|
||||||
|
String cidPublic = createClientByAdmin(clientPublicId, (ClientRepresentation clientRep) -> {
|
||||||
|
clientRep.setSecret("app-secret");
|
||||||
|
clientRep.setStandardFlowEnabled(Boolean.TRUE);
|
||||||
|
clientRep.setImplicitFlowEnabled(Boolean.TRUE);
|
||||||
|
clientRep.setPublicClient(Boolean.TRUE);
|
||||||
|
clientRep.setBearerOnly(Boolean.FALSE);
|
||||||
|
Map<String, String> attributes = Optional.ofNullable(clientRep.getAttributes()).orElse(new HashMap<>());
|
||||||
|
attributes.put(CibaConfig.CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE_PER_CLIENT, "poll");
|
||||||
|
attributes.put(CibaConfig.OIDC_CIBA_GRANT_ENABLED, Boolean.TRUE.toString());
|
||||||
|
clientRep.setAttributes(attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
String username = "nutzername-rot";
|
||||||
|
Map<String, String> additionalParameters = new HashMap<>();
|
||||||
|
additionalParameters.put("user_device", "mobile");
|
||||||
|
String bindingMessage = "bmbmbmbm";
|
||||||
|
|
||||||
|
// register profiles
|
||||||
|
String json = (new ClientProfilesBuilder()).addProfile(
|
||||||
|
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Erstes Profil")
|
||||||
|
.addExecutor(ConfidentialClientAcceptExecutorFactory.PROVIDER_ID, null)
|
||||||
|
.toRepresentation()
|
||||||
|
).toString();
|
||||||
|
updateProfiles(json);
|
||||||
|
|
||||||
|
// register policies
|
||||||
|
json = (new ClientPoliciesBuilder()).addPolicy(
|
||||||
|
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
|
||||||
|
.addCondition(AnyClientConditionFactory.PROVIDER_ID,
|
||||||
|
createAnyClientConditionConfig())
|
||||||
|
.addProfile(PROFILE_NAME)
|
||||||
|
.toRepresentation()
|
||||||
|
).toString();
|
||||||
|
updatePolicies(json);
|
||||||
|
|
||||||
|
// user Backchannel Authentication Request
|
||||||
|
AuthenticationRequestAcknowledgement response = oauth.doBackchannelAuthenticationRequest(clientPublicId, "app-secret", username, bindingMessage, null, additionalParameters);
|
||||||
|
assertThat(response.getStatusCode(), is(equalTo(400)));
|
||||||
|
assertThat(response.getError(), is(OAuthErrorException.INVALID_CLIENT));
|
||||||
|
assertThat(response.getErrorDescription(), is("invalid client access type"));
|
||||||
|
|
||||||
|
String clientConfidentialId = generateSuffixedName("confidential-app");
|
||||||
|
String clientConfidentialSecret = "app-secret";
|
||||||
|
String cidConfidential = createClientByAdmin(clientConfidentialId, (ClientRepresentation clientRep) -> {
|
||||||
|
clientRep.setSecret(clientConfidentialSecret);
|
||||||
|
clientRep.setStandardFlowEnabled(Boolean.TRUE);
|
||||||
|
clientRep.setImplicitFlowEnabled(Boolean.TRUE);
|
||||||
|
clientRep.setPublicClient(Boolean.FALSE);
|
||||||
|
clientRep.setBearerOnly(Boolean.FALSE);
|
||||||
|
Map<String, String> attributes = Optional.ofNullable(clientRep.getAttributes()).orElse(new HashMap<>());
|
||||||
|
attributes.put(CibaConfig.CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE_PER_CLIENT, "poll");
|
||||||
|
attributes.put(CibaConfig.OIDC_CIBA_GRANT_ENABLED, Boolean.TRUE.toString());
|
||||||
|
clientRep.setAttributes(attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
// user Backchannel Authentication Request
|
||||||
|
response = doBackchannelAuthenticationRequest(clientConfidentialId, clientConfidentialSecret, username, bindingMessage, additionalParameters);
|
||||||
|
|
||||||
|
updateClientByAdmin(cidConfidential, (ClientRepresentation cRep) -> {
|
||||||
|
cRep.setPublicClient(Boolean.TRUE);
|
||||||
|
});
|
||||||
|
|
||||||
|
// user Token Request
|
||||||
|
OAuthClient.AccessTokenResponse tokenRes = oauth.doBackchannelAuthenticationTokenRequest(clientConfidentialId, clientConfidentialSecret, response.getAuthReqId());
|
||||||
|
assertThat(tokenRes.getStatusCode(), is(equalTo(400)));
|
||||||
|
assertThat(tokenRes.getError(), is(OAuthErrorException.INVALID_GRANT));
|
||||||
|
assertThat(tokenRes.getErrorDescription(), is("invalid client access type"));
|
||||||
|
}
|
||||||
|
|
||||||
private void testBackchannelAuthenticationFlowNotRegisterSigAlgInAdvanceWithSignedAuthentication(String clientName, boolean useRequestUri, String requestedSigAlg, String sigAlg, int statusCode, String errorDescription) throws Exception {
|
private void testBackchannelAuthenticationFlowNotRegisterSigAlgInAdvanceWithSignedAuthentication(String clientName, boolean useRequestUri, String requestedSigAlg, String sigAlg, int statusCode, String errorDescription) throws Exception {
|
||||||
String clientId = createClientDynamically(clientName, (OIDCClientRepresentation clientRep) -> {
|
String clientId = createClientDynamically(clientName, (OIDCClientRepresentation clientRep) -> {
|
||||||
List<String> grantTypes = Optional.ofNullable(clientRep.getGrantTypes()).orElse(new ArrayList<>());
|
List<String> grantTypes = Optional.ofNullable(clientRep.getGrantTypes()).orElse(new ArrayList<>());
|
||||||
|
|
Loading…
Reference in a new issue