mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Redirect using client data when the session has expired (#39330)
Closes #36150 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
c76bb0683c
commit
6007dc959b
@ -43,6 +43,8 @@ import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||
import org.keycloak.protocol.ClientData;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.RestartLoginCookie;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
@ -448,6 +450,19 @@ public class SessionCodeChecks {
|
||||
flowPath = LoginActionsService.AUTHENTICATE_PATH;
|
||||
}
|
||||
|
||||
//set redirect uri and other notes from client data parameter
|
||||
try {
|
||||
ClientData clientData = ClientData.decodeClientDataFromParameter(clientDataString);
|
||||
if (RedirectUtils.verifyRedirectUri(session, clientData.getRedirectUri(), authSession.getClient()) != null) {
|
||||
authSession.setRedirectUri(clientData.getRedirectUri());
|
||||
authSession.setClientNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, clientData.getResponseType());
|
||||
authSession.setClientNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, clientData.getResponseMode());
|
||||
authSession.setClientNote(OIDCLoginProtocol.STATE_PARAM, clientData.getState());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debugf(e, "ClientData parameter in invalid format. ClientData parameter was %s", clientDataString);
|
||||
}
|
||||
|
||||
String clientData = AuthenticationProcessor.getClientData(session, authSession);
|
||||
URI redirectUri = getLastExecutionUrl(flowPath, null, authSession.getTabId(), clientData);
|
||||
logger.debugf("Authentication session restart from cookie succeeded. Redirecting to %s", redirectUri);
|
||||
|
||||
@ -23,10 +23,12 @@ import static org.keycloak.testsuite.AssertEvents.DEFAULT_REDIRECT_URI;
|
||||
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.core.UriBuilder;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
@ -41,6 +43,7 @@ import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.ClientData;
|
||||
import org.keycloak.protocol.RestartLoginCookie;
|
||||
import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
|
||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||
@ -727,6 +730,156 @@ public class MultipleTabsLoginTest extends AbstractChangeImportedUserPasswordsTe
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectToCorrectUrlAfterAuthSessionExpiration() {
|
||||
|
||||
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
|
||||
|
||||
String redirectUri1 = String.format("%s/auth/realms/master/app/auth/suffix1", getAuthServerContextRoot());
|
||||
String redirectUri2 = String.format("%s/auth/realms/master/app/auth/suffix2", getAuthServerContextRoot());
|
||||
|
||||
//open tab 1 with redirect uri 1
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.is(1));
|
||||
oauth.redirectUri(redirectUri1);
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab1: " + driver.getCurrentUrl());
|
||||
|
||||
//open tab 2 with redirect uri 2
|
||||
oauth.redirectUri(redirectUri2);
|
||||
tabUtil.newTab(oauth.loginForm().build());
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(2));
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab2: " + driver.getCurrentUrl());
|
||||
|
||||
// Wait until authentication session expires
|
||||
setTimeOffset(7200000);
|
||||
|
||||
//triggers the postponed function in authChecker.js to check if the auth session cookie has changed
|
||||
WaitUtils.pause(2000);
|
||||
|
||||
// Go back to tab1
|
||||
tabUtil.closeTab(1);
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(1));
|
||||
|
||||
getLogger().info("URL in tab1 after close: " + driver.getCurrentUrl());
|
||||
|
||||
// Try to login in tab2. After fill login form, the login will be restarted (due KC_RESTART cookie). User can continue login
|
||||
loginPage.login("login-test", getPassword("login-test"));
|
||||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError());
|
||||
events.clear();
|
||||
loginSuccessAndDoRequiredActions();
|
||||
|
||||
getLogger().info("URL in after: " + driver.getCurrentUrl());
|
||||
|
||||
//redirected url should be the redirect uri 1
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(redirectUri1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartFailureWithDifferentClientAfterAuthSessionExpiration() {
|
||||
|
||||
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
|
||||
|
||||
String redirectUri1 = String.format("%s/auth/realms/master/app/auth/suffix1", getAuthServerContextRoot());
|
||||
String redirectUri2 = String.format("%s/foo/bar/baz", getAuthServerContextRoot());
|
||||
|
||||
//open tab 1 with redirect uri 1
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.is(1));
|
||||
oauth.redirectUri(redirectUri1);
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab1: " + driver.getCurrentUrl());
|
||||
|
||||
//open tab 2 with redirect uri 2 and different client
|
||||
oauth.client("root-url-client");
|
||||
oauth.redirectUri(redirectUri2);
|
||||
tabUtil.newTab(oauth.loginForm().build());
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(2));
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab2: " + driver.getCurrentUrl());
|
||||
// Wait until authentication session expires
|
||||
setTimeOffset(7200000);
|
||||
|
||||
//triggers the postponed function in authChecker.js to check if the auth session cookie has changed
|
||||
WaitUtils.pause(2000);
|
||||
|
||||
// Go back to tab1
|
||||
tabUtil.closeTab(1);
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(1));
|
||||
|
||||
// Try to login in tab1.
|
||||
loginPage.login("login-test", getPassword("login-test"));
|
||||
|
||||
//assert cookie not found
|
||||
events.expect(EventType.LOGIN_ERROR)
|
||||
.user(new UserRepresentation())
|
||||
.error(Errors.COOKIE_NOT_FOUND)
|
||||
.assertEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectRedirectUriInClientDataAfterAuthSessionExpiration() throws IOException {
|
||||
|
||||
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
|
||||
|
||||
String redirectUri1 = String.format("%s/auth/realms/master/app/auth/suffix1", getAuthServerContextRoot());
|
||||
String redirectUri2 = String.format("%s/auth/realms/master/app/auth/suffix2", getAuthServerContextRoot());
|
||||
String redirectUriInject = String.format("%s/auth/realms/master/app/authFake/suffix1", getAuthServerContextRoot());
|
||||
|
||||
//open tab 1 with redirect uri 1
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.is(1));
|
||||
oauth.redirectUri(redirectUri1);
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab1: " + driver.getCurrentUrl());
|
||||
|
||||
//login with wrong credentials to move to authenticate page with clientData param
|
||||
loginPage.login("wrong", "wrong");
|
||||
|
||||
//open tab 2
|
||||
oauth.redirectUri(redirectUri2);
|
||||
tabUtil.newTab(oauth.loginForm().build());
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(2));
|
||||
loginPage.assertCurrent();
|
||||
getLogger().info("URL in tab2: " + driver.getCurrentUrl());
|
||||
|
||||
// Wait until authentication session expires
|
||||
setTimeOffset(7200000);
|
||||
|
||||
//triggers the postponed function in authChecker.js to check if the auth session cookie has changed
|
||||
WaitUtils.pause(2000);
|
||||
|
||||
// Go back to tab1
|
||||
tabUtil.closeTab(1);
|
||||
assertThat(tabUtil.getCountOfTabs(), Matchers.equalTo(1));
|
||||
|
||||
//replace clientData param injecting a different redirect uri
|
||||
String currentClientDataString = ActionURIUtils.parseQueryParamsFromActionURI(oauth.getDriver().getCurrentUrl()).get(CLIENT_DATA);
|
||||
ClientData clientData = ClientData.decodeClientDataFromParameter(currentClientDataString);
|
||||
clientData.setRedirectUri(redirectUriInject);
|
||||
|
||||
String injectedUrl = UriBuilder.fromUri(oauth.getDriver().getCurrentUrl())
|
||||
.replaceQueryParam(CLIENT_DATA, clientData.encode())
|
||||
.build().toString();
|
||||
|
||||
oauth.getDriver().navigate().to(injectedUrl);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError());
|
||||
events.clear();
|
||||
|
||||
loginPage.assertCurrent();
|
||||
loginSuccessAndDoRequiredActions();
|
||||
|
||||
//injected redirected url should be ignored
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(redirectUri2));
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForAppPage(Runnable htmlUnitAction) {
|
||||
if (driver instanceof HtmlUnitDriver) {
|
||||
// authChecker.js javascript does not work with HtmlUnitDriver. So need to "refresh" the current browser tab by running the last action in order to simulate "already_logged_in"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user