diff --git a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java index d3666a281a..34fafc15b3 100644 --- a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java +++ b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java @@ -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) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java index bb2f7cdfca..e2de83f134 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java @@ -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 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 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 714cb8de12..2af4992a22 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -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 { diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 3d7ce31bf5..3bae5fd521 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -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 } diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java index 811a223817..6d50cd5a77 100644 --- a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java +++ b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java @@ -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 diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java index 4fe4945a83..250e19e011 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java @@ -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())) { diff --git a/services/src/main/java/org/keycloak/services/util/UserSessionUtil.java b/services/src/main/java/org/keycloak/services/util/UserSessionUtil.java index 86403e4a0d..71c3359b8e 100644 --- a/services/src/main/java/org/keycloak/services/util/UserSessionUtil.java +++ b/services/src/main/java/org/keycloak/services/util/UserSessionUtil.java @@ -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"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java index 23401d20a8..99c5dda733 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java @@ -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; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/AbstractClientPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/AbstractClientPoliciesTest.java index 2cee630e92..2fa53dd801 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/AbstractClientPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/policies/AbstractClientPoliciesTest.java @@ -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); }