Remove root auth session after backchannel logout
Closes #32197 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
cb765c8d73
commit
b46fab2308
8 changed files with 65 additions and 40 deletions
|
@ -308,8 +308,7 @@ public class AuthenticationManager {
|
||||||
checkUserSessionOnlyHasLoggedOutClients(realm, userSession, logoutAuthSession);
|
checkUserSessionOnlyHasLoggedOutClients(realm, userSession, logoutAuthSession);
|
||||||
} finally {
|
} finally {
|
||||||
logger.tracef("Removing logout session '%s' after backchannel logout", logoutAuthSession.getParentSession().getId());
|
logger.tracef("Removing logout session '%s' after backchannel logout", logoutAuthSession.getParentSession().getId());
|
||||||
RootAuthenticationSessionModel rootAuthSession = logoutAuthSession.getParentSession();
|
session.authenticationSessions().removeRootAuthenticationSession(realm, logoutAuthSession.getParentSession());
|
||||||
rootAuthSession.removeAuthenticationSessionByTabId(logoutAuthSession.getTabId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userSession.setState(UserSessionModel.State.LOGGED_OUT);
|
userSession.setState(UserSessionModel.State.LOGGED_OUT);
|
||||||
|
|
|
@ -79,6 +79,10 @@ public class BrowserTabUtil implements AutoCloseable {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void cleanup() {
|
||||||
|
instances = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
public WebDriver getDriver() {
|
public WebDriver getDriver() {
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
@ -155,4 +159,4 @@ public class BrowserTabUtil implements AutoCloseable {
|
||||||
public void close() {
|
public void close() {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.keycloak.testsuite.auth.page.login.OIDCLogin;
|
||||||
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
|
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
|
||||||
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||||
|
import org.keycloak.testsuite.util.BrowserTabUtil;
|
||||||
import org.keycloak.testsuite.util.CryptoInitRule;
|
import org.keycloak.testsuite.util.CryptoInitRule;
|
||||||
import org.keycloak.testsuite.util.DroneUtils;
|
import org.keycloak.testsuite.util.DroneUtils;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
@ -258,6 +259,7 @@ public abstract class AbstractKeycloakTest {
|
||||||
|
|
||||||
// Remove all browsers from queue
|
// Remove all browsers from queue
|
||||||
DroneUtils.resetQueue();
|
DroneUtils.resetQueue();
|
||||||
|
BrowserTabUtil.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TestCleanup getCleanup(String realmName) {
|
protected TestCleanup getCleanup(String realmName) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.LoginTotpPage;
|
import org.keycloak.testsuite.pages.LoginTotpPage;
|
||||||
import org.keycloak.testsuite.pages.LoginUsernameOnlyPage;
|
import org.keycloak.testsuite.pages.LoginUsernameOnlyPage;
|
||||||
import org.keycloak.testsuite.pages.PasswordPage;
|
import org.keycloak.testsuite.pages.PasswordPage;
|
||||||
|
import org.keycloak.testsuite.util.BrowserTabUtil;
|
||||||
import org.keycloak.testsuite.util.FederatedIdentityBuilder;
|
import org.keycloak.testsuite.util.FederatedIdentityBuilder;
|
||||||
import org.keycloak.testsuite.util.FlowUtil;
|
import org.keycloak.testsuite.util.FlowUtil;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
@ -366,6 +367,42 @@ public class ReAuthenticationTest extends AbstractTestRealmKeycloakTest {
|
||||||
realmsResouce().realm(rep.getRealm()).update(rep);
|
realmsResouce().realm(rep.getRealm()).update(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginAfterLogoutWithDifferentSessionId() {
|
||||||
|
BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver);
|
||||||
|
|
||||||
|
assertThat(tabUtil.getCountOfTabs(), Matchers.is(1));
|
||||||
|
oauth.openLoginForm();
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
tabUtil.newTab(oauth.getLoginFormUrl());
|
||||||
|
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(2));
|
||||||
|
oauth.openLoginForm();
|
||||||
|
|
||||||
|
tabUtil.closeTab(tabUtil.getCountOfTabs() - 1);
|
||||||
|
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(1));
|
||||||
|
|
||||||
|
tabUtil.switchToTab(0);
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
|
OAuthClient.AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password");
|
||||||
|
AccessToken accessToken1 = oauth.verifyToken(response1.getAccessToken());
|
||||||
|
|
||||||
|
oauth.doLogout(response1.getRefreshToken(), "password");
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
|
OAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(code, "password");
|
||||||
|
AccessToken accessToken2 = oauth.verifyToken(response2.getAccessToken());
|
||||||
|
|
||||||
|
Assert.assertNotEquals(accessToken1.getId(), accessToken2.getId());
|
||||||
|
Assert.assertNotEquals(accessToken1.getSessionId(), accessToken2.getSessionId());
|
||||||
|
}
|
||||||
|
|
||||||
private void setupIdentityFirstFlow() {
|
private void setupIdentityFirstFlow() {
|
||||||
String newFlowAlias = "browser - identity first";
|
String newFlowAlias = "browser - identity first";
|
||||||
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
||||||
|
|
|
@ -146,30 +146,14 @@ public class LogoutTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
|
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
|
|
||||||
Assert.assertFalse(loginPage.isCurrent());
|
Assert.assertFalse(loginPage.isCurrent());
|
||||||
|
|
||||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
OAuthClient.AccessTokenResponse tokenResponse2 = oauth.doAccessTokenRequest(code, "password");
|
OAuthClient.AccessTokenResponse tokenResponse2 = oauth.doAccessTokenRequest(code, "password");
|
||||||
|
|
||||||
// POST logout with token should fail
|
|
||||||
try (CloseableHttpResponse response = oauth.doLogout(refreshToken1, "password")) {
|
|
||||||
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
String logoutUrl = oauth.getLogoutUrl()
|
|
||||||
.idTokenHint(accessTokenResponse.getIdToken())
|
|
||||||
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// GET logout with ID token should fail as well
|
|
||||||
try (CloseableHttpClient c = HttpClientBuilder.create().disableRedirectHandling().build();
|
|
||||||
CloseableHttpResponse response = c.execute(new HttpGet(logoutUrl))) {
|
|
||||||
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally POST logout with VALID token should succeed
|
// finally POST logout with VALID token should succeed
|
||||||
try (CloseableHttpResponse response = oauth.doLogout(tokenResponse2.getRefreshToken(), "password")) {
|
try (CloseableHttpResponse response = oauth.doLogout(tokenResponse2.getRefreshToken(), "password")) {
|
||||||
MatcherAssert.assertThat(response, Matchers.statusCodeIsHC(Status.NO_CONTENT));
|
MatcherAssert.assertThat(response, Matchers.statusCodeIsHC(Status.NO_CONTENT));
|
||||||
|
@ -178,7 +162,6 @@ public class LogoutTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postLogoutFailWithCredentialsOfDifferentClient() throws Exception {
|
public void postLogoutFailWithCredentialsOfDifferentClient() throws Exception {
|
||||||
oauth.doLogin("test-user@localhost", "password");
|
oauth.doLogin("test-user@localhost", "password");
|
||||||
|
@ -247,7 +230,7 @@ public class LogoutTest extends AbstractKeycloakTest {
|
||||||
.idTokenHint(idTokenString)
|
.idTokenHint(idTokenString)
|
||||||
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT)
|
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (CloseableHttpClient c = HttpClientBuilder.create().disableRedirectHandling().build();
|
try (CloseableHttpClient c = HttpClientBuilder.create().disableRedirectHandling().build();
|
||||||
CloseableHttpResponse response = c.execute(new HttpGet(logoutUrl))) {
|
CloseableHttpResponse response = c.execute(new HttpGet(logoutUrl))) {
|
||||||
MatcherAssert.assertThat(response, Matchers.statusCodeIsHC(Status.FOUND));
|
MatcherAssert.assertThat(response, Matchers.statusCodeIsHC(Status.FOUND));
|
||||||
|
|
|
@ -1070,8 +1070,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
||||||
try {
|
try {
|
||||||
// Continue with login
|
// Continue with login
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
|
|
||||||
assertFalse(loginPage.isCurrent());
|
assertFalse(loginPage.isCurrent());
|
||||||
|
|
||||||
|
@ -1104,8 +1104,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
||||||
try {
|
try {
|
||||||
// Continue with login
|
// Continue with login
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
|
|
||||||
assertFalse(loginPage.isCurrent());
|
assertFalse(loginPage.isCurrent());
|
||||||
|
|
||||||
|
@ -1137,8 +1137,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// Continue with login
|
// Continue with login
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
|
|
||||||
assertFalse(loginPage.isCurrent());
|
assertFalse(loginPage.isCurrent());
|
||||||
|
|
||||||
|
|
|
@ -250,8 +250,8 @@ public class TokenIntrospectionTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
|
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
events.expectLogin().assertEvent();
|
events.expectLogin().assertEvent();
|
||||||
|
|
||||||
Assert.assertFalse(loginPage.isCurrent());
|
Assert.assertFalse(loginPage.isCurrent());
|
||||||
|
|
|
@ -207,7 +207,7 @@ public class UserInfoTest extends AbstractKeycloakTest {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_postMethod_charset_body() throws Exception {
|
public void testSuccess_postMethod_charset_body() throws Exception {
|
||||||
Client client = AdminClientUtil.createResteasyClient();
|
Client client = AdminClientUtil.createResteasyClient();
|
||||||
|
@ -531,7 +531,7 @@ public class UserInfoTest extends AbstractKeycloakTest {
|
||||||
public void testSuccessSignedResponseRS256AcceptJWT() throws Exception {
|
public void testSuccessSignedResponseRS256AcceptJWT() throws Exception {
|
||||||
testSuccessSignedResponse(Algorithm.RS256, MediaType.APPLICATION_JWT);
|
testSuccessSignedResponse(Algorithm.RS256, MediaType.APPLICATION_JWT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionExpired() {
|
public void testSessionExpired() {
|
||||||
Client client = AdminClientUtil.createResteasyClient();
|
Client client = AdminClientUtil.createResteasyClient();
|
||||||
|
@ -607,8 +607,8 @@ public class UserInfoTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
setTimeOffset(2);
|
setTimeOffset(2);
|
||||||
|
|
||||||
WaitUtils.waitForPageToLoad();
|
driver.navigate().refresh();
|
||||||
loginPage.login("password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
events.expectLogin().assertEvent();
|
events.expectLogin().assertEvent();
|
||||||
|
|
||||||
Assert.assertFalse(loginPage.isCurrent());
|
Assert.assertFalse(loginPage.isCurrent());
|
||||||
|
@ -630,7 +630,7 @@ public class UserInfoTest extends AbstractKeycloakTest {
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
events.expect(EventType.USER_INFO_REQUEST_ERROR)
|
events.expect(EventType.USER_INFO_REQUEST_ERROR)
|
||||||
.error(Errors.INVALID_TOKEN)
|
.error(Errors.USER_SESSION_NOT_FOUND)
|
||||||
.user(Matchers.nullValue(String.class))
|
.user(Matchers.nullValue(String.class))
|
||||||
.session(Matchers.nullValue(String.class))
|
.session(Matchers.nullValue(String.class))
|
||||||
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
|
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
|
||||||
|
@ -1088,23 +1088,23 @@ public class UserInfoTest extends AbstractKeycloakTest {
|
||||||
assertNull(userInfo.getOtherClaims().get("realm_access"));
|
assertNull(userInfo.getOtherClaims().get("realm_access"));
|
||||||
assertNull(userInfo.getOtherClaims().get("resource_access"));
|
assertNull(userInfo.getOtherClaims().get("resource_access"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_noContentType() throws Exception {
|
public void test_noContentType() throws Exception {
|
||||||
Client client = AdminClientUtil.createResteasyClient();
|
Client client = AdminClientUtil.createResteasyClient();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client);
|
AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client);
|
||||||
|
|
||||||
WebTarget userInfoTarget = UserInfoClientUtil.getUserInfoWebTarget(client);
|
WebTarget userInfoTarget = UserInfoClientUtil.getUserInfoWebTarget(client);
|
||||||
Response response = userInfoTarget.request()
|
Response response = userInfoTarget.request()
|
||||||
.header(HttpHeaders.AUTHORIZATION, "bearer " + accessTokenResponse.getToken())
|
.header(HttpHeaders.AUTHORIZATION, "bearer " + accessTokenResponse.getToken())
|
||||||
.build("POST")
|
.build("POST")
|
||||||
.invoke();
|
.invoke();
|
||||||
|
|
||||||
Assert.assertEquals(200, response.getStatus());
|
Assert.assertEquals(200, response.getStatus());
|
||||||
Assert.assertEquals("OK", response.getStatusInfo().toString());
|
Assert.assertEquals("OK", response.getStatusInfo().toString());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue