mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Enhance SSO SAML in desktop app (#5705)
# Description of Changes Change the SAML support for SSO to understand when a request is coming from the desktop app, and use the alternate auth flow that the desktop app requires.
This commit is contained in:
+40
@@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.proprietary.audit.AuditEventType;
|
||||
import stirling.software.proprietary.audit.AuditLevel;
|
||||
import stirling.software.proprietary.audit.Audited;
|
||||
import stirling.software.proprietary.security.oauth2.TauriOAuthUtils;
|
||||
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
@@ -33,9 +34,33 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
|
||||
|
||||
if (exception instanceof Saml2AuthenticationException) {
|
||||
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
|
||||
if (TauriSamlUtils.isTauriRelayState(request)) {
|
||||
String redirectUrl =
|
||||
TauriOAuthUtils.defaultTauriCallbackPath(request.getContextPath());
|
||||
String nonce = TauriSamlUtils.extractNonceFromRequest(request);
|
||||
if (nonce != null) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "nonce", nonce);
|
||||
}
|
||||
redirectUrl = appendQueryParam(redirectUrl, "errorOAuth", error.getErrorCode());
|
||||
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
|
||||
return;
|
||||
}
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(request, response, "/login?errorOAuth=" + error.getErrorCode());
|
||||
} else if (exception instanceof ProviderNotFoundException) {
|
||||
if (TauriSamlUtils.isTauriRelayState(request)) {
|
||||
String redirectUrl =
|
||||
TauriOAuthUtils.defaultTauriCallbackPath(request.getContextPath());
|
||||
String nonce = TauriSamlUtils.extractNonceFromRequest(request);
|
||||
if (nonce != null) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "nonce", nonce);
|
||||
}
|
||||
redirectUrl =
|
||||
appendQueryParam(
|
||||
redirectUrl, "errorOAuth", "not_authentication_provider_found");
|
||||
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
|
||||
return;
|
||||
}
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(
|
||||
request,
|
||||
@@ -43,4 +68,19 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
|
||||
"/login?errorOAuth=not_authentication_provider_found");
|
||||
}
|
||||
}
|
||||
|
||||
private String appendQueryParam(String path, String key, String value) {
|
||||
if (path == null || path.isBlank()) {
|
||||
return path;
|
||||
}
|
||||
String separator = path.contains("?") ? "&" : "?";
|
||||
String encodedKey =
|
||||
java.net.URLEncoder.encode(key, java.nio.charset.StandardCharsets.UTF_8);
|
||||
String encodedValue =
|
||||
value == null
|
||||
? ""
|
||||
: java.net.URLEncoder.encode(
|
||||
value, java.nio.charset.StandardCharsets.UTF_8);
|
||||
return path + separator + encodedKey + "=" + encodedValue;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -33,6 +33,7 @@ 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.oauth2.TauriOAuthUtils;
|
||||
import stirling.software.proprietary.security.service.JwtServiceInterface;
|
||||
import stirling.software.proprietary.security.service.LoginAttemptService;
|
||||
import stirling.software.proprietary.security.service.UserService;
|
||||
@@ -233,7 +234,16 @@ public class CustomSaml2AuthenticationSuccessHandler
|
||||
String redirectPath = resolveRedirectPath(request, contextPath);
|
||||
String origin = resolveOrigin(request);
|
||||
clearRedirectCookie(response);
|
||||
return origin + redirectPath + "#access_token=" + jwt;
|
||||
String url = origin + redirectPath + "#access_token=" + jwt;
|
||||
|
||||
String nonce = TauriSamlUtils.extractNonceFromRequest(request);
|
||||
if (nonce != null) {
|
||||
url +=
|
||||
"&nonce="
|
||||
+ java.net.URLEncoder.encode(
|
||||
nonce, java.nio.charset.StandardCharsets.UTF_8);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,6 +266,9 @@ public class CustomSaml2AuthenticationSuccessHandler
|
||||
}
|
||||
|
||||
private String resolveRedirectPath(HttpServletRequest request, String contextPath) {
|
||||
if (TauriSamlUtils.isTauriRelayState(request)) {
|
||||
return TauriOAuthUtils.defaultTauriCallbackPath(contextPath);
|
||||
}
|
||||
return extractRedirectPathFromCookie(request)
|
||||
.filter(path -> path.startsWith("/"))
|
||||
.orElseGet(() -> defaultCallbackPath(contextPath));
|
||||
|
||||
+10
@@ -156,6 +156,16 @@ public class Saml2Configuration {
|
||||
OpenSaml4AuthenticationRequestResolver resolver =
|
||||
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||
|
||||
resolver.setRelayStateResolver(
|
||||
request -> {
|
||||
String tauriParam = request.getParameter("tauri");
|
||||
if (!"1".equals(tauriParam)) {
|
||||
return null;
|
||||
}
|
||||
String nonce = request.getParameter("nonce");
|
||||
return TauriSamlUtils.buildRelayState(nonce);
|
||||
});
|
||||
|
||||
resolver.setAuthnRequestCustomizer(
|
||||
customizer -> {
|
||||
HttpServletRequest request = customizer.getRequest();
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package stirling.software.proprietary.security.saml2;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/** Utility helpers for the Tauri desktop SAML flow. */
|
||||
public final class TauriSamlUtils {
|
||||
|
||||
public static final String TAURI_RELAY_STATE_PREFIX = "tauri:";
|
||||
|
||||
private TauriSamlUtils() {
|
||||
// Utility class - prevent instantiation
|
||||
}
|
||||
|
||||
public static boolean isTauriRelayState(HttpServletRequest request) {
|
||||
String relayState = request.getParameter("RelayState");
|
||||
return relayState != null
|
||||
&& (relayState.equals("tauri") || relayState.startsWith(TAURI_RELAY_STATE_PREFIX));
|
||||
}
|
||||
|
||||
public static String extractNonceFromRelayState(String relayState) {
|
||||
if (relayState == null || !relayState.startsWith(TAURI_RELAY_STATE_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = relayState.split(":");
|
||||
if (parts.length >= 2) {
|
||||
String nonce = parts[parts.length - 1];
|
||||
return nonce.isBlank() ? null : nonce;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String extractNonceFromRequest(HttpServletRequest request) {
|
||||
return extractNonceFromRelayState(request.getParameter("RelayState"));
|
||||
}
|
||||
|
||||
public static String buildRelayState(String nonce) {
|
||||
if (nonce == null || nonce.isBlank()) {
|
||||
return "tauri";
|
||||
}
|
||||
return TAURI_RELAY_STATE_PREFIX + nonce;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user