GUACAMOLE-519: Implement Single Logout on OpenID Extension

This commit is contained in:
Safa Bayar committed 2026-01-17 22:25:55 -05:00
1 parent 27ac2d4168
commit d3d1a980da
4 files changed
+140 -3

No files matched your search

@@ -63,6 +63,28 @@ public interface SSOAuthenticationProviderService {
*/
URI getLoginURI() throws GuacamoleException;
/**
* Returns the full URI of the logout endpoint to which a user should be
* redirected when they log out from Guacamole. This allows the user to
* also log out from the SSO identity provider. If no logout endpoint is
* configured, null is returned.
*
* @param idToken
* The ID token or authentication token from the user's session, if
* available. This may be used as an id_token_hint parameter in the
* logout request. May be null if not available.
*
* @return
* The full URI of the SSO logout endpoint, or null if not configured.
*
* @throws GuacamoleException
* If configuration information required for generating the logout URI
* cannot be read.
*/
default URI getLogoutURI(String idToken) throws GuacamoleException {
return null;
}
/**
* Frees all resources associated with the relevant
* SSOAuthenticationProvider implementation. This function is automatically
@@ -70,5 +92,5 @@ public interface SSOAuthenticationProviderService {
* down.
*/
void shutdown();
}
@@ -19,9 +19,11 @@
package org.apache.guacamole.auth.sso;
import com.google.inject.Inject;
import java.net.URI;
import javax.ws.rs.core.Response;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import org.apache.guacamole.GuacamoleException;
/**
@@ -55,4 +57,33 @@ public class SSOResource {
return Response.seeOther(authService.getLoginURI()).build();
}
/**
* Redirects the user to the identity provider's logout endpoint, if
* configured. This allows the user to log out from both Guacamole and the
* SSO identity provider. If no logout endpoint is configured, a 204 No
* Content response is returned.
*
* @param idToken
* The ID token from the user's authentication session, if available.
* This will be included as the id_token_hint parameter in the logout
* request. May be null.
*
* @return
* An HTTP Response that will redirect the user to the IdP logout
* endpoint if configured, or a 204 No Content response otherwise.
*
* @throws GuacamoleException
* If an error occurs preventing the redirect from being created.
*/
@GET
@Path("logout")
public Response getLogoutRedirect(
@QueryParam("id_token") String idToken) throws GuacamoleException {
URI logoutURI = authService.getLogoutURI(idToken);
if (logoutURI != null)
return Response.seeOther(logoutURI).build();
else
return Response.noContent().build();
}
}
@@ -130,9 +130,33 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
.build();
}
@Override
public URI getLogoutURI(String idToken) throws GuacamoleException {
// If no logout endpoint is configured, return null
URI logoutEndpoint = confService.getLogoutEndpoint();
if (logoutEndpoint == null)
return null;
// Build the logout URI with appropriate parameters
UriBuilder logoutUriBuilder = UriBuilder.fromUri(logoutEndpoint);
// Add post_logout_redirect_uri parameter
logoutUriBuilder.queryParam("post_logout_redirect_uri",
confService.getPostLogoutRedirectURI());
// Add id_token_hint if available, otherwise add client_id
if (idToken != null && !idToken.isEmpty())
logoutUriBuilder.queryParam("id_token_hint", idToken);
else
logoutUriBuilder.queryParam("client_id", confService.getClientID());
return logoutUriBuilder.build();
}
@Override
public void shutdown() {
// Nothing to clean up
}
}
@@ -217,7 +217,32 @@ public class ConfigurationService {
@Override
public String getName() { return "openid-redirect-uri"; }
};
/**
* The logout endpoint (URI) of the OpenID service. If specified, users
* will be redirected to this endpoint when they log out, allowing them
* to log out from the OpenID provider as well.
*/
private static final URIGuacamoleProperty OPENID_LOGOUT_ENDPOINT =
new URIGuacamoleProperty() {
@Override
public String getName() { return "openid-logout-endpoint"; }
};
/**
* The URI that the OpenID service should redirect to after logout is
* complete. If not specified, the main redirect URI will be used.
*/
private static final URIGuacamoleProperty OPENID_POST_LOGOUT_REDIRECT_URI =
new URIGuacamoleProperty() {
@Override
public String getName() { return "openid-post-logout-redirect-uri"; }
};
/**
@@ -429,4 +454,39 @@ public class ConfigurationService {
return environment.getProperty(OPENID_MAX_NONCE_VALIDITY, DEFAULT_MAX_NONCE_VALIDITY);
}
/**
* Returns the logout endpoint (URI) of the OpenID service, as configured
* with guacamole.properties. If configured, users will be redirected to
* this endpoint when they log out from Guacamole.
*
* @return
* The logout endpoint of the OpenID service, as configured with
* guacamole.properties, or null if not configured.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public URI getLogoutEndpoint() throws GuacamoleException {
return environment.getProperty(OPENID_LOGOUT_ENDPOINT);
}
/**
* Returns the URI that the OpenID service should redirect to after logout
* is complete, as configured with guacamole.properties. If not configured,
* the main redirect URI will be used.
*
* @return
* The post-logout redirect URI, as configured with
* guacamole.properties, or the main redirect URI if not specified.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public URI getPostLogoutRedirectURI() throws GuacamoleException {
URI postLogoutRedirectURI = environment.getProperty(OPENID_POST_LOGOUT_REDIRECT_URI);
if (postLogoutRedirectURI != null)
return postLogoutRedirectURI;
return getRedirectURI();
}
}