diff --git a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java
index 6f10f8522..d8228d24d 100644
--- a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java
+++ b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java
@@ -67,16 +67,15 @@ public class RequestUriUtils {
// Public auth endpoints that don't require authentication
return trimmedUri.startsWith("/login")
- || trimmedUri.startsWith("/signup")
|| trimmedUri.startsWith("/register")
|| trimmedUri.startsWith("/auth/")
|| trimmedUri.startsWith("/oauth2")
|| trimmedUri.startsWith("/saml2")
+ || trimmedUri.contains("/login/oauth2/code/") // Spring Security OAuth2 callback
+ || trimmedUri.contains("/oauth2/authorization/") // OAuth2 authorization endpoint
|| trimmedUri.startsWith("/api/v1/auth/login")
- || trimmedUri.startsWith("/api/v1/auth/register")
|| trimmedUri.startsWith("/api/v1/auth/refresh")
|| trimmedUri.startsWith("/api/v1/auth/logout")
- || trimmedUri.startsWith("/api/v1/user/register")
|| trimmedUri.startsWith("/api/v1/proprietary/ui-data/account")
|| trimmedUri.startsWith("/api/v1/config")
|| trimmedUri.startsWith("/v1/api-docs")
diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/controller/web/ReactRoutingController.java b/app/proprietary/src/main/java/stirling/software/proprietary/controller/web/ReactRoutingController.java
new file mode 100644
index 000000000..2e45fdf99
--- /dev/null
+++ b/app/proprietary/src/main/java/stirling/software/proprietary/controller/web/ReactRoutingController.java
@@ -0,0 +1,58 @@
+package stirling.software.proprietary.controller.web;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * Controller to handle OAuth2 callback routing. Returns an HTML page that preserves the URL
+ * fragment (which contains the JWT token) and redirects to the frontend.
+ */
+@Controller
+public class ReactRoutingController {
+
+ @Value("${frontend.dev.url:http://localhost:5173}")
+ private String frontendDevUrl;
+
+ @Value("${app.dev.mode:true}")
+ private boolean devMode;
+
+ /**
+ * Handle /auth/callback by returning HTML that preserves the fragment and redirects to the
+ * frontend.
+ */
+ @GetMapping(value = "/auth/callback", produces = MediaType.TEXT_HTML_VALUE)
+ @ResponseBody
+ public String handleAuthCallback() {
+ String targetUrl = devMode ? frontendDevUrl : "";
+ return """
+
+
+
+ OAuth Callback
+
+
+
+
+
Completing authentication...
+
If you are not redirected automatically, click here.
+
+
+
+ """
+ .formatted(targetUrl, targetUrl.isEmpty() ? "/" : targetUrl);
+ }
+}
diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java
index 8dcd4a270..010c15e29 100644
--- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java
+++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java
@@ -160,7 +160,13 @@ public class SecurityConfiguration {
// Set exposed headers (headers that the browser can access)
cfg.setExposedHeaders(
- List.of("WWW-Authenticate", "X-Total-Count", "X-Page-Number", "X-Page-Size"));
+ List.of(
+ "WWW-Authenticate",
+ "X-Total-Count",
+ "X-Page-Number",
+ "X-Page-Size",
+ "Content-Disposition",
+ "Content-Type"));
// Allow credentials (cookies, authorization headers)
cfg.setAllowCredentials(true);
@@ -181,7 +187,11 @@ public class SecurityConfiguration {
}
@Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ public SecurityFilterChain filterChain(
+ HttpSecurity http,
+ @Lazy IPRateLimitingFilter rateLimitingFilter,
+ @Lazy JwtAuthenticationFilter jwtAuthenticationFilter)
+ throws Exception {
// Enable CORS only if we have configured origins
CorsConfigurationSource corsSource = corsConfigurationSource();
if (corsSource != null) {
@@ -200,9 +210,8 @@ public class SecurityConfiguration {
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
- .addFilterBefore(
- rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class)
- .addFilterBefore(jwtAuthenticationFilter(), UserAuthenticationFilter.class);
+ .addFilterBefore(rateLimitingFilter, UsernamePasswordAuthenticationFilter.class)
+ .addFilterBefore(jwtAuthenticationFilter, UserAuthenticationFilter.class);
if (!securityProperties.getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
@@ -310,7 +319,8 @@ public class SecurityConfiguration {
// Check if it's a public auth endpoint or static
// resource
return RequestUriUtils.isStaticResource(
- contextPath, uri) || RequestUriUtils.isPublicAuthEndpoint(
+ contextPath, uri)
+ || RequestUriUtils.isPublicAuthEndpoint(
uri, contextPath);
})
.permitAll()
diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AuthController.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AuthController.java
index a22454534..de6428554 100644
--- a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AuthController.java
+++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AuthController.java
@@ -252,11 +252,4 @@ public class AuthController {
return userMap;
}
-
- // ===========================
- // Request/Response DTOs
- // ===========================
-
- /** Login request DTO */
- public record LoginRequest(String email, String password) {}
}
diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java
index b74295cf7..6265281d9 100644
--- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java
+++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java
@@ -250,7 +250,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
String contextPath = request.getContextPath();
String[] permitAllPatterns = {
contextPath + "/login",
- contextPath + "/signup",
contextPath + "/register",
contextPath + "/error",
contextPath + "/images/",
@@ -262,7 +261,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
contextPath + "/pdfjs-legacy/",
contextPath + "/api/v1/info/status",
contextPath + "/api/v1/auth/login",
- contextPath + "/api/v1/auth/register",
contextPath + "/api/v1/auth/refresh",
contextPath + "/api/v1/auth/me",
contextPath + "/site.webmanifest"