KEYCLOAK-18833 FAPI-CIBA-ID1 : need to only accept confidential client on Backchannel Authentication endpoint

This commit is contained in:
Takashi Norimatsu 2021-07-23 03:04:50 +09:00 committed by Marek Posolda
parent 6bd7420907
commit 84e19f1c57
2 changed files with 76 additions and 1 deletions

View file

@ -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;

View file

@ -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<>());