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 90c270164c..3224f8ae33 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
@@ -40,6 +40,7 @@ public enum ClientPolicyEvent {
TOKEN_REFRESH,
TOKEN_REFRESH_RESPONSE,
TOKEN_REVOKE,
+ TOKEN_REVOKE_RESPONSE,
TOKEN_INTROSPECT,
USERINFO_REQUEST,
LOGOUT_REQUEST,
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index b8a6e89601..531e417e17 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -1018,6 +1018,8 @@ public class TokenManager {
String stateHash;
+ private AccessTokenResponse response;
+
public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
this.realm = realm;
@@ -1152,6 +1154,8 @@ public class TokenManager {
}
public AccessTokenResponse build() {
+ if (response != null) return response;
+
if (accessToken != null) {
event.detail(Details.TOKEN_ID, accessToken.getId());
}
@@ -1214,7 +1218,8 @@ public class TokenManager {
res.setScope(responseScope);
event.detail(Details.SCOPE, responseScope);
- return res;
+ response = res;
+ return response;
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 766f81790f..6df51ad1df 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -512,6 +512,7 @@ public class LogoutEndpoint {
try {
session.clientPolicy().triggerOnEvent(new LogoutRequestContext(form));
+ refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
} catch (ClientPolicyException cpe) {
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 1cd7fcaf95..7ed8101b53 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -497,6 +497,7 @@ public class TokenEndpoint {
} else {
res = responseBuilder.build();
}
+
event.success();
return cors.builder(Response.ok(res).type(MediaType.APPLICATION_JSON_TYPE)).build();
@@ -528,6 +529,7 @@ public class TokenEndpoint {
try {
session.clientPolicy().triggerOnEvent(new TokenRefreshContext(formParams));
+ refreshToken = formParams.getFirst(OAuth2Constants.REFRESH_TOKEN);
} catch (ClientPolicyException cpe) {
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
index 802642725f..8af3e13e8e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
@@ -46,8 +46,8 @@ import jakarta.ws.rs.core.Response.Status;
*/
public class TokenIntrospectionEndpoint {
- private static final String PARAM_TOKEN_TYPE_HINT = "token_type_hint";
- private static final String PARAM_TOKEN = "token";
+ public static final String PARAM_TOKEN_TYPE_HINT = "token_type_hint";
+ public static final String PARAM_TOKEN = "token";
private final KeycloakSession session;
@@ -100,6 +100,7 @@ public class TokenIntrospectionEndpoint {
try {
session.clientPolicy().triggerOnEvent(new TokenIntrospectContext(formParams));
+ token = formParams.getFirst(PARAM_TOKEN);
} catch (ClientPolicyException cpe) {
throw throwErrorResponseException(Errors.INVALID_REQUEST, cpe.getErrorDetail(), Status.BAD_REQUEST);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java
index 66694337fa..ef433ac096 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java
@@ -50,6 +50,7 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.TokenRevokeContext;
+import org.keycloak.services.clientpolicy.context.TokenRevokeResponseContext;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
@@ -59,7 +60,7 @@ import org.keycloak.util.TokenUtil;
* @author Yoshiyuki Tabata
*/
public class TokenRevocationEndpoint {
- private static final String PARAM_TOKEN = "token";
+ public static final String PARAM_TOKEN = "token";
private final KeycloakSession session;
@@ -120,6 +121,13 @@ public class TokenRevocationEndpoint {
event.success();
+ try {
+ session.clientPolicy().triggerOnEvent(new TokenRevokeResponseContext(formParams));
+ } catch (ClientPolicyException cpe) {
+ event.error(cpe.getError());
+ throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
+ }
+
session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
return cors.builder(Response.ok()).build();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index 9e3f328364..406d75cb74 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -100,7 +100,7 @@ public class UserInfoEndpoint {
private final RealmModel realm;
private final OAuth2Error error;
private Cors cors;
- private String authorization;
+ private TokenForUserInfo tokenForUserInfo = new TokenForUserInfo();
public UserInfoEndpoint(KeycloakSession session, org.keycloak.protocol.oidc.TokenManager tokenManager) {
this.session = session;
@@ -163,7 +163,7 @@ public class UserInfoEndpoint {
cors.allowAllOrigins();
try {
- session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(authorization));
+ session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(tokenForUserInfo));
} catch (ClientPolicyException cpe) {
throw error.error(cpe.getError()).errorDescription(cpe.getErrorDetail()).status(cpe.getErrorStatus()).build();
}
@@ -172,7 +172,7 @@ public class UserInfoEndpoint {
.event(EventType.USER_INFO_REQUEST)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
- if (authorization == null) {
+ if (tokenForUserInfo.getToken() == null) {
event.error(Errors.INVALID_TOKEN);
throw error.unauthorized();
}
@@ -180,7 +180,7 @@ public class UserInfoEndpoint {
AccessToken token;
ClientModel clientModel = null;
try {
- TokenVerifier verifier = TokenVerifier.create(authorization, AccessToken.class).withDefaultChecks()
+ TokenVerifier verifier = TokenVerifier.create(tokenForUserInfo.getToken(), AccessToken.class).withDefaultChecks()
.realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
@@ -417,11 +417,24 @@ public class UserInfoEndpoint {
private void authorization(String accessToken) {
if (accessToken != null) {
- if (authorization == null) {
- authorization = accessToken;
+ if (tokenForUserInfo.getToken() == null) {
+ tokenForUserInfo.setToken(accessToken);
} else {
throw error.cors(cors.allowAllOrigins()).invalidRequest("More than one method used for including an access token");
}
}
}
+
+ public static class TokenForUserInfo {
+
+ private String token;
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java
index 435f0feb29..9e2f8fe7d7 100644
--- a/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java
+++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java
@@ -17,7 +17,6 @@
package org.keycloak.services.clientpolicy.context;
-import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import org.keycloak.services.clientpolicy.ClientPolicyContext;
diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeResponseContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeResponseContext.java
new file mode 100644
index 0000000000..139e474d3a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeResponseContext.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 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 jakarta.ws.rs.core.MultivaluedMap;
+
+import org.keycloak.services.clientpolicy.ClientPolicyContext;
+import org.keycloak.services.clientpolicy.ClientPolicyEvent;
+
+/**
+ * @author Takashi Norimatsu
+ */
+public class TokenRevokeResponseContext implements ClientPolicyContext {
+
+ private final MultivaluedMap params;
+
+ public TokenRevokeResponseContext(MultivaluedMap params) {
+ this.params = params;
+ }
+
+ @Override
+ public ClientPolicyEvent getEvent() {
+ return ClientPolicyEvent.TOKEN_REVOKE_RESPONSE;
+ }
+
+ public MultivaluedMap getParams() {
+ return params;
+ }
+
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java
index 7c9f2c1c8d..c74972d0ca 100644
--- a/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java
+++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java
@@ -17,6 +17,7 @@
package org.keycloak.services.clientpolicy.context;
+import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint;
import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyEvent;
@@ -25,10 +26,10 @@ import org.keycloak.services.clientpolicy.ClientPolicyEvent;
*/
public class UserInfoRequestContext implements ClientPolicyContext {
- private final String tokenString;
+ private UserInfoEndpoint.TokenForUserInfo tokenForUserInfo;
- public UserInfoRequestContext(String tokenString) {
- this.tokenString = tokenString;
+ public UserInfoRequestContext(UserInfoEndpoint.TokenForUserInfo tokenForUserInfo) {
+ this.tokenForUserInfo = tokenForUserInfo;
}
@Override
@@ -36,8 +37,8 @@ public class UserInfoRequestContext implements ClientPolicyContext {
return ClientPolicyEvent.USERINFO_REQUEST;
}
- public String getTokenString() {
- return tokenString;
+ public UserInfoEndpoint.TokenForUserInfo getTokenForUserInfo() {
+ return tokenForUserInfo;
}
}
diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforcerExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforcerExecutor.java
index 0ff2f3d250..6a4c06863b 100644
--- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforcerExecutor.java
+++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforcerExecutor.java
@@ -143,7 +143,7 @@ public class HolderOfKeyEnforcerExecutor implements ClientPolicyExecutorProvider
}
private void checkUserInfo(UserInfoRequestContext context, HttpRequest request) throws ClientPolicyException {
- String encodedAccessToken = context.getTokenString();
+ String encodedAccessToken = context.getTokenForUserInfo().getToken();
AccessToken accessToken = session.tokens().decode(encodedAccessToken, AccessToken.class);
if (accessToken == null) {
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutor.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutor.java
new file mode 100644
index 0000000000..d1f3f1f149
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 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.testsuite.services.clientpolicy.executor;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
+import org.keycloak.services.clientpolicy.ClientPolicyContext;
+import org.keycloak.services.clientpolicy.ClientPolicyEvent;
+import org.keycloak.services.clientpolicy.ClientPolicyException;
+import org.keycloak.services.clientpolicy.context.TokenResponseContext;
+import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
+
+/**
+ * @author Takashi Norimatsu
+ */
+public class TestEnhancedPluggableTokenManagerExecutor implements ClientPolicyExecutorProvider {
+
+ private static final Logger logger = Logger.getLogger(TestEnhancedPluggableTokenManagerExecutor.class);
+
+ protected final KeycloakSession session;
+
+ public TestEnhancedPluggableTokenManagerExecutor(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public String getProviderId() {
+ return TestEnhancedPluggableTokenManagerExecutorFactory.PROVIDER_ID;
+ }
+
+ @Override
+ public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException {
+ ClientPolicyEvent event = context.getEvent();
+
+ if (event.equals(ClientPolicyEvent.TOKEN_RESPONSE)) {
+ TokenResponseContext tokenResponseContext = (TokenResponseContext)context;
+ dropSubClaimAndBuildTokenResponse(tokenResponseContext.getAccessTokenResponseBuilder());
+ }
+ }
+
+ private void dropSubClaimAndBuildTokenResponse(TokenManager.AccessTokenResponseBuilder builder) throws ClientPolicyException {
+ builder.getAccessToken().subject(null);
+ builder.build();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutorFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutorFactory.java
new file mode 100644
index 0000000000..c560be293d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestEnhancedPluggableTokenManagerExecutorFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 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.testsuite.services.clientpolicy.executor;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
+import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory;
+
+/**
+ * @author Takashi Norimatsu
+ */
+public class TestEnhancedPluggableTokenManagerExecutorFactory implements ClientPolicyExecutorProviderFactory {
+
+ public static final String PROVIDER_ID = "test-enhanced-token-mgr";
+
+ @Override
+ public ClientPolicyExecutorProvider create(KeycloakSession session) {
+ return new TestEnhancedPluggableTokenManagerExecutor(session);
+ }
+
+ @Override
+ public void init(Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "NA";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory
index 297fa71711..4f59e0a90a 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory
@@ -1 +1,2 @@
-org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExceptionExecutorFactory
\ No newline at end of file
+org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExceptionExecutorFactory
+org.keycloak.testsuite.services.clientpolicy.executor.TestEnhancedPluggableTokenManagerExecutorFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/ClientPoliciesExtendedEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/ClientPoliciesExtendedEventTest.java
index a8e07e8659..feac42ee90 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/ClientPoliciesExtendedEventTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/ClientPoliciesExtendedEventTest.java
@@ -46,6 +46,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.OAuth2DeviceConfig;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
+import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -64,6 +65,8 @@ import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LogoutConfirmPage;
import org.keycloak.testsuite.pages.OAuth2DeviceVerificationPage;
import org.keycloak.testsuite.pages.OAuthGrantPage;
+import org.keycloak.testsuite.services.clientpolicy.executor.TestEnhancedPluggableTokenManagerExecutor;
+import org.keycloak.testsuite.services.clientpolicy.executor.TestEnhancedPluggableTokenManagerExecutorFactory;
import org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExceptionExecutorFactory;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientPoliciesUtil.ClientPoliciesBuilder;
@@ -597,4 +600,44 @@ public class ClientPoliciesExtendedEventTest extends AbstractClientPoliciesTest
assertTrue(errorPage.isCurrent());
assertEquals("Exception thrown intentionally", errorPage.getError());
}
+
+ @Test
+ public void testEnhancedPluggableTokenManagerForTokenResponse() throws Exception {
+ // register a confidential client
+ String clientId = generateSuffixedName(CLIENT_NAME);
+ String clientSecret = "secret";
+ createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
+ clientRep.setSecret(clientSecret);
+ clientRep.setPublicClient(Boolean.FALSE);
+ clientRep.setBearerOnly(Boolean.FALSE);
+ });
+
+ // register profiles
+ String json = (new ClientProfilesBuilder()).addProfile(
+ (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen")
+ .addExecutor(TestEnhancedPluggableTokenManagerExecutorFactory.PROVIDER_ID, null)
+ .toRepresentation()
+ ).toString();
+ updateProfiles(json);
+
+ // register policies
+ json = (new ClientPoliciesBuilder()).addPolicy(
+ (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Primera Plitica", Boolean.TRUE)
+ .addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID,
+ createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_CONFIDENTIAL)))
+ .addProfile(PROFILE_NAME)
+ .toRepresentation()
+ ).toString();
+ updatePolicies(json);
+
+ oauth.clientId(clientId);
+ oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
+
+ events.expectLogin().client(clientId).assertEvent();
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+ OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, clientSecret);
+ assertEquals(200, response.getStatusCode());
+ AccessToken token = oauth.verifyToken(response.getAccessToken());
+ assertNull(token.getSubject());
+ }
}