keycloak-aplcache
Changes
examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java 12(+9 -3)
integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java 8(+8 -0)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java 6(+2 -4)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java 7(+2 -5)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java 7(+2 -5)
Details
diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml
index 0f57ea3..077eb98 100755
--- a/docbook/reference/en/en-US/master.xml
+++ b/docbook/reference/en/en-US/master.xml
@@ -12,6 +12,7 @@
<!ENTITY JBossAdapter SYSTEM "modules/jboss-adapter.xml">
<!ENTITY JavascriptAdapter SYSTEM "modules/javascript-adapter.xml">
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
+ <!ENTITY Logout SYSTEM "modules/logout.xml">
<!ENTITY SocialConfig SYSTEM "modules/social-config.xml">
<!ENTITY SocialFacebook SYSTEM "modules/social-facebook.xml">
<!ENTITY SocialGitHub SYSTEM "modules/social-github.xml">
@@ -80,6 +81,7 @@ This one is short
&JBossAdapter;
&JavascriptAdapter;
&InstalledApplications;
+ &Logout;
</chapter>
<chapter>
diff --git a/docbook/reference/en/en-US/modules/direct-access.xml b/docbook/reference/en/en-US/modules/direct-access.xml
index 1074537..c08735b 100755
--- a/docbook/reference/en/en-US/modules/direct-access.xml
+++ b/docbook/reference/en/en-US/modules/direct-access.xml
@@ -88,7 +88,7 @@ try {
if (isPublic()) { // if client is public access type
formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "customer-portal"));
} else {
- String authorization = BasicAuthHelper.createHeader("customer-portal", "secret-secret-secret);
+ String authorization = BasicAuthHelper.createHeader("customer-portal", "secret-secret-secret");
post.setHeader("Authorization", authorization);
}
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
@@ -125,4 +125,36 @@ GET /my/rest/api
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
</programlisting>
</para>
+ <para>
+ To logout you must use the refresh token contained in the AccessTokenResponse object.
+ </para>
+<programlisting>
+ <![CDATA[
+List<NameValuePair> formparams = new ArrayList<NameValuePair>();
+if (isPublic()) { // if client is public access type
+ formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "customer-portal"));
+} else {
+ String authorization = BasicAuthHelper.createHeader("customer-portal", "secret-secret-secret");
+ post.setHeader("Authorization", authorization);
+}
+formparams.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, tokenResponse.getRefreshToken()));
+HttpResponse response = null;
+URI logoutUri = KeycloakUriBuilder.fromUri(getBaseUrl(request) + "/auth")
+ .path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+ .build("demo");
+HttpPost post = new HttpPost(logoutUri);
+UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+post.setEntity(form);
+response = client.execute(post);
+int status = response.getStatusLine().getStatusCode();
+HttpEntity entity = response.getEntity();
+if (status != 204) {
+ error(status, entity);
+}
+if (entity == null) {
+ return;
+}
+InputStream is = entity.getContent();
+if (is != null) is.close();
+]]></programlisting>
</chapter>
\ No newline at end of file
diff --git a/docbook/reference/en/en-US/modules/logout.xml b/docbook/reference/en/en-US/modules/logout.xml
new file mode 100755
index 0000000..e2812b8
--- /dev/null
+++ b/docbook/reference/en/en-US/modules/logout.xml
@@ -0,0 +1,9 @@
+<section>
+ <title>Logout</title>
+ <para>
+ There are multiple ways you can logout from a web application. For Java EE servlet containers, you can call
+ HttpServletRequest.logout().
+ For any other browser application, you can point the browser at the url <literal>http://auth-server/auth/realms/{realm-name}/tokens/logout?redirect_uri=encodedRedirectUri</literal>.
+ This will log you out if you have an SSO session with your browser.
+ </para>
+</section>
\ No newline at end of file
diff --git a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
index aecaf91..7b5f56b 100755
--- a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
+++ b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
@@ -1,6 +1,17 @@
<chapter id="Migration_from_older_versions">
<title>Migration from older versions</title>
<sect1>
+ <title>Migrating from 1.0 Beta 4 to RC-1</title>
+ <itemizedlist>
+ <listitem>
+ logout REST API has been refactored. The GET request on the logout URI does not take a session_state
+ parameter anymore. You must be logged in in order to log out the session.
+ You can also POST to the lougt REST URI. This action requires a valid refresh token to perform the logout.
+ The signature is the same as refresh token minus the grant type form parameter. See documentation for details.
+ </listitem>
+ </itemizedlist>
+ </sect1>
+ <sect1>
<title>Migrating from 1.0 Beta 1 to Beta 4</title>
<itemizedlist>
<listitem>
diff --git a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
index 763dfee..4e26fdf 100755
--- a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
+++ b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -102,17 +102,23 @@ public class AdminClient {
try {
- HttpGet get = new HttpGet(KeycloakUriBuilder.fromUri(getBaseUrl(request) + "/auth")
+ HttpPost post = new HttpPost(KeycloakUriBuilder.fromUri(getBaseUrl(request) + "/auth")
.path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
- .queryParam("session_state", res.getSessionState())
.build("demo"));
- HttpResponse response = client.execute(get);
+ List<NameValuePair> formparams = new ArrayList<NameValuePair>();
+ formparams.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, res.getRefreshToken()));
+ formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "admin-client"));
+ HttpResponse response = client.execute(post);
+ boolean status = response.getStatusLine().getStatusCode() != 204;
HttpEntity entity = response.getEntity();
if (entity == null) {
return;
}
InputStream is = entity.getContent();
if (is != null) is.close();
+ if (status) {
+ throw new RuntimeException("failed to logout");
+ }
} finally {
client.getConnectionManager().shutdown();
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
index 6442499..5f4325a 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
@@ -42,6 +42,14 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
return super.getTokenString();
}
+ public void logout(KeycloakDeployment deployment) {
+ try {
+ ServerRequest.invokeLogout(deployment, refreshToken);
+ } catch (Exception e) {
+ log.error("failed to invoke remote logout", e);
+ }
+ }
+
public boolean isActive() {
return this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/ServerRequest.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/ServerRequest.java
index ac08c80..ed8e91f 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/ServerRequest.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/ServerRequest.java
@@ -48,19 +48,41 @@ public class ServerRequest {
}
}
- public static void invokeLogout(KeycloakDeployment deployment, String sessionId) throws IOException, HttpFailure {
- URI uri = deployment.getLogoutUrl().clone().queryParam("session_state", sessionId).build();
- HttpGet logout = new HttpGet(uri);
- HttpResponse response = deployment.getClient().execute(logout);
+ public static void invokeLogout(KeycloakDeployment deployment, String refreshToken) throws IOException, HttpFailure {
+ String client_id = deployment.getResourceName();
+ Map<String, String> credentials = deployment.getResourceCredentials();
+ HttpClient client = deployment.getClient();
+ URI uri = deployment.getLogoutUrl().clone().build();
+ List<NameValuePair> formparams = new ArrayList<NameValuePair>();
+ for (Map.Entry<String, String> entry : credentials.entrySet()) {
+ formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
+ }
+ formparams.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, refreshToken));
+ HttpResponse response = null;
+ HttpPost post = new HttpPost(uri);
+ if (!deployment.isPublicClient()) {
+ String clientSecret = credentials.get(CredentialRepresentation.SECRET);
+ if (clientSecret != null) {
+ String authorization = BasicAuthHelper.createHeader(client_id, clientSecret);
+ post.setHeader("Authorization", authorization);
+ }
+ } else {
+ formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, client_id));
+ }
+
+ UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+ post.setEntity(form);
+ response = client.execute(post);
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
- if (status != 200) {
+ if (status != 204) {
error(status, entity);
}
if (entity == null) {
return;
}
- entity.getContent().close();
+ InputStream is = entity.getContent();
+ if (is != null) is.close();
}
public static AccessTokenResponse invokeAccessCodeToToken(KeycloakDeployment deployment, String code, String redirectUri) throws HttpFailure, IOException {
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index 52945cb..45707b4 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -64,10 +64,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
Session session = request.getSessionInternal(false);
if (session != null) {
session.removeNote(KeycloakSecurityContext.class.getName());
- try {
- ServerRequest.invokeLogout(deploymentContext.getDeployment(), ksc.getToken().getSessionState());
- } catch (Exception e) {
- log.error("failed to invoke remote logout", e);
+ if (ksc instanceof RefreshableKeycloakSecurityContext) {
+ ((RefreshableKeycloakSecurityContext)ksc).logout(deploymentContext.getDeployment());
}
}
}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
index 45522fd..1ccce63 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
@@ -81,11 +81,8 @@ public class ServletKeycloakAuthMech extends UndertowKeycloakAuthMech {
if (account == null) return;
session.removeAttribute(KeycloakSecurityContext.class.getName());
session.removeAttribute(KeycloakUndertowAccount.class.getName());
- String sessionId = account.getKeycloakSecurityContext().getToken().getSessionState();
- try {
- ServerRequest.invokeLogout(deploymentContext.getDeployment(), sessionId);
- } catch (Exception e) {
- log.error("failed to invoke remote logout", e);
+ if (account.getKeycloakSecurityContext() != null) {
+ account.getKeycloakSecurityContext().logout(deploymentContext.getDeployment());
}
}
};
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
index 8e55e5a..8182496 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
@@ -72,11 +72,8 @@ public abstract class UndertowKeycloakAuthMech implements AuthenticationMechanis
KeycloakUndertowAccount account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
if (account == null) return;
session.removeAttribute(KeycloakUndertowAccount.class.getName());
- String sessionId = account.getKeycloakSecurityContext().getToken().getSessionState();
- try {
- ServerRequest.invokeLogout(deploymentContext.getDeployment(), sessionId);
- } catch (Exception e) {
- log.error("failed to invoke remote logout", e);
+ if (account.getKeycloakSecurityContext() != null) {
+ account.getKeycloakSecurityContext().logout(deploymentContext.getDeployment());
}
}
};
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 0971a07..0fc5834 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -64,23 +64,7 @@ public class TokenManager {
}
public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
- JWSInput jws = new JWSInput(encodedRefreshToken);
- RefreshToken refreshToken = null;
- try {
- if (!RSAProvider.verify(jws, realm.getPublicKey())) {
- throw new RuntimeException("Invalid refresh token");
- }
- refreshToken = jws.readJsonContent(RefreshToken.class);
- } catch (IOException e) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", e);
- }
- if (refreshToken.isExpired()) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired");
- }
-
- if (refreshToken.getIssuedAt() < realm.getNotBefore()) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token");
- }
+ RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken);
audit.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId());
@@ -122,6 +106,27 @@ public class TokenManager {
return accessToken;
}
+ public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken) throws OAuthErrorException {
+ JWSInput jws = new JWSInput(encodedRefreshToken);
+ RefreshToken refreshToken = null;
+ try {
+ if (!RSAProvider.verify(jws, realm.getPublicKey())) {
+ throw new RuntimeException("Invalid refresh token");
+ }
+ refreshToken = jws.readJsonContent(RefreshToken.class);
+ } catch (IOException e) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", e);
+ }
+ if (refreshToken.isExpired()) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired");
+ }
+
+ if (refreshToken.getIssuedAt() < realm.getNotBefore()) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token");
+ }
+ return refreshToken;
+ }
+
public AccessToken createClientAccessToken(Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
AccessToken token = initToken(realm, client, user, session);
for (RoleModel role : requestedRoles) {
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index cba599c..df5fbc3 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -31,6 +31,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.ClientConnection;
import org.keycloak.services.managers.AccessCode;
@@ -1040,39 +1041,29 @@ public class TokenService {
}
/**
- * Logout user session.
+ * Logout user session. User must be logged in via a session cookie.
*
- * @param sessionState
* @param redirectUri
* @return
*/
@Path("logout")
@GET
@NoCache
- public Response logout(final @QueryParam("session_state") String sessionState, final @QueryParam("redirect_uri") String redirectUri) {
+ public Response logout(final @QueryParam("redirect_uri") String redirectUri) {
// todo do we care if anybody can trigger this?
audit.event(EventType.LOGOUT);
if (redirectUri != null) {
audit.detail(Details.REDIRECT_URI, redirectUri);
}
- if (sessionState != null) {
- audit.session(sessionState);
- }
-
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
if (authResult != null) {
logout(authResult.getSession());
- } else if (sessionState != null) {
- UserSessionModel userSession = session.sessions().getUserSession(realm, sessionState);
- if (userSession != null) {
- logout(userSession);
- } else {
- audit.error(Errors.USER_SESSION_NOT_FOUND);
- }
} else {
audit.error(Errors.USER_NOT_LOGGED_IN);
+ OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
+ return oauth.forwardToSecurityFailure("Not logged in.");
}
if (redirectUri != null) {
@@ -1088,6 +1079,61 @@ public class TokenService {
}
}
+ /**
+ * Logout a session via a non-browser invocation. Similar signature to refresh token except there is no grant_type.
+ * You must pass in the refresh token and
+ * authenticate the client if it is not public.
+ *
+ * If the client is a confidential client
+ * you must include the client-id (application name or oauth client name) and secret in an Basic Auth Authorization header.
+ *
+ * If the client is a public client, then you must include a "client_id" form parameter with the app's or oauth client's name.
+ *
+ * returns 204 if successful, 400 if not with a json error response.
+ *
+ * @param authorizationHeader
+ * @param form
+ * @return
+ */
+ @Path("logout")
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response logoutToken(final @HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader,
+ final MultivaluedMap<String, String> form) {
+ logger.info("--> logoutToken");
+ if (!checkSsl()) {
+ throw new NotAcceptableException("HTTPS required");
+ }
+
+ audit.event(EventType.LOGOUT);
+
+ ClientModel client = authorizeClient(authorizationHeader, form, audit);
+ String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
+ if (refreshToken == null) {
+ Map<String, String> error = new HashMap<String, String>();
+ error.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_REQUEST);
+ error.put(OAuth2Constants.ERROR_DESCRIPTION, "No refresh token");
+ audit.error(Errors.INVALID_TOKEN);
+ logger.error("OAuth Error: no refresh token");
+ return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+ }
+ try {
+ RefreshToken token = tokenManager.verifyRefreshToken(realm, refreshToken);
+ UserSessionModel userSessionModel = session.sessions().getUserSession(realm, token.getSessionState());
+ if (userSessionModel != null) {
+ logout(userSessionModel);
+ }
+ } catch (OAuthErrorException e) {
+ Map<String, String> error = new HashMap<String, String>();
+ error.put(OAuth2Constants.ERROR, e.getError());
+ if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription());
+ audit.error(Errors.INVALID_TOKEN);
+ logger.error("OAuth Error", e);
+ return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+ }
+ return Cors.add(request, Response.noContent()).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
+ }
+
private void logout(UserSessionModel userSession) {
authManager.logout(session, realm, userSession, uriInfo, clientConnection);
audit.user(userSession.getUser()).session(userSession).success();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 9bb0f49..6e746cc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -214,7 +214,8 @@ public class AdapterTest {
driver.navigate().to("http://localhost:8081/customer-portal");
- Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+ String currentUrl = driver.getCurrentUrl();
+ Assert.assertTrue(currentUrl.startsWith(LOGIN_URL));
driver.navigate().to("http://localhost:8081/product-portal");
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
old mode 100644
new mode 100755
index 4ec9193..26c59e7
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
@@ -124,32 +124,15 @@ public class LogoutTest {
String sessionId = events.expectLogin().assertEvent().getSessionId();
- // Login session 2
- WebDriver driver2 = WebRule.createWebDriver();
-
- OAuthClient oauth2 = new OAuthClient(driver2);
- oauth2.doLogin("test-user@localhost", "password");
-
- String sessionId2 = events.expectLogin().assertEvent().getSessionId();
- assertNotEquals(sessionId, sessionId2);
-
// Check session 1 logged-in
oauth.openLoginForm();
events.expectLogin().session(sessionId).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
- // Check session 2 logged-in
- oauth2.openLoginForm();
- events.expectLogin().session(sessionId2).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
-
- // Logout session 1 by redirect
+ // Logout session 1 by redirect
driver.navigate().to(oauth.getLogoutUrl(AppPage.baseUrl, null));
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, AppPage.baseUrl).assertEvent();
- // Check session 2 logged-in
- oauth2.openLoginForm();
- events.expectLogin().session(sessionId2).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
-
- // Check session 1 not logged-in
+ // Check session 1 not logged-in
oauth.openLoginForm();
assertEquals(oauth.getLoginFormUrl(), driver.getCurrentUrl());
@@ -157,19 +140,10 @@ public class LogoutTest {
oauth.doLogin("test-user@localhost", "password");
String sessionId3 = events.expectLogin().assertEvent().getSessionId();
assertNotEquals(sessionId, sessionId3);
- assertNotEquals(sessionId2, sessionId3);
-
- // Logout session 2 by session_state
- oauth2.doLogout(null, sessionId2);
- events.expectLogout(sessionId2).removeDetail(Details.REDIRECT_URI).assertEvent();
// Check session 3 logged-in
oauth.openLoginForm();
events.expectLogin().session(sessionId3).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
-
- // Check session 2 not logged-in
- oauth2.openLoginForm();
- assertEquals(oauth2.getLoginFormUrl(), driver2.getCurrentUrl());
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index f2ce2b6..55df18f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -305,8 +305,13 @@ public class AccessTokenTest {
{
builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
URI logoutUri = TokenService.logoutUrl(builder).build("test");
- Response response = client.target(logoutUri).queryParam("session_state", tokenResponse.getSessionState()).request().get();
- Assert.assertEquals(200, response.getStatus());
+ String header = BasicAuthHelper.createHeader("test-app", "password");
+ Form form = new Form();
+ form.param("refresh_token", tokenResponse.getRefreshToken());
+ Response response = client.target(logoutUri).request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ Assert.assertEquals(204, response.getStatus());
response.close();
}
{
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
index 5a1dad3..1ebf327 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
@@ -131,13 +131,9 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
.removeDetail(Details.REDIRECT_URI)
.assertEvent();
- HttpResponse logoutResponse = oauth.doLogout(null, accessToken.getSessionState());
- assertEquals(200, logoutResponse.getStatusLine().getStatusCode());
- events.expectLogout(accessToken.getSessionState()).removeDetail(Details.REDIRECT_URI).assertEvent();
-
- logoutResponse = oauth.doLogout(null, accessToken.getSessionState());
- assertEquals(200, logoutResponse.getStatusLine().getStatusCode());
- events.expectLogout(accessToken.getSessionState()).user((String) null).removeDetail(Details.REDIRECT_URI).error(Errors.USER_SESSION_NOT_FOUND).assertEvent();
+ HttpResponse logoutResponse = oauth.doLogout(response.getRefreshToken(), "secret");
+ assertEquals(204, logoutResponse.getStatusLine().getStatusCode());
+ events.expectLogout(accessToken.getSessionState()).client("resource-owner").removeDetail(Details.REDIRECT_URI).assertEvent();
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret");
assertEquals(400, response.getStatusCode());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 3a3f6c8..9a14580 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -171,11 +171,31 @@ public class OAuthClient {
return new AccessTokenResponse(client.execute(post));
}
- public HttpResponse doLogout(String redirectUri, String sessionState) throws IOException {
+ public HttpResponse doLogout(String refreshToken, String clientSecret) throws IOException {
HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(getLogoutUrl(redirectUri, sessionState));
+ HttpPost post = new HttpPost(getLogoutUrl(null, null));
- return client.execute(get);
+ List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+ if (refreshToken != null) {
+ parameters.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, refreshToken));
+ }
+ if (clientId != null && clientSecret != null) {
+ String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+ post.setHeader("Authorization", authorization);
+ }
+ else if (clientId != null) {
+ parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
+ }
+
+ UrlEncodedFormEntity formEntity;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ post.setEntity(formEntity);
+
+ return client.execute(post);
}
public AccessTokenResponse doRefreshTokenRequest(String refreshToken, String password) {