KEYCLOAK-4201 Offline tokens become useless when accessing admin REST API

This commit is contained in:
mposolda 2017-01-12 22:40:11 +01:00
parent c4cce147e2
commit 93157e49d5
5 changed files with 66 additions and 7 deletions

View file

@ -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() {

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
}