CORS issue with Spring Boot 2023 And Custom Header from React App

163 views Asked by At

This is my actual @Configuration class:

package com.example.churchbillboard2.configs;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**")
            .allowedOrigins("*")
            .allowedMethods("PUT", "DELETE", "POST", "GET")
            .allowedHeaders("CustomAuth", "Authorization", "header3", "Origin", "Access-Control-Allow-Origin", "Content-Type",
            "Accept", "Origin, Accept", "X-Requested-With",
            "Access-Control-Request-Method", "Access-Control-Request-Headers")
            .exposedHeaders("CustomAuth", "Origin", "Content-Type", "Accept", "Authorization",
            "Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
            .allowCredentials(true).maxAge(3600);
    }
}

And this my Spring Boot target controller:

package com.example.churchbillboard2.controllers;

import org.springframework.web.bind.annotation.RestController;
import com.example.churchbillboard2.security.SessionToken;
import com.example.churchbillboard2.services.TimeManager;
import com.example.churchbillboard2.services.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
@RequestMapping("/")
public class Login {
    private UserService userService;
    private TimeManager timeManager;
    private SessionTokenWrapper sessionTokenWrapper;

    public Login(UserService userService, TimeManager timeManager, SessionTokenWrapper sessionTokenWrapper) {
        this.userService = userService;
        this.timeManager = timeManager;
        this.sessionTokenWrapper = sessionTokenWrapper;
    }

    @PostMapping(value = "/login")
    public SessionToken getMethodName(@RequestBody LoginDTO user, HttpSession session) {
        SessionToken sessionToken = (userService.getUserByUserName(user) == null) ? new SessionToken("Invalid User")
                : new SessionToken(null);
        sessionTokenWrapper.setSessionToken(sessionToken.getSessionToken());
        System.out.println("sessionTokenWrapper.getSessionToken()");
        System.out.println(sessionTokenWrapper.getSessionToken());
        return sessionToken;
    }

    @PostMapping("/months")
    public AvailableMonthsWrapper getMethodName(@RequestHeader("CustomAuth") String headerValue,
            HttpSession session) {
                System.out.println("headerValue");
                System.out.println(headerValue);
        return (sessionTokenWrapper.validateToken(headerValue))
                ? new AvailableMonthsWrapper(timeManager.availableMonths())
                : new AvailableMonthsWrapper("Not Valid Session");
    }

    @GetMapping(value = "/")
    public String getHome() {
        return "Hi From Home";
    }
}

Notice There is a custom header for PostMapping "/months".

Now I'm trying to use fetch from two custom hooks on react, in order to preserve the SessionScope, of the sessionTokenWrapper bean.

My react client is running on 3000 port localhost. But I need my SpringBoot server to be able to handle this requests from anywhere, no matter the source ip, no matter the source port.

My first Hook goes like this:

export const validateLogin = async (userName, password) => {
  const url = 'http://localhost:5000/login';
  const loginData = {
    username: userName,
    password: password,
  };

  const response = await fetch(url, {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(loginData),
  });

  if (response.ok) {
    return await response.text();
  } else {
    throw new Error(`Request failed with status ${response.status}`);
  }
}

And my second hook is (The one that sends the custom header):

export const fetchMonths = async (sessionToken) => {
    console.log('token: ', sessionToken);
    const url = 'http://localhost:5000/months';

    const response = await fetch(url, {
        method: 'POST',
        credentials: 'include', 
        headers: {
            'Content-Type': 'application/json',
            'CustomAuth': sessionToken, 
        },
    });

    if (response.ok) {
        return await response.text();
    } else {
        throw new Error(`Request failed with status ${response.status}`);
    }

};

I keep getting every kind of errors, starting by

"allowed origins can not be setup to "" when include credentials...*"

to the current one and the one I'm suffering the most:

"Access to fetch at 'http://localhost:5000/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

I'm pretty new at Spring Boot and this cors configuration is a mess. What am I doing wrong? and what do I need to allow my Spring Boot server to have a free communication with any client even if it sends a custom header. (This because I need to scale it later to an Android client too).

1

There are 1 answers

0
Clown On

For anyone having trouble witht the same. I just changed .allowedOrigins to .allowedOriginPatterns

registry.addMapping("/**")
            .allowedOriginPatterns("*") //here
            .allowedMethods("PUT", "DELETE", "POST", "GET") 
            .allowedHeaders("CustomAuth", "Authorization", "header3", "Origin", "Access-Control-Allow-Origin", "Content-Type",
            "Accept", "Origin, Accept", "X-Requested-With",
            "Access-Control-Request-Method", "Access-Control-Request-Headers")
            .exposedHeaders("CustomAuth", "Origin", "Content-Type", "Accept", "Authorization",
            "Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
            .allowCredentials(true);