removing /logout from filters

This commit is contained in:
Dario Ghunney Ware
2026-01-24 12:46:41 +00:00
committed by DarioGii
parent 817f43613f
commit ec72cfd7e2
4 changed files with 97 additions and 14 deletions
@@ -158,7 +158,6 @@ public class RequestUriUtils {
// Public auth endpoints that don't require authentication
return trimmedUri.startsWith("/login")
|| trimmedUri.startsWith("/logout")
|| trimmedUri.startsWith("/auth/")
|| trimmedUri.startsWith("/oauth2")
|| trimmedUri.startsWith("/saml2")
@@ -13,6 +13,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
@@ -244,7 +245,6 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Redirect based on OAuth2 provider
switch (registrationId.toLowerCase(Locale.ROOT)) {
case "keycloak" -> handleOidcLogout(request, response, oAuthToken, oauth, redirectUrl);
case "github", "google" -> {
// These providers don't support OIDC logout
log.info(
@@ -253,10 +253,69 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
redirectUrl);
response.sendRedirect(redirectUrl);
}
default -> {
// Try generic OIDC logout for any other provider
handleOidcLogout(request, response, oAuthToken, oauth, redirectUrl);
default -> handleOidcLogout(response, oAuthToken, oauth, redirectUrl);
}
}
// Redirect for JWT-based OAuth2 authentication logout
private void handleJwtOAuth2Logout(HttpServletRequest request, HttpServletResponse response)
throws IOException {
OAUTH2 oauth = securityProperties.getOauth2();
String path = checkForErrors(request);
String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path;
// For JWT-based auth, we don't have OAuth2AuthenticationToken
// Attempt generic OIDC logout
String issuer = null;
String clientId = null;
if (oauth.getClient() != null && oauth.getClient().getKeycloak() != null) {
KeycloakProvider keycloak = oauth.getClient().getKeycloak();
if (keycloak.getIssuer() != null && !keycloak.getIssuer().isBlank()) {
issuer = keycloak.getIssuer();
clientId = keycloak.getClientId();
} else if (oauth.getIssuer() != null && !oauth.getIssuer().isBlank()) {
issuer = oauth.getIssuer();
clientId = oauth.getClientId();
}
} else if (oauth.getIssuer() != null && !oauth.getIssuer().isBlank()) {
issuer = oauth.getIssuer();
clientId = oauth.getClientId();
}
String endSessionEndpoint = getEndSessionEndpoint(oauth, issuer);
// If no endpoint found, try Keycloak fallback
if (endSessionEndpoint == null && issuer != null) {
endSessionEndpoint = issuer + "/protocol/openid-connect/logout";
log.debug("Using Keycloak fallback logout path: {}", endSessionEndpoint);
}
// If we have an endpoint, construct the logout URL
if (endSessionEndpoint != null) {
StringBuilder logoutUrlBuilder = new StringBuilder(endSessionEndpoint);
logoutUrlBuilder.append(endSessionEndpoint.contains("?") ? "&" : "?");
// Without OAuth2AuthenticationToken, we don't have id_token_hint
// Just use client_id and post_logout_redirect_uri
if (clientId != null && !clientId.isBlank()) {
logoutUrlBuilder.append("client_id=").append(clientId);
logoutUrlBuilder.append("&");
}
logoutUrlBuilder
.append("post_logout_redirect_uri=")
.append(response.encodeRedirectURL(redirectUrl));
String logoutUrl = logoutUrlBuilder.toString();
log.info("JWT-based OAuth2 logout URL: {}", logoutUrl);
response.sendRedirect(logoutUrl);
} else {
// No OIDC logout endpoint available - fallback to local logout
log.info(
"No OIDC logout endpoint available for issuer: {}. Using local logout: {}",
issuer,
redirectUrl);
response.sendRedirect(redirectUrl);
}
}
@@ -265,7 +324,6 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
* Discovered endpoint 3. Keycloak fallback (if isKeycloak=true) 4. Local logout
*/
private void handleOidcLogout(
HttpServletRequest request,
HttpServletResponse response,
OAuth2AuthenticationToken oAuthToken,
OAUTH2 oauth,
@@ -6,17 +6,20 @@ import static stirling.software.proprietary.security.model.AuthenticationType.OA
import static stirling.software.proprietary.security.model.AuthenticationType.SAML2;
import static stirling.software.proprietary.security.model.AuthenticationType.WEB;
import io.jsonwebtoken.Jwts;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
import java.util.Optional;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -26,7 +29,6 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties;
@@ -40,7 +42,6 @@ import stirling.software.proprietary.security.service.JwtServiceInterface;
import stirling.software.proprietary.security.service.UserService;
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtServiceInterface jwtService;
@@ -49,6 +50,19 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final AuthenticationEntryPoint authenticationEntryPoint;
private final ApplicationProperties.Security securityProperties;
public JwtAuthenticationFilter(
JwtServiceInterface jwtService,
UserService userService,
CustomUserDetailsService userDetailsService,
AuthenticationEntryPoint authenticationEntryPoint,
ApplicationProperties.Security securityProperties) {
this.jwtService = jwtService;
this.userService = userService;
this.userDetailsService = userDetailsService;
this.authenticationEntryPoint = authenticationEntryPoint;
this.securityProperties = securityProperties;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
@@ -111,7 +125,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String tokenUsername = claims.get("sub").toString();
try {
authenticate(request, claims);
authenticate(request, jwtToken, claims);
} catch (SQLException | UnsupportedProviderException e) {
log.error("Error processing user authentication for user: {}", tokenUsername, e);
handleAuthenticationFailure(
@@ -165,7 +179,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
return true;
}
private void authenticate(HttpServletRequest request, Map<String, Object> claims)
private void authenticate(
HttpServletRequest request, String jwtToken, Map<String, Object> claims)
throws SQLException, UnsupportedProviderException {
String username = claims.get("sub").toString();
@@ -174,9 +189,14 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails != null) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
Jwt jwt =
Jwt.withTokenValue(jwtToken)
.headers(headers -> headers.put("alg", Jwts.SIG.RS256.getId()))
.claims(claimsMap -> claimsMap.putAll(claims))
.build();
JwtAuthenticationToken authToken =
new JwtAuthenticationToken(jwt, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.debug("Setting authentication for user: {}", username);
+6
View File
@@ -102,6 +102,12 @@ export default defineConfig(({ mode }) => {
secure: false,
xfwd: true,
},
'/logout': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false,
xfwd: true,
},
},
},
base: process.env.RUN_SUBPATH ? `/${process.env.RUN_SUBPATH}` : './',