最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Cookies, csrf protection with spring boot and next js (Render.com, Vercel.com) - Stack Overflow

programmeradmin2浏览0评论

I have a backend in Spring boot 3.4.2 and a frontend Next JS 15.0.4. I have authentication on backend side with google oauth2 and it works. My cooki setup locally works with localhost:3000 and localhost:8080. I use csrf protection and cors config on backend side and do not want to turn off, so my question is, how to make it work on production.

My prod setup: I have a domain, lets call it: example. I created a subdomain like api.example for the backend with domain records and it works, so the namecheap doman setup in theory is good.

My Nextjs app is on vercel. My backend app is on render. The domain and subdomain have been configured on both platfroms.

THE PROBLEM:

when i click on frontend to the login button, that redirects to the backend. Backend manages to authenticate the user, and it's great. But after the successful auth, when the handler redirects to the next js app to a protected page, the request (next js server side request towards backend) fails for getting any protected data, so it redirects to login, and this whole flow leds to TOO_MANY_REDIRECTIONS

I am pretty sure, my cookie setup is messed somehow with the domain and subdomain on prod env.

    @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http


            .authorizeHttpRequests(authorize -> authorize
                    .anyRequest().authenticated()
            )
            .exceptionHandling(exceptionHandlingConfigurer -> {
                exceptionHandlingConfigurer.authenticationEntryPoint(
                        new Oauth2AuthenticationEntrypoint());
            })
            .oauth2Login(customizer -> {
                customizer
                        .successHandler(new Oauth2LoginSuccessHandler(userService, appConfig));
            })
            .logout(logout -> logout
                    .logoutUrl("/logout")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID", "XSRF-TOKEN")
                    .logoutSuccessHandler((request, response, authentication) -> {
                        response.setStatus(HttpServletResponse.SC_OK);
                    })
            );
    http.addFilterBefore(debuglogFilter, UsernamePasswordAuthenticationFilter.class);

    http.cors().configurationSource(corsConfigurationSource());
    http.csrf(csrf -> csrf
            .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/api/v1/books/process-images"))
            .disable());
    return http.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(appConfig.getAllowedOrigins()); // Your frontend URL
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // Allow all HTTP methods, including OPTIONS
    configuration.setAllowedHeaders(Arrays.asList("X-XSRF-TOKEN", "Content-Type", "Authorization", "Cookie"));
    configuration.setAllowCredentials(true); // Allow credentials (cookies)


    // Create and return the CorsConfigurationSource
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration); // Apply to all endpoints
    return source;
}

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins(appConfig.getAllowedOrigins().get(0)) // Allow your frontend URL
            .allowedMethods("GET", "POST", "PUT", "DELETE") // Allowed HTTP methods
            .allowedHeaders("X-XSRF-TOKEN", "Content-Type", "Authorization", "Cookie")
            .allowCredentials(true); // Allow credentials (cookies, etc.)
}

@Slf4j
@RequiredArgsConstructor
public class Oauth2LoginSuccessHandler implements AuthenticationSuccessHandler {

    private final CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository(); // Use session-based CSRF storage
    private final UserService userService;
    private final AppConfig appConfig;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        log.debug("Authentication: {}", authentication);

        // Convert OAuth2 user to your app user
        DefaultOAuth2User oidcUser = (DefaultOAuth2User) authentication.getPrincipal();
        AppUser appUser = AppUser.fromGoogleUser(oidcUser);

        userService.findOrCreateUser(appUser);

        AppAuthenticationToken token = new AppAuthenticationToken(appUser);
        SecurityContextHolder.getContext().setAuthentication(token);

        CsrfToken csrfToken = csrfTokenRepository.generateToken(request);
        csrfTokenRepository.saveToken(csrfToken, request, response);

//        Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken());
//        csrfCookie.setPath("/");
//        csrfCookie.setSecure(false); // Set to true in production with HTTPS
//        csrfCookie.setHttpOnly(false); // Allow JS to read it
        Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken());
        csrfCookie.setPath("/");
        csrfCookie.setSecure(true); // MUST be true in production with HTTPS
        csrfCookie.setHttpOnly(false); // JavaScript should access it
//        csrfCookie.setDomain("example"); // Ensure it's accessible from frontend
        csrfCookie.setAttribute("SameSite", "None"); // Enable cross-site requests

//        response.addCookie(csrfCookie);
        response.addCookie(csrfCookie);

        log.debug("Set CSRF token: {}", csrfToken.getToken());

        // 

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论