diff --git a/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java b/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java
index 5f497a8436..d473282994 100644
--- a/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java
+++ b/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java
@@ -36,6 +36,7 @@ public enum ClientPolicyEvent {
TOKEN_REVOKE,
TOKEN_INTROSPECT,
USERINFO_REQUEST,
- LOGOUT_REQUEST
+ LOGOUT_REQUEST,
+ BACKCHANNEL_AUTHENTICATION_REQUEST
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java
index 97446fd07b..b51a403f34 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java
@@ -38,6 +38,8 @@ import org.keycloak.protocol.oidc.grants.ciba.endpoints.request.BackchannelAuthe
import org.keycloak.protocol.oidc.grants.ciba.endpoints.request.BackchannelAuthenticationEndpointRequestParserProcessor;
import org.keycloak.protocol.oidc.grants.ciba.resolvers.CIBALoginUserResolver;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.clientpolicy.ClientPolicyException;
+import org.keycloak.services.clientpolicy.context.BackchannelAuthenticationRequestContext;
import org.keycloak.util.JsonSerialization;
import javax.ws.rs.Consumes;
@@ -185,6 +187,12 @@ public class BackchannelAuthenticationEndpoint extends AbstractCibaEndpoint {
extractAdditionalParams(endpointRequest, request);
+ try {
+ session.clientPolicy().triggerOnEvent(new BackchannelAuthenticationRequestContext(endpointRequest, params));
+ } catch (ClientPolicyException cpe) {
+ throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
+ }
+
return request;
}
diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/BackchannelAuthenticationRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/BackchannelAuthenticationRequestContext.java
new file mode 100644
index 0000000000..97d888b1a9
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/BackchannelAuthenticationRequestContext.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.clientpolicy.context;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.keycloak.protocol.oidc.grants.ciba.endpoints.request.BackchannelAuthenticationEndpointRequest;
+import org.keycloak.services.clientpolicy.ClientPolicyContext;
+import org.keycloak.services.clientpolicy.ClientPolicyEvent;
+
+/**
+ * @author Takashi Norimatsu
+ */
+public class BackchannelAuthenticationRequestContext implements ClientPolicyContext {
+
+ private final BackchannelAuthenticationEndpointRequest request;
+ private final MultivaluedMap requestParameters;
+
+ public BackchannelAuthenticationRequestContext(BackchannelAuthenticationEndpointRequest request,
+ MultivaluedMap requestParameters) {
+ this.request = request;
+ this.requestParameters = requestParameters;
+ }
+
+ @Override
+ public ClientPolicyEvent getEvent() {
+ return ClientPolicyEvent.BACKCHANNEL_AUTHENTICATION_REQUEST;
+ }
+
+ public BackchannelAuthenticationEndpointRequest getRequest() {
+ return request;
+ }
+
+ public MultivaluedMap getRequestParameters() {
+ return requestParameters;
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java
index f67d5ebb38..4b1d9876eb 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java
@@ -51,6 +51,7 @@ public class TestRaiseExeptionExecutor implements ClientPolicyExecutorProvider {
+ clientRep.setSecret(clientSecret);
+ clientRep.setStandardFlowEnabled(Boolean.TRUE);
+ clientRep.setImplicitFlowEnabled(Boolean.TRUE);
+ clientRep.setPublicClient(Boolean.FALSE);
+ clientRep.setBearerOnly(Boolean.FALSE);
+ Map 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);
+ });
+
+ // register profiles
+ String json = (new ClientProfilesBuilder()).addProfile(
+ (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen")
+ .addExecutor(TestRaiseExeptionExecutorFactory.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);
+
+ AuthenticationRequestAcknowledgement response = oauth.doBackchannelAuthenticationRequest(clientId, clientSecret, TEST_USER_NAME, "Pjb9eD8w", null, null);
+ assertEquals(400, response.getStatusCode());
+ assertEquals(ClientPolicyEvent.BACKCHANNEL_AUTHENTICATION_REQUEST.toString(), response.getError());
+ assertEquals("Exception thrown intentionally", response.getErrorDescription());
+ }
+
private void checkMtlsFlow() throws IOException {
// Check login.
OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);