Skip to content

Commit

Permalink
KEYCLOAK-11773 Front-channel logout with identity brokering does not …
Browse files Browse the repository at this point in the history
…work after browser restart
  • Loading branch information
martin-kanis authored and stianst committed Dec 3, 2019
1 parent 5fc39da commit 73d1a26
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String
}

UserSessionModel userSession = null;
IDToken idToken = null;
if (encodedIdToken != null) {
try {
IDToken idToken = tokenManager.verifyIDTokenSignature(session, encodedIdToken);
idToken = tokenManager.verifyIDTokenSignature(session, encodedIdToken);
userSession = session.sessions().getUserSession(realm, idToken.getSessionState());

if (userSession != null) {
Expand All @@ -135,14 +136,14 @@ public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
if (authResult != null) {
userSession = userSession != null ? userSession : authResult.getSession();
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, authResult.getSession(), session.getContext().getUri(), clientConnection, headers, initiatingIdp);
logger.debug("finishing OIDC browser logout");
return response;
} else if (userSession != null) { // non browser logout
return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
}
else if (userSession != null) {
// identity cookie is missing but there's valid id_token_hint which matches session cookie => continue with browser logout
if (idToken != null && idToken.getSessionState().equals(AuthenticationManager.getSessionIdFromSessionCookie(session))) {
return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
}
// non browser logout
event.event(EventType.LOGOUT);
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
event.user(userSession.getUser()).session(userSession).success();
Expand Down Expand Up @@ -245,4 +246,14 @@ private void checkTokenIssuedAt(IDToken token, UserSessionModel userSession) thr
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,21 @@ public static Response redirectAfterSuccessfulFlow(KeycloakSession session, Real

}

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) {
String ssoAuth = clientSession.getNote(SSO_AUTH);
return Boolean.parseBoolean(ssoAuth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,15 @@ protected void logoutFromRealm(String realm) {
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)
+ "/auth/realms/" + realm
+ "/protocol/" + "openid-connect"
+ "/logout?redirect_uri=" + encodeUrl(getAccountUrl(realm))
+ (!StringUtils.isBlank(initiatingIdp) ? "&initiating_idp=" + initiatingIdp : "")
+ (!StringUtils.isBlank(tokenHint) ? "&id_token_hint=" + tokenHint : "")
);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.openqa.selenium.Cookie;

import javax.ws.rs.core.Response;
import java.util.List;
Expand Down Expand Up @@ -87,4 +89,22 @@ public void logoutWithOtherIdpAsInitiatinIdpLogsOutOfIdp() {
driver.navigate().to(getAccountUrl(REALM_PROV_NAME));
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);
}
}

0 comments on commit 73d1a26

Please sign in to comment.