KEYCLOAK-11773 Front-channel logout with identity brokering does not work after browser restart
This commit is contained in:
parent
5fc39daad3
commit
73d1a26040
4 changed files with 59 additions and 10 deletions
|
@ -116,9 +116,10 @@ public class LogoutEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionModel userSession = null;
|
UserSessionModel userSession = null;
|
||||||
|
IDToken idToken = null;
|
||||||
if (encodedIdToken != null) {
|
if (encodedIdToken != null) {
|
||||||
try {
|
try {
|
||||||
IDToken idToken = tokenManager.verifyIDTokenSignature(session, encodedIdToken);
|
idToken = tokenManager.verifyIDTokenSignature(session, encodedIdToken);
|
||||||
userSession = session.sessions().getUserSession(realm, idToken.getSessionState());
|
userSession = session.sessions().getUserSession(realm, idToken.getSessionState());
|
||||||
|
|
||||||
if (userSession != null) {
|
if (userSession != null) {
|
||||||
|
@ -135,14 +136,14 @@ public class LogoutEndpoint {
|
||||||
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
|
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
|
||||||
if (authResult != null) {
|
if (authResult != null) {
|
||||||
userSession = userSession != null ? userSession : authResult.getSession();
|
userSession = userSession != null ? userSession : authResult.getSession();
|
||||||
if (redirect != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirect);
|
return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
|
||||||
if (state != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_STATE_PARAM, state);
|
}
|
||||||
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, OIDCLoginProtocol.LOGIN_PROTOCOL);
|
else if (userSession != null) {
|
||||||
logger.debug("Initiating OIDC browser logout");
|
// identity cookie is missing but there's valid id_token_hint which matches session cookie => continue with browser logout
|
||||||
Response response = AuthenticationManager.browserLogout(session, realm, authResult.getSession(), session.getContext().getUri(), clientConnection, headers, initiatingIdp);
|
if (idToken != null && idToken.getSessionState().equals(AuthenticationManager.getSessionIdFromSessionCookie(session))) {
|
||||||
logger.debug("finishing OIDC browser logout");
|
return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
|
||||||
return response;
|
}
|
||||||
} else if (userSession != null) { // non browser logout
|
// non browser logout
|
||||||
event.event(EventType.LOGOUT);
|
event.event(EventType.LOGOUT);
|
||||||
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
|
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
|
||||||
event.user(userSession.getUser()).session(userSession).success();
|
event.user(userSession.getUser()).session(userSession).success();
|
||||||
|
@ -245,4 +246,14 @@ public class LogoutEndpoint {
|
||||||
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the user session started");
|
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the user session started");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Response initiateBrowserLogout(UserSessionModel userSession, String redirect, String state, String initiatingIdp ) {
|
||||||
|
if (redirect != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirect);
|
||||||
|
if (state != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_STATE_PARAM, state);
|
||||||
|
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
logger.debug("Initiating OIDC browser logout");
|
||||||
|
Response response = AuthenticationManager.browserLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, initiatingIdp);
|
||||||
|
logger.debug("finishing OIDC browser logout");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -800,6 +800,21 @@ public class AuthenticationManager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getSessionIdFromSessionCookie(KeycloakSession session) {
|
||||||
|
Cookie cookie = session.getContext().getRequestHeaders().getCookies().get(KEYCLOAK_SESSION_COOKIE);
|
||||||
|
if (cookie == null || "".equals(cookie.getValue())) {
|
||||||
|
logger.debugv("Could not find cookie: {0}", KEYCLOAK_SESSION_COOKIE);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = cookie.getValue().split("/", 3);
|
||||||
|
if (parts.length != 3) {
|
||||||
|
logger.debugv("Cannot parse session value from: {0}", KEYCLOAK_SESSION_COOKIE);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isSSOAuthentication(AuthenticatedClientSessionModel clientSession) {
|
public static boolean isSSOAuthentication(AuthenticatedClientSessionModel clientSession) {
|
||||||
String ssoAuth = clientSession.getNote(SSO_AUTH);
|
String ssoAuth = clientSession.getNote(SSO_AUTH);
|
||||||
return Boolean.parseBoolean(ssoAuth);
|
return Boolean.parseBoolean(ssoAuth);
|
||||||
|
|
|
@ -253,12 +253,15 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
||||||
logoutFromRealm(realm, null);
|
logoutFromRealm(realm, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void logoutFromRealm(String realm, String initiatingIdp) {
|
protected void logoutFromRealm(String realm, String initiatingIdp) { logoutFromRealm(realm, initiatingIdp, null); }
|
||||||
|
|
||||||
|
protected void logoutFromRealm(String realm, String initiatingIdp, String tokenHint) {
|
||||||
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
|
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
|
||||||
+ "/auth/realms/" + realm
|
+ "/auth/realms/" + realm
|
||||||
+ "/protocol/" + "openid-connect"
|
+ "/protocol/" + "openid-connect"
|
||||||
+ "/logout?redirect_uri=" + encodeUrl(getAccountUrl(realm))
|
+ "/logout?redirect_uri=" + encodeUrl(getAccountUrl(realm))
|
||||||
+ (!StringUtils.isBlank(initiatingIdp) ? "&initiating_idp=" + initiatingIdp : "")
|
+ (!StringUtils.isBlank(initiatingIdp) ? "&initiating_idp=" + initiatingIdp : "")
|
||||||
|
+ (!StringUtils.isBlank(tokenHint) ? "&id_token_hint=" + tokenHint : "")
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
import org.openqa.selenium.Cookie;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -87,4 +89,22 @@ public class KcOidcBrokerLogoutTest extends AbstractBaseBrokerTest {
|
||||||
driver.navigate().to(getAccountUrl(REALM_PROV_NAME));
|
driver.navigate().to(getAccountUrl(REALM_PROV_NAME));
|
||||||
waitForPage(driver, "log in to provider", true);
|
waitForPage(driver, "log in to provider", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logoutAfterBrowserRestart() {
|
||||||
|
logInAsUserInIDPForFirstTime();
|
||||||
|
assertLoggedInAccountManagement();
|
||||||
|
|
||||||
|
Cookie identityCookie = driver.manage().getCookieNamed(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE);
|
||||||
|
String idToken = identityCookie.getValue();
|
||||||
|
|
||||||
|
// simulate browser restart by deleting an identity cookie
|
||||||
|
log.debugf("Deleting %s cookie", AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE);
|
||||||
|
driver.manage().deleteCookieNamed(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE);
|
||||||
|
|
||||||
|
logoutFromRealm(bc.consumerRealmName(), null, idToken);
|
||||||
|
driver.navigate().to(getAccountUrl(REALM_PROV_NAME));
|
||||||
|
|
||||||
|
waitForPage(driver, "log in to provider", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue