I hosted my frontend (react) and backend (springboot) subfolders separately on Railway. I am getting CORS error after updating the origin where the frontend is deployed as follows:
This is my configuration in spring:
@Configuration
public class DataRestConfig implements RepositoryRestConfigurer {
private String theAllowedOrigins = "https://finance-app-frontend-production-7ab9.up.railway.app";
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
HttpMethod[] theUnsupportedActions = {HttpMethod.PATCH};
config.exposeIdsFor(Expense.class);
config.exposeIdsFor(Goal.class);
// disable HTTP methods
disableHttpMethods(Expense.class, config, theUnsupportedActions);
disableHttpMethods(Goal.class, config, theUnsupportedActions);
// Configure CORS Mapping
cors.addMapping(config.getBasePath() + "/**")
.allowedOrigins(theAllowedOrigins).allowedMethods("GET", "POST", "DELETE", "PUT");
}
private void disableHttpMethods(Class theClass, RepositoryRestConfiguration config, HttpMethod[] theUnsupportedActions) {
config.getExposureConfiguration()
.forDomainType(theClass)
.withItemExposure((metadata, httpMethods) -> httpMethods.disable(theUnsupportedActions))
.withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(theUnsupportedActions));
}
}
and
package com.example.demo.config;
import com.okta.spring.boot.oauth.Okta;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// Disable cross site request forgery
http.csrf(csrf -> csrf.disable())
.cors(cors -> {
cors.configurationSource(corsConfigurationSource());
}
)
// Protect endpoints at /api/<type>/secure
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/expenses/**").authenticated()
.requestMatchers("/api/goals/**").authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
)
// Add content negotiation strategy
.setSharedObject(ContentNegotiationStrategy.class,
new HeaderContentNegotiationStrategy());
Okta.configureResourceServer401ResponseBody(http);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("https://finance-app-frontend-production-7ab9.up.railway.app");
// configuration.addAllowedMethod("*");
configuration.setAllowedMethods(Arrays.asList("GET","POST", "PUT", "DELETE", "PATCH"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
When I tested the APIs in postman, they worked for all APIs except for the APIs with /api/goals/** (Not sure why I get a 403 error) Below are examples of the controllers
@CrossOrigin("https://finance-app-frontend-production-7ab9.up.railway.app")
@RestController
@RequestMapping("/api/expenses")
public class ExpenseController {
private ExpenseService expenseService;
@Autowired
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
@PostMapping("/add")
public void addExpense(@RequestHeader(value="Authorization") String token,
@RequestBody AddExpenseRequest addExpenseRequest) throws Exception {
String userEmail = ExtractJWT.payloadJWTExtract(token, "\"sub\"");
if (userEmail==null) {
throw new Exception("User email is missing");
}
expenseService.addExpense(userEmail, addExpenseRequest);
}
}
and
@CrossOrigin("https://finance-app-frontend-production-7ab9.up.railway.app")
@RestController
@RequestMapping("/api/goals")
public class GoalController {
private GoalService goalService;
@Autowired
public GoalController(GoalService goalService) {
this.goalService = goalService;
}
@PostMapping("/add")
public void addGoal(@RequestHeader(value = "Authorization") String token,
@RequestBody AddGoalRequest addGoalRequest) throws Exception {
String userEmail = ExtractJWT.payloadJWTExtract(token, "\"sub\"");
if (userEmail == null) {
throw new Exception("User email is missing");
}
goalService.addGoal(userEmail,addGoalRequest);
}
}
Frontend:
REACT_APP_API=https://finance-app-production-8d91.up.railway.app/api (I set this variable in variables in Railway)
Appreciate my help or ideas, thanks!
Authorization
andContent-Type
in your CORS configuration for that client request to be accepted by the server.