Skip to content

Commit

Permalink
[ELY-2534] OIDC logout support
Browse files Browse the repository at this point in the history
  • Loading branch information
rsearls authored and fjuma committed Dec 20, 2024
1 parent 3aebc41 commit 9a61d67
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,24 @@ protected boolean removeEldestEntry(Map.Entry<String, OidcClientConfiguration> e
});

boolean tryLogout(OidcHttpFacade facade) {
log.trace("tryLogout entered");
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);
if (securityContext == null) {
// no active session
log.trace("tryLogout securityContext == null");
return false;
}

if (isRpInitiatedLogoutPath(facade)) {
log.trace("isRpInitiatedLogoutPath");
redirectEndSessionEndpoint(facade);
return true;
}

if (isLogoutCallbackPath(facade)) {
log.trace("isLogoutCallbackPath");
if (isFrontChannel(facade)) {
log.trace("isFrontChannel");
handleFrontChannelLogoutRequest(facade);
return true;
} else {
Expand All @@ -84,7 +89,6 @@ boolean tryLogout(OidcHttpFacade facade) {
facade.authenticationFailed();
}
}

return false;
}

Expand Down Expand Up @@ -119,6 +123,7 @@ private void redirectEndSessionEndpoint(OidcHttpFacade facade) {
}

logoutUri = redirectUriBuilder.build().toString();
log.trace("redirectEndSessionEndpoint path: " + redirectUriBuilder.toString());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
Expand All @@ -129,8 +134,11 @@ private void redirectEndSessionEndpoint(OidcHttpFacade facade) {
}

boolean tryBackChannelLogout(OidcHttpFacade facade) {
log.trace("tryBackChannelLogout entered");
if (isLogoutCallbackPath(facade)) {
log.trace("isLogoutCallbackPath");
if (isBackChannel(facade)) {
log.trace("isBackChannel");
handleBackChannelLogoutRequest(facade);
return true;
}
Expand Down Expand Up @@ -219,7 +227,7 @@ private String getRedirectUri(OidcHttpFacade facade) {
return uri;
}

boolean isLogoutCallbackPath(OidcHttpFacade facade) {
private boolean isLogoutCallbackPath(OidcHttpFacade facade) {
String path = facade.getRequest().getRelativePath();
return path.endsWith(getLogoutCallbackPath(facade));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public String getMechanismName() {

@Override
public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
String tmpURI = request.getRequestURI().toString();
log.debug("evaluateRequest uri: " + request.getRequestURI().toString());
OidcClientContext oidcClientContext = getOidcClientContext(request);
if (oidcClientContext == null) {
log.debugf("Ignoring request for path [%s] from mechanism [%s]. No client configuration context found.", request.getRequestURI(), getMechanismName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ protected OidcClientConfiguration internalBuild(final OidcJsonConfiguration oidc
oidcClientConfiguration.setTokenSignatureAlgorithm(oidcJsonConfiguration.getTokenSignatureAlgorithm());

String tmpLogoutPath = System.getProperty(LOGOUT_PATH);
log.debug("sysProp LOGOUT_PATH: " + (tmpLogoutPath == null ? "NULL" : tmpLogoutPath));
if (tmpLogoutPath != null) {
if (isValidPath(tmpLogoutPath)) {
oidcClientConfiguration.setLogoutPath(tmpLogoutPath);
Expand All @@ -210,6 +211,7 @@ protected OidcClientConfiguration internalBuild(final OidcJsonConfiguration oidc


String tmpLogoutCallbackPath = System.getProperty(LOGOUT_CALLBACK_PATH);
log.debug("sysProp LOGOUT_CALLBACK_PATH: " + (tmpLogoutCallbackPath == null ? "NULL" : tmpLogoutCallbackPath));
if (tmpLogoutCallbackPath != null) {
if (isValidPath(tmpLogoutCallbackPath)
&& !tmpLogoutCallbackPath.endsWith(oidcClientConfiguration.getLogoutPath())) {
Expand All @@ -225,6 +227,7 @@ protected OidcClientConfiguration internalBuild(final OidcJsonConfiguration oidc
}

String tmpPostLogoutPath = System.getProperty(POST_LOGOUT_PATH);
log.debug("sysProp POST_LOGOUT_PATH: " + (tmpPostLogoutPath == null ? "NULL" : tmpPostLogoutPath));
if (tmpPostLogoutPath != null) {
if (isValidPath(tmpPostLogoutPath)) {
oidcClientConfiguration.setPostLogoutPath(tmpPostLogoutPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ protected String getCode() {
protected String getRedirectUri(String state) {
String url = getRequestUrl();
log.debugf("callback uri: %s", url);

try {
if (! facade.getRequest().isSecure() && deployment.getSSLRequired().isRequired(facade.getRequest().getRemoteAddr())) {
int port = getSSLRedirectPort();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,29 @@ public OidcSessionTokenStore(OidcHttpFacade httpFacade) {
@Override
public void checkCurrentToken() {
HttpScope session = httpFacade.getScope(Scope.SESSION);
if (session == null || ! session.exists()) return;
if (session == null || ! session.exists()) {
return;
}
RefreshableOidcSecurityContext securityContext = (RefreshableOidcSecurityContext) session.getAttachment(OidcSecurityContext.class.getName());
if (securityContext == null) return;
if (securityContext == null) {
return;
}

// just in case session got serialized
if (securityContext.getOidcClientConfiguration() == null) {
securityContext.setCurrentRequestInfo(httpFacade.getOidcClientConfiguration(), this);
}

if (securityContext.isActive() && ! securityContext.getOidcClientConfiguration().isAlwaysRefreshToken()) return;
if (securityContext.isActive() && ! securityContext.getOidcClientConfiguration().isAlwaysRefreshToken()) {
return;
}

// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
boolean success = securityContext.refreshToken(false);
if (success && securityContext.isActive()) return;
if (success && securityContext.isActive()) {
return;
}

// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
session.setAttachment(OidcSecurityContext.class.getName(), null);
Expand Down Expand Up @@ -132,7 +140,6 @@ public void saveAccountInfo(OidcAccount account) {
}
});
}

session.setAttachment(OidcAccount.class.getName(), account);
session.setAttachment(OidcSecurityContext.class.getName(), account.getOidcSecurityContext());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ public void onBefore() throws Exception {
OidcBaseTest.client = new MockWebServer();
OidcBaseTest.client.start(new InetSocketAddress(0).getAddress(), CLIENT_PORT);
configureDispatcher();
RealmRepresentation realm = KeycloakConfiguration.getRealmRepresentation(TEST_REALM, CLIENT_ID, CLIENT_SECRET, CLIENT_HOST_NAME, CLIENT_PORT, CLIENT_APP, false);
RealmRepresentation realm = KeycloakConfiguration.getRealmRepresentation(
TEST_REALM, CLIENT_ID, CLIENT_SECRET, CLIENT_HOST_NAME, CLIENT_PORT,
CLIENT_APP, false);

realm.setAccessTokenLifespan(100);
realm.setSsoSessionMaxLifespan(100);
Expand Down Expand Up @@ -161,7 +163,6 @@ public ElytronDispatcher(HttpServerAuthenticationMechanism mechanism, Dispatcher
public MockResponse dispatch(RecordedRequest serverRequest) throws InterruptedException {
if (beforeDispatcher != null) {
MockResponse response = beforeDispatcher.dispatch(serverRequest);

if (response != null) {
return response;
}
Expand Down Expand Up @@ -225,7 +226,7 @@ protected void configureDispatcher(OidcClientConfiguration clientConfig, Dispatc
}

protected void assertUserNotAuthenticated() {
assertNull(getCurrentSession().getAttachment(OidcAccount.class.getName()));
assertNull(getCurrentSession());
}

protected void assertUserAuthenticated() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ private static String rewriteHost(String redirectUri) {
}

@Test
public void testRPInitiatedLogout() throws Exception {
public void testBackChannelLogout() throws Exception {
URI requestUri = new URI(getClientUrl());
WebClient webClient = getWebClient();

webClient.getPage(getClientUrl());
TestingHttpServerResponse response = getCurrentResponse();
assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusCode());
Expand All @@ -73,8 +74,6 @@ public void testRPInitiatedLogout() throws Exception {
// logged out after finishing the redirections during frontchannel logout
assertUserAuthenticated();
webClient.getPage(getClientUrl() + getClientConfig().getLogoutPath());
assertUserAuthenticated();
webClient.getPage(getClientUrl());
assertUserNotAuthenticated();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ protected void doConfigureClient(ClientRepresentation client) {
client.setFrontchannelLogout(true);
List<String> redirectUris = client.getRedirectUris();
String redirectUri = redirectUris.get(0);

OidcClientConfiguration ocConfig = new OidcClientConfiguration();
OidcClientConfiguration config = new OidcClientConfiguration();
client.getAttributes().put("frontchannel.logout.url", redirectUri
+ ocConfig.getLogoutCallbackPath());
+ config.getLogoutCallbackPath());
}

@Test
Expand Down Expand Up @@ -101,7 +100,7 @@ public MockResponse dispatch(RecordedRequest request) {
assertTrue(page.getWebResponse().getContentAsString().contains("Welcome, authenticated user"));

assertUserAuthenticated();
HtmlPage continueLogout = webClient.getPage(getClientUrl() + "/logout");
HtmlPage continueLogout = webClient.getPage(getClientUrl() + getClientConfig().getLogoutPath());
page = continueLogout.getElementById("continue").click();
assertUserNotAuthenticated();
assertTrue(page.getWebResponse().getContentAsString().contains("you are logged out from app"));
Expand All @@ -126,4 +125,4 @@ public void testFrontChannelLogout() throws Exception {
client.setDispatcher(new QueueDispatcher());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ public boolean resumeRequest() {
}

public HttpScope getScope(Scope scope) {
if (requestURI != null && "/clientApp/logout/callback".equals(requestURI.getPath())){
return null;
}
if (scope.equals(Scope.SSL_SESSION)) {
return null;
} else if (sessionScope != null) {
Expand Down

0 comments on commit 9a61d67

Please sign in to comment.