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"