Using jwt to determine logout strategy

This commit is contained in:
DarioGii
2026-01-24 12:46:41 +00:00
parent c008198e13
commit d92079f59e
2 changed files with 45 additions and 46 deletions
@@ -3,12 +3,12 @@ package stirling.software.proprietary.security;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.core.io.Resource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
@@ -32,6 +32,8 @@ import stirling.software.common.util.UrlUtils;
import stirling.software.proprietary.audit.AuditEventType;
import stirling.software.proprietary.audit.AuditLevel;
import stirling.software.proprietary.audit.Audited;
import stirling.software.proprietary.security.model.AuthenticationType;
import stirling.software.proprietary.security.saml2.CertificateUtils;
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.proprietary.security.service.JwtServiceInterface;
@@ -72,23 +74,28 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
}
if (authentication != null) {
if (authentication instanceof OAuth2AuthenticationToken oAuthToken) {
// Handle OAuth2 logout redirection
getRedirectOauth2(request, response, oAuthToken);
} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
// Handle Username/Password logout
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
} else {
// Handle unknown authentication types
log.error(
"Authentication class unknown: {}",
authentication.getClass().getSimpleName());
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
// Check for JWT-based authentication and extract authType claim
String authType;
if (authentication instanceof JwtAuthenticationToken jwtAuthToken) {
authType =
(String)
jwtAuthToken
.getToken()
.getClaims()
.getOrDefault("authType", AuthenticationType.WEB);
log.debug("{} logout detected", authType);
switch (authType) {
case "OAUTH2" -> getAuthRedirect(request, response, jwtAuthToken);
case "SAML2" -> {}
default ->
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
}
}
} else {
// Redirect to login page after logout (handles error parameters if present)
String queryParams = checkForErrors(request);
getRedirectStrategy().sendRedirect(request, response, "/login?" + queryParams);
// Redirect to login page after logout
String path = checkForErrors(request);
getRedirectStrategy().sendRedirect(request, response, path);
}
}
}
@@ -230,33 +237,6 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
}
}
// Redirect for OAuth2 authentication logout
private void getRedirectOauth2(
HttpServletRequest request,
HttpServletResponse response,
OAuth2AuthenticationToken oAuthToken)
throws IOException {
String registrationId;
OAUTH2 oauth = securityProperties.getOauth2();
String path = checkForErrors(request);
String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path;
registrationId = oAuthToken.getAuthorizedClientRegistrationId();
// Redirect based on OAuth2 provider
switch (registrationId.toLowerCase(Locale.ROOT)) {
case "github", "google" -> {
// These providers don't support OIDC logout
log.info(
"No logout URL for {} available. Redirecting to local logout: {}",
registrationId,
redirectUrl);
response.sendRedirect(redirectUrl);
}
default -> handleOidcLogout(response, oAuthToken, oauth, redirectUrl);
}
}
// Redirect for JWT-based OAuth2 authentication logout
private void getAuthRedirect(
HttpServletRequest request,
@@ -306,6 +286,13 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
StringBuilder logoutUrlBuilder = new StringBuilder(endSessionEndpoint);
logoutUrlBuilder.append(endSessionEndpoint.contains("?") ? "&" : "?");
// Extract id_token from JWT claims for proper OIDC logout
String idToken = (String) jwtAuthenticationToken.getToken().getClaims().get("id_token");
if (idToken != null && !idToken.isBlank()) {
logoutUrlBuilder.append("id_token_hint=").append(idToken).append("&");
log.debug("Including id_token_hint in OIDC logout for proper SSO logout");
}
// Use client_id and post_logout_redirect_uri
if (clientId != null && !clientId.isBlank()) {
logoutUrlBuilder.append("client_id=").append(clientId).append("&");
@@ -14,6 +14,7 @@ import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
@@ -149,9 +150,20 @@ public class CustomOAuth2AuthenticationSuccessHandler
}
if (jwtService.isJwtEnabled()) {
String jwt =
jwtService.generateToken(
authentication, Map.of("authType", AuthenticationType.OAUTH2));
// Build JWT claims with authType and optionally id_token for OIDC logout
Map<String, Object> claims = new java.util.HashMap<>();
claims.put("authType", AuthenticationType.OAUTH2);
// Store OIDC id_token for proper SSO logout
if (principal instanceof OidcUser oidcUser) {
String idToken = oidcUser.getIdToken().getTokenValue();
if (idToken != null && !idToken.isBlank()) {
claims.put("id_token", idToken);
log.debug("Stored OIDC id_token in JWT claims for SSO logout");
}
}
String jwt = jwtService.generateToken(authentication, claims);
// Build context-aware redirect URL based on the original request
String redirectUrl =