KEYCLOAK-3173 enable logout offline refresh token using OIDC logout endpoint

This commit is contained in:
Stian Thorgersen 2017-11-13 16:27:57 +01:00 committed by Stian Thorgersen
parent d8b3654011
commit 925d5e1dea
3 changed files with 50 additions and 8 deletions

View file

@ -40,8 +40,10 @@ import org.keycloak.representations.RefreshToken;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@ -181,9 +183,19 @@ public class LogoutEndpoint {
}
try {
RefreshToken token = tokenManager.verifyRefreshToken(session, realm, refreshToken, false);
UserSessionModel userSessionModel = session.sessions().getUserSession(realm, token.getSessionState());
boolean offline = TokenUtil.TOKEN_TYPE_OFFLINE.equals(token.getType());
UserSessionModel userSessionModel;
if (offline) {
UserSessionManager sessionManager = new UserSessionManager(session);
userSessionModel = sessionManager.findOfflineUserSession(realm, token.getSessionState());
} else {
userSessionModel = session.sessions().getUserSession(realm, token.getSessionState());
}
if (userSessionModel != null) {
logout(userSessionModel);
logout(userSessionModel, offline);
}
} catch (OAuthErrorException e) {
event.error(Errors.INVALID_TOKEN);
@ -192,8 +204,8 @@ public class LogoutEndpoint {
return Cors.add(request, Response.noContent()).auth().allowedOrigins(uriInfo, client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
}
private void logout(UserSessionModel userSession) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
private void logout(UserSessionModel userSession, boolean offline) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true, offline);
event.user(userSession.getUser()).session(userSession).success();
}

View file

@ -155,6 +155,12 @@ public class AuthenticationManager {
);
}
public static void backchannelLogout(KeycloakSession session, RealmModel realm,
UserSessionModel userSession, UriInfo uriInfo,
ClientConnection connection, HttpHeaders headers,
boolean logoutBroker) {
backchannelLogout(session, realm, userSession, uriInfo, connection, headers, logoutBroker, false);
}
/**
* Do not logout broker
@ -169,7 +175,8 @@ public class AuthenticationManager {
public static void backchannelLogout(KeycloakSession session, RealmModel realm,
UserSessionModel userSession, UriInfo uriInfo,
ClientConnection connection, HttpHeaders headers,
boolean logoutBroker) {
boolean logoutBroker,
boolean offlineSession) {
if (userSession == null) return;
UserModel user = userSession.getUser();
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
@ -190,7 +197,12 @@ public class AuthenticationManager {
}
userSession.setState(UserSessionModel.State.LOGGED_OUT);
session.sessions().removeUserSession(realm, userSession);
if (offlineSession) {
session.sessions().removeOfflineUserSession(realm, userSession);
} else {
session.sessions().removeUserSession(realm, userSession);
}
}
private static AuthenticationSessionModel createOrJoinLogoutSession(RealmModel realm, final AuthenticationSessionManager asm, boolean browserCookie) {

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.oauth;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert;
import org.junit.Before;
@ -58,12 +59,11 @@ import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.NotFoundException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.ws.rs.NotFoundException;
import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.admin.ApiUtil.findRealmRoleByName;
@ -550,4 +550,22 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
List<Map<String, Object>> consents = user.getConsents();
Assert.assertTrue(consents.isEmpty());
}
@Test
public void offlineTokenLogout() throws Exception {
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
oauth.clientId("offline-client");
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret1", "test-user@localhost", "password");
assertEquals(200, response.getStatusCode());
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1");
assertEquals(200, response.getStatusCode());
CloseableHttpResponse logoutResponse = oauth.doLogout(response.getRefreshToken(), "secret1");
assertEquals(204, logoutResponse.getStatusLine().getStatusCode());
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1");
assertEquals(400, response.getStatusCode());
}
}