KEYCLOAK-4201 Offline tokens become useless when accessing admin REST API
This commit is contained in:
parent
c4cce147e2
commit
93157e49d5
5 changed files with 66 additions and 7 deletions
|
@ -78,8 +78,8 @@ public class Keycloak {
|
|||
return new Keycloak(serverUrl, realm, username, password, clientId, null, PASSWORD, null, null);
|
||||
}
|
||||
|
||||
public static Keycloak getInstance(String serverUrl, String realm, String clientId, String authtoken) {
|
||||
return new Keycloak(serverUrl, realm, null, null, clientId, null, PASSWORD, null, null);
|
||||
public static Keycloak getInstance(String serverUrl, String realm, String clientId, String authToken) {
|
||||
return new Keycloak(serverUrl, realm, null, null, clientId, null, PASSWORD, null, authToken);
|
||||
}
|
||||
|
||||
public RealmsResource realms() {
|
||||
|
|
|
@ -39,7 +39,7 @@ public interface RoleMappingResource {
|
|||
@Path("realm")
|
||||
public RoleScopeResource realmLevel();
|
||||
|
||||
@Path("clients/{clientId}")
|
||||
public RoleScopeResource clientLevel(@PathParam("clientId") String clientId);
|
||||
@Path("clients/{clientUUID}")
|
||||
public RoleScopeResource clientLevel(@PathParam("clientUUID") String clientUUID);
|
||||
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class AppAuthManager extends AuthenticationManager {
|
|||
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||
String tokenString = extractAuthorizationHeaderToken(headers);
|
||||
if (tokenString == null) return null;
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, tokenString, headers);
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, false, tokenString, headers);
|
||||
return authResult;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,15 @@ public class AuthenticationManager {
|
|||
return userSession.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
|
||||
}
|
||||
|
||||
public static boolean isOfflineSessionValid(RealmModel realm, UserSessionModel userSession) {
|
||||
if (userSession == null) {
|
||||
logger.debug("No offline user session");
|
||||
return false;
|
||||
}
|
||||
int currentTime = Time.currentTime();
|
||||
return userSession.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout() > currentTime;
|
||||
}
|
||||
|
||||
public static void expireUserSessionCookie(KeycloakSession session, UserSessionModel userSession, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, ClientConnection connection) {
|
||||
try {
|
||||
// check to see if any identity cookie is set with the same session and expire it if necessary
|
||||
|
@ -390,7 +399,7 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
String tokenString = cookie.getValue();
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, tokenString, session.getContext().getRequestHeaders());
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, true, tokenString, session.getContext().getRequestHeaders());
|
||||
if (authResult == null) {
|
||||
expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
|
||||
return null;
|
||||
|
@ -691,7 +700,7 @@ public class AuthenticationManager {
|
|||
|
||||
|
||||
protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, boolean checkTokenType,
|
||||
String tokenString, HttpHeaders headers) {
|
||||
boolean isCookie, String tokenString, HttpHeaders headers) {
|
||||
try {
|
||||
TokenVerifier verifier = TokenVerifier.create(tokenString).realmUrl(Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName())).checkActive(checkActive).checkTokenType(checkTokenType);
|
||||
String kid = verifier.getHeader().getKeyId();
|
||||
|
@ -729,6 +738,14 @@ public class AuthenticationManager {
|
|||
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||
if (!isSessionValid(realm, userSession)) {
|
||||
// Check if accessToken was for the offline session.
|
||||
if (!isCookie) {
|
||||
UserSessionModel offlineUserSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||
if (isOfflineSessionValid(realm, offlineUserSession)) {
|
||||
return new AuthResult(user, offlineUserSession, token);
|
||||
}
|
||||
}
|
||||
|
||||
if (userSession != null) backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
||||
logger.debug("User session not active");
|
||||
return null;
|
||||
|
|
|
@ -23,12 +23,15 @@ import org.junit.Before;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.RoleResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -40,6 +43,9 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||
import org.keycloak.testsuite.auth.page.AuthRealm;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.ClientManager;
|
||||
|
@ -432,4 +438,40 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
|||
testUser.roles().realmLevel().add(Collections.singletonList(offlineAccess));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-4201
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void offlineTokenAdminRESTAccess() throws Exception {
|
||||
// Grant "view-realm" role to user
|
||||
RealmResource appRealm = adminClient.realm("test");
|
||||
ClientResource realmMgmt = ApiUtil.findClientByClientId(appRealm, Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||
String realmMgmtUuid = realmMgmt.toRepresentation().getId();
|
||||
RoleRepresentation roleRep = realmMgmt.roles().get(AdminRoles.VIEW_REALM).toRepresentation();
|
||||
|
||||
UserResource testUser = findUserByUsernameId(appRealm, "test-user@localhost");
|
||||
testUser.roles().clientLevel(realmMgmtUuid).add(Collections.singletonList(roleRep));
|
||||
|
||||
// Login with offline token now
|
||||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("offline-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "test-user@localhost", "password");
|
||||
|
||||
events.clear();
|
||||
|
||||
// Set the time offset, so that "normal" userSession expires
|
||||
setTimeOffset(86400);
|
||||
|
||||
// Refresh with the offline token
|
||||
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret1");
|
||||
|
||||
// Use accessToken to admin REST request
|
||||
Keycloak offlineTokenAdmin = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
||||
AuthRealm.MASTER, Constants.ADMIN_CLI_CLIENT_ID, tokenResponse.getAccessToken());
|
||||
RealmRepresentation testRealm = offlineTokenAdmin.realm("test").toRepresentation();
|
||||
Assert.assertNotNull(testRealm);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue