Simplified checks in IntrospectionEndpoint (#28642)

Closes #24466

Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>


Co-authored-by: mposolda <mposolda@gmail.com>
This commit is contained in:
Giuseppe Graziano 2024-04-12 21:19:04 +02:00 committed by GitHub
parent a3b4b487d5
commit 4672366eb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 105 additions and 157 deletions

View file

@ -31,7 +31,6 @@ import org.jboss.logging.Logger;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.AccessTokenIntrospectionProvider;
import org.keycloak.representations.AccessToken;
@ -56,8 +55,7 @@ public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
public Response introspect(String token, EventBuilder eventBuilder) {
LOGGER.debug("Introspecting requesting party token");
try {
AccessToken accessToken = verifyAccessToken(token, eventBuilder);
AccessToken accessToken = verifyAccessToken(token, eventBuilder, true);
ObjectNode tokenMetadata;
if (accessToken != null) {

View file

@ -65,12 +65,13 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
public Response introspect(String token, EventBuilder eventBuilder) {
AccessToken accessToken = null;
try {
accessToken = verifyAccessToken(token, eventBuilder);
accessToken = transformAccessToken(accessToken);
ObjectNode tokenMetadata;
accessToken = verifyAccessToken(token, eventBuilder, false);
UserSessionModel userSession = tokenManager.getValidUserSessionIfTokenIsValid(session, realm, accessToken, eventBuilder);
ObjectNode tokenMetadata;
if (userSession != null) {
accessToken = transformAccessToken(accessToken, userSession);
if (accessToken != null) {
UserSessionModel userSession = accessToken.getSessionId() == null ? null : session.sessions().getUserSession(realm, accessToken.getSessionId());
tokenMetadata = JsonSerialization.createObjectNode(accessToken);
tokenMetadata.put("client_id", accessToken.getIssuedFor());
@ -83,22 +84,17 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
if (accessToken.getPreferredUsername() != null) {
tokenMetadata.put("username", accessToken.getPreferredUsername());
} else {
UserModel userModel = accessToken.getSubject() == null ? null : session.users().getUserById(realm, accessToken.getSubject());
UserModel userModel = userSession.getUser();
if (userModel != null) {
tokenMetadata.put("username", userModel.getUsername());
} else if (userSession != null && userSession.getUser() != null) {
tokenMetadata.put("username", userSession.getUser().getUsername());
}
}
}
if (userSession != null) {
String actor = userSession.getNote(ImpersonationSessionNote.IMPERSONATOR_USERNAME.toString());
if (actor != null) {
// for token exchange delegation semantics when an entity (actor) other than the subject is the acting party to whom authority has been delegated
tokenMetadata.putObject("act").put("sub", actor);
}
String actor = userSession.getNote(ImpersonationSessionNote.IMPERSONATOR_USERNAME.toString());
if (actor != null) {
// for token exchange delegation semantics when an entity (actor) other than the subject is the acting party to whom authority has been delegated
tokenMetadata.putObject("act").put("sub", actor);
}
tokenMetadata.put(OAuth2Constants.TOKEN_TYPE, accessToken.getType());
@ -109,7 +105,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
eventBuilder.error(Errors.TOKEN_INTROSPECTION_FAILED);
}
tokenMetadata.put("active", accessToken != null);
tokenMetadata.put("active", userSession != null);
return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
} catch (Exception e) {
@ -121,35 +117,20 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
}
}
public AccessToken transformAccessToken(AccessToken token) {
if (token == null) {
return null;
public AccessToken transformAccessToken(AccessToken token, UserSessionModel userSession) {
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if(clientSession == null) {
return token;
}
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
EventBuilder event = new EventBuilder(realm, session, session.getContext().getConnection())
.event(EventType.INTROSPECT_TOKEN)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
UserSessionModel userSession;
try {
userSession = UserSessionUtil.findValidSession(session, realm, token, event, client);
} catch (Exception e) {
logger.debugf("Can not get user session: %s", e.getMessage());
// Backwards compatibility
return token;
}
if (userSession.getUser() == null) {
logger.debugf("User not found");
// Backwards compatibility
return token;
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, token.getScope(), session);
AccessToken smallToken = getAccessTokenFromStoredData(token, userSession);
AccessToken smallToken = getAccessTokenFromStoredData(token);
return tokenManager.transformIntrospectionAccessToken(session, smallToken, userSession, clientSessionCtx);
}
private AccessToken getAccessTokenFromStoredData(AccessToken token, UserSessionModel userSession) {
private AccessToken getAccessTokenFromStoredData(AccessToken token) {
// Copy just "basic" claims from the initial token. The same like filled in TokenManager.initToken. The rest should be possibly added by protocol mappers (only if configured for introspection response)
AccessToken newToken = new AccessToken();
newToken.id(token.getId());
@ -171,8 +152,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
return newToken;
}
protected AccessToken verifyAccessToken(String token, EventBuilder eventBuilder) {
AccessToken accessToken;
protected AccessToken verifyAccessToken(String token, EventBuilder eventBuilder, boolean validateSession) {
try {
TokenVerifier<AccessToken> verifier = TokenVerifier.create(token, AccessToken.class)
@ -181,16 +161,17 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
verifier.verifierContext(verifierContext);
accessToken = verifier.verify().getToken();
AccessToken accessToken = verifier.verify().getToken();
if (validateSession) {
return tokenManager.checkTokenValidForIntrospection(session, realm, verifier.verify().getToken(), eventBuilder);
}
return accessToken;
} catch (VerificationException e) {
logger.debugf("Introspection access token : JWT check failed: %s", e.getMessage());
eventBuilder.detail(Details.REASON,"Access token JWT check failed");
return null;
}
RealmModel realm = this.session.getContext().getRealm();
return tokenManager.checkTokenValidForIntrospection(session, realm, accessToken, false, eventBuilder) ? accessToken : null;
}
@Override

View file

@ -90,6 +90,7 @@ import org.keycloak.services.util.AuthorizationContextUtil;
import org.keycloak.services.util.DPoPUtil;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.MtlsHoKTokenUtil;
import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.util.TokenUtil;
@ -240,25 +241,38 @@ public class TokenManager {
}
/**
* Checks if the token is valid. Optionally the session last refresh and client session timestamp
* are updated if the token was valid. This is used to keep the session alive when long lived tokens are used.
* Checks if the token is valid.
*
* @param session
* @param realm
* @param token
* @param updateTimestamps
* @return
*/
public boolean checkTokenValidForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token, boolean updateTimestamps, EventBuilder eventBuilder) {
public AccessToken checkTokenValidForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder eventBuilder) {
return getValidUserSessionIfTokenIsValid(session, realm, token, eventBuilder) != null ? token : null;
}
/**
* Checks if the token is valid and return a valid user session.
*
* @param session
* @param realm
* @param token
* @return
*/
public UserSessionModel getValidUserSessionIfTokenIsValid(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder eventBuilder) {
if (token == null) {
return null;
}
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
if (client == null) {
logger.debugf("Introspection access token : client with clientId %s does not exist", token.getIssuedFor() );
eventBuilder.detail(Details.REASON, String.format("Could not find client for %s", token.getIssuedFor()));
return false;
return null;
} else if (!client.isEnabled()) {
logger.debugf("Introspection access token : client with clientId %s is disabled", token.getIssuedFor() );
eventBuilder.detail(Details.REASON, String.format("Client with clientId %s is disabled", token.getIssuedFor()));
return false;
return null;
}
try {
@ -268,86 +282,42 @@ public class TokenManager {
} catch (VerificationException e) {
logger.debugf("Introspection access token for %s client: JWT check failed: %s", token.getIssuedFor(), e.getMessage());
eventBuilder.detail(Details.REASON, "Introspection access token for "+token.getIssuedFor() +" client: JWT check failed");
return false;
return null;
}
boolean valid = false;
UserSessionModel userSession;
try {
userSession = UserSessionUtil.findValidSession(session, realm, token, eventBuilder, client);
} catch (Exception e) {
logger.debugf( "Introspection access token for " + token.getIssuedFor() + " client:" + e.getMessage());
eventBuilder.detail(Details.REASON, "Introspection access token for " + token.getIssuedFor() + " client:" + e.getMessage());
return null;
}
// Tokens without sessions are considered valid. Signature check and revocation check are sufficient checks for them
if (token.getSessionState() == null) {
UserModel user = lookupUserFromStatelessToken(session, realm, token);
valid = isUserValid(session, realm, token, user);
if (!valid)
eventBuilder.detail(Details.REASON, "Could not find valid transient user session");
} else {
if (!isUserValid(session, realm, token, userSession.getUser())) {
logger.debugf("Could not find valid user from user");
eventBuilder.detail(Details.REASON, "Could not find valid user from user");
return null;
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
if (userSession == null) {
// also try to resolve sessions created during token exchange when the user is impersonated
userSession = session.sessions().getUserSessionWithPredicate(realm,
token.getSessionState(), false,
model -> client.getId().equals(model.getNote(ImpersonationSessionNote.IMPERSONATOR_CLIENT.toString())));
}
if (AuthenticationManager.isSessionValid(realm, userSession)) {
valid = isUserValid(session, realm, token, userSession.getUser());
} else {
userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
valid = isUserValid(session, realm, token, userSession.getUser());
}
}
if (!valid) {
logger.debugf("Could not find valid user session for session_state = %s", token.getSessionState());
eventBuilder.detail(Details.REASON, String.format("Could not find valid user session for session_state = %s", token.getSessionState()));
}
if (valid && (token.isIssuedBeforeSessionStart(userSession.getStarted()))) {
valid = false;
logger.debugf("Token is issued (%s) before session () has started", String.valueOf(token.getIat()), String.valueOf(userSession.getStarted()));
eventBuilder.detail(Details.REASON, String.format("Token is issued (%s) before user session () has started", String.valueOf(token.getIat()), String.valueOf(userSession.getStarted())));
}
AuthenticatedClientSessionModel clientSession = userSession == null ? null : userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
if (valid && (token.isIssuedBeforeSessionStart(clientSession.getStarted()))) {
valid = false;
logger.debugf("Token is issued (%s) before session () has started", String.valueOf(token.getIat()), String.valueOf(clientSession.getStarted()));
eventBuilder.detail(Details.REASON, String.format("Token is issued (%s) before client session () has started", String.valueOf(token.getIat()), String.valueOf(clientSession.getStarted())));
}
}
String tokenType = token.getType();
if (realm.isRevokeRefreshToken()
String tokenType = token.getType();
if (realm.isRevokeRefreshToken()
&& (tokenType.equals(TokenUtil.TOKEN_TYPE_REFRESH) || tokenType.equals(TokenUtil.TOKEN_TYPE_OFFLINE))
&& !validateTokenReuseForIntrospection(session, realm, token)) {
logger.debug("Introspection access token for "+token.getIssuedFor() +" client: failed to validate Token reuse for introspection");
eventBuilder.detail(Details.REASON, "Realm revoke refresh token, token type is "+tokenType+ " and token is not eligible for introspection");
return false;
}
if (updateTimestamps && valid) {
int currentTime = Time.currentTime();
userSession.setLastSessionRefresh(currentTime);
if (clientSession != null) {
clientSession.setTimestamp(currentTime);
}
}
logger.debug("Introspection access token for "+token.getIssuedFor() +" client: failed to validate Token reuse for introspection");
eventBuilder.detail(Details.REASON, "Realm revoke refresh token, token type is "+tokenType+ " and token is not eligible for introspection");
return null;
}
return valid;
return userSession;
}
private boolean validateTokenReuseForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) {
UserSessionModel userSession = null;
if (token.getType().equals(TokenUtil.TOKEN_TYPE_REFRESH)) {
userSession = session.sessions().getUserSession(realm, token.getSessionState());
userSession = session.sessions().getUserSession(realm, token.getSessionId());
} else {
UserSessionManager sessionManager = new UserSessionManager(session);
userSession = sessionManager.findOfflineUserSession(realm, token.getSessionState());
userSession = sessionManager.findOfflineUserSession(realm, token.getSessionId());
}
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
@ -362,13 +332,13 @@ public class TokenManager {
}
}
private boolean isUserValid(KeycloakSession session, RealmModel realm, AccessToken token, UserModel user) {
public static boolean isUserValid(KeycloakSession session, RealmModel realm, AccessToken token, UserModel user) {
if (user == null) {
logger.debugf("User does not exist for token introspection");
logger.debugf("User does not exists");
return false;
}
if (!user.isEnabled()) {
logger.debugf("User is disable for token introspection");
logger.debugf("User '%s' is disabled", user.getUsername());
return false;
}
try {

View file

@ -1410,14 +1410,14 @@ public class AuthenticationManager {
UserModel user = null;
if (token.getSessionState() == null) {
user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
if (!isUserValid(session, realm, user, token)) {
if (!TokenManager.isUserValid(session, realm, token, user)) {
return null;
}
} else {
userSession = session.sessions().getUserSession(realm, token.getSessionState());
if (userSession != null) {
user = userSession.getUser();
if (!isUserValid(session, realm, user, token)) {
if (!TokenManager.isUserValid(session, realm, token, user)) {
return null;
}
}
@ -1483,23 +1483,6 @@ public class AuthenticationManager {
return true;
}
private static boolean isUserValid(KeycloakSession session, RealmModel realm, UserModel user, AccessToken token) {
if (user == null || !user.isEnabled()) {
logger.debug("Unknown user in identity token");
return false;
}
if (! isLightweightUser(user)) {
int userNotBefore = session.users().getNotBeforeOfUser(realm, user);
if (token.getIssuedAt() < userNotBefore) {
logger.debug("User notBefore newer than token");
return false;
}
}
return true;
}
public enum AuthenticationStatus {
SUCCESS, ACCOUNT_TEMPORARILY_DISABLED, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED
}

View file

@ -22,10 +22,11 @@ import java.util.List;
import java.util.Objects;
import org.jboss.logging.Logger;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ImpersonationSessionNote;
import static org.keycloak.services.managers.AuthenticationManager.authenticateIdentityCookie;
@ -48,6 +49,9 @@ public class UserSessionCrossDCManager {
return kcSession.sessions().getUserSessionWithPredicate(realm, id, offline, userSession -> userSession.getAuthenticatedClientSessionByClient(clientUUID) != null);
}
public UserSessionModel getUserSessionWithImpersonatorClient(RealmModel realm, String id, boolean offline, String clientUUID) {
return kcSession.sessions().getUserSessionWithPredicate(realm, id, offline, userSession -> clientUUID.equals(userSession.getNote(ImpersonationSessionNote.IMPERSONATOR_CLIENT.toString())));
}
// get userSession if it has "authenticatedClientSession" of specified client attached to it. Otherwise download it from remoteCache
// TODO Probably remove this method once AuthenticatedClientSession.getAction is removed and information is moved to OAuth code JWT instead

View file

@ -34,6 +34,7 @@ import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.resource.AccountResourceProvider;
import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.theme.Theme;
import jakarta.ws.rs.HttpMethod;
@ -120,11 +121,14 @@ public class AccountLoader {
}
AccessToken accessToken = authResult.getToken();
UserSessionUtil.checkTokenIssuedAt(client.getRealm(), accessToken, authResult.getSession(), event, authResult.getClient());
if (accessToken.getAudience() == null || accessToken.getResourceAccess(client.getClientId()) == null) {
// transform for introspection to get the required claims
AccessTokenIntrospectionProvider provider = (AccessTokenIntrospectionProvider) session.getProvider(TokenIntrospectionProvider.class,
AccessTokenIntrospectionProviderFactory.ACCESS_TOKEN_TYPE);
accessToken = provider.transformAccessToken(accessToken);
accessToken = provider.transformAccessToken(accessToken, authResult.getSession());
}
if (!accessToken.hasAudience(client.getClientId())) {

View file

@ -12,7 +12,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.AccessTokenIntrospectionProvider;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
@ -28,6 +27,9 @@ public class UserSessionUtil {
private static final Logger logger = Logger.getLogger(UserSessionUtil.class);
public static UserSessionModel findValidSession(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
return findValidSession(session, realm, token, event, client, error);
@ -35,18 +37,23 @@ public class UserSessionUtil {
public static UserSessionModel findValidSession(KeycloakSession session, RealmModel realm,
AccessToken token, EventBuilder event, ClientModel client, OAuth2Error error) {
if (token.getSessionState() == null) {
if (token.getSessionId() == null) {
return createTransientSessionForClient(session, realm, token, client, event);
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionId(), false, client.getId());
if (userSession == null) {
// also try to resolve sessions created during token exchange when the user is impersonated
userSession = new UserSessionCrossDCManager(session).getUserSessionWithImpersonatorClient(realm, token.getSessionId(), false, client.getId());
}
UserSessionModel offlineUserSession = null;
if (AuthenticationManager.isSessionValid(realm, userSession)) {
checkTokenIssuedAt(realm, token, userSession, event, client);
event.session(userSession);
return userSession;
} else {
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionId(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
checkTokenIssuedAt(realm, token, offlineUserSession, event, client);
event.session(offlineUserSession);
@ -94,7 +101,7 @@ public class UserSessionUtil {
return userSession;
}
private static void checkTokenIssuedAt(RealmModel realm, AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
public static void checkTokenIssuedAt(RealmModel realm, AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
logger.debug("Stale token for user session");
@ -103,7 +110,7 @@ public class UserSessionUtil {
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
if (clientSession != null && token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
logger.debug("Stale token for client session");
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");

View file

@ -2783,6 +2783,7 @@ public class CIBATest extends AbstractClientPoliciesTest {
}
private String doIntrospectAccessTokenWithClientCredential(OAuthClient.AccessTokenResponse tokenRes, String username) throws IOException {
AccessToken accessToken = oauth.verifyToken(tokenRes.getAccessToken());
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, tokenRes.getAccessToken());
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(tokenResponse);
@ -2793,7 +2794,7 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(rep.isActive(), is(equalTo(true)));
assertThat(rep.getClientId(), is(equalTo(TEST_CLIENT_NAME)));
assertThat(rep.getIssuedFor(), is(equalTo(TEST_CLIENT_NAME)));
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).clearDetails().assertEvent();
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).session(accessToken.getSessionId()).clearDetails().assertEvent();
tokenResponse = oauth.introspectAccessTokenWithClientCredential(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, tokenRes.getRefreshToken());
jsonNode = objectMapper.readTree(tokenResponse);
@ -2804,7 +2805,7 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(rep.getClientId(), is(equalTo(TEST_CLIENT_NAME)));
assertThat(rep.getIssuedFor(), is(equalTo(TEST_CLIENT_NAME)));
assertThat(rep.getAudience()[0], is(equalTo(rep.getIssuer())));
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).clearDetails().assertEvent();
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).session(accessToken.getSessionId()).clearDetails().assertEvent();
tokenResponse = oauth.introspectAccessTokenWithClientCredential(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, tokenRes.getIdToken());
jsonNode = objectMapper.readTree(tokenResponse);
@ -2817,7 +2818,7 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(rep.getIssuedFor(), is(equalTo(TEST_CLIENT_NAME)));
assertThat(rep.getPreferredUsername(), is(equalTo(username)));
assertThat(rep.getAudience()[0], is(equalTo(rep.getIssuedFor())));
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).clearDetails().assertEvent();
events.expect(EventType.INTROSPECT_TOKEN).user((String) null).session(accessToken.getSessionId()).clearDetails().assertEvent();
return tokenResponse;
}

View file

@ -648,7 +648,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
// OAuth2 protocol operation
protected void doIntrospectAccessToken(OAuthClient.AccessTokenResponse tokenRes, String username, String clientId, String clientSecret) throws IOException {
protected void doIntrospectAccessToken(OAuthClient.AccessTokenResponse tokenRes, String username, String clientId, String sessionId, String clientSecret) throws IOException {
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(clientId, clientSecret, tokenRes.getAccessToken());
JsonNode jsonNode = objectMapper.readTree(tokenResponse);
assertEquals(true, jsonNode.get("active").asBoolean());
@ -658,7 +658,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
assertEquals(true, rep.isActive());
assertEquals(clientId, rep.getClientId());
assertEquals(clientId, rep.getIssuedFor());
events.expect(EventType.INTROSPECT_TOKEN).client(clientId).user((String)null).clearDetails().assertEvent();
events.expect(EventType.INTROSPECT_TOKEN).client(clientId).session(sessionId).user((String)null).clearDetails().assertEvent();
}
protected void doTokenRevoke(String refreshToken, String clientId, String clientSecret, String userId, boolean isOfflineAccess) throws IOException {
@ -1530,7 +1530,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
assertEquals(sessionId, refreshedRefreshToken.getSessionState());
assertEquals(findUserByUsername(adminClient.realm(REALM_NAME), userName).getId(), refreshedToken.getSubject());
doIntrospectAccessToken(refreshResponse, userName, clientId, clientSecret);
doIntrospectAccessToken(refreshResponse, userName, clientId, sessionId, clientSecret);
doTokenRevoke(refreshResponse.getRefreshToken(), clientId, clientSecret, userId, false);
}