[SOLVED] Springbot 2.3.0.0 Cors Issue with API

Hi, Codebots teams,

I got some issue on CORS, if we call the API using POSTMAN with origin enabled it’s getting Invalid cors request, but when not using origin options it works. I try to call the API for reporting and generate a pdf.
it doesn’t work because of the CORS issue.

our production server using HTTPS, and the development server using non-https.
it was working on the localhost and dev server.

I still can’t figure out what caused that issue.
any help would be appreciated.

Thanks

Hi @Virgananta_Nugraha,

Thanks for getting in touch. I believe we can help you.

CORS stands for cross-origin resource sharing and is a mechanism that allow servers to indicate what servers they allow requests from.

For SpringBot applications, CORS protection in the development and test profiles (see SpringBot application profiles and configuration) has been expanded to allow requests from http://localhost:8000 and http://localhost:4200 to allow for the client-side to be development independently of the server (i.e port 4200).

This configuration can been in the snippet below from configs/security/SecurityConfig.java.

@Bean  
@Profile("dev")  
public CorsFilter corsFilter() {  
   // % protected region % \[Customise your development COR's filter here\] off begin  
 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
   CorsConfiguration config = new CorsConfiguration();  
   config.setAllowCredentials(true);  
   config.addAllowedOrigin("http://localhost:4200");  
   config.addAllowedOrigin("http://localhost:8000");  
   config.addAllowedHeader("\*");  
   config.addAllowedMethod("OPTIONS");  
   config.addAllowedMethod("GET");  
   config.addAllowedMethod("POST");  
   config.addAllowedMethod("PUT");  
   config.addAllowedMethod("DELETE");  
   source.registerCorsConfiguration("/\*\*", config);  
  
   // % protected region % \[Customise your development COR's filter here\] end  
 return new CorsFilter(source);  
}

You noted that you can get access if you remove the Origin header from your POSTMAN request. This is because tools like POSTMAN cheat by not adding all the normal headers that a browser would and thus not allowing the server to identify the origin of the request.

As CORS is a security measure, my recommendation to resolve your issue is to update your CORS filter to allow the servers that you require.

You can see an example of this in practice in this video.

PLEASE NOTE: Do not disable CORS as it can be used as an attack vector by a malicious third party as it allows them to make requests against your server.

Please see here for more details on CORS in Spring Security.

While there are many ways of configuring CORS, hopefully the above will point you in the right direction. Please let me know if you have any other questions.![KMS-help-2021-05-18_17.39.17|video]

@kellie Hi Kellie, thanks for helping,
I saw that was using (‘dev’)
how about if I use another profile? please correct me if I miss understanding that.

we deploy the project to AWS
we used another profile because we create another profile called Staging.

Hi @Virgananta_Nugraha.

That is correct. Looking at your build it appears that you don’t have CORS enabled for your staging profile.

In your SecurityConfig.java file you should see something like the following:

http
	...
	.cors()

This line enables CORS for the given environment. At this stage it is hard to see what may be causing the issue you are running into.

To help you troubleshoot we are going to need some more details about your production server.

Is it possible for you to send us some information about your production server via email at support@codebots.com?

As part of this email can you please include details about your production server as well as the version/branch of your application that you have running in it.

Thank you.

Hi @Virgananta_Nugraha.

Updating this with the solution.

CORS can be considered protection against the polite hackers as it only prevents requests made within the browser context.

The browser adds the Origin header and outside a browser context this can be spoofed easily or omitted entirely preventing the server from being able to correctly identify if the request is actually cross-origin in nature. CORS protection by itself is not enough to secure your application from malicious actors but is an important part of the security tool kit.

CORS needs to be correctly configured to correctly allow access, this can be done in a couple of different ways.

Global configuration

Global configuration is the method to choose if you need the same CORS rules to apply to all your API endpoints.

All changes for this method are added to your SecurityConfig class found at serverside/src/main/java/[ProjectName]/configs/security/SecurityConfig.java.

Enabling CORS

To configure how CORS operates, it has to be enabled by adding the following into your configure method.

http
	.cors()

For example, it is enabled for development in a default SpringBot application as follows.

@Override
protected void configure(HttpSecurity http) throws Exception {
	// % protected region % [Add any additional security configuration before the main body here] off begin
	// % protected region % [Add any additional security configuration before the main body here] end

	if (isDevEnvironment || isTestEnvironment) {
		// % protected region % [Add any additional security configuration for dev and test environments before the main body here] off begin
		// % protected region % [Add any additional security configuration for dev and test environments before the main body here] end

		http
				.cors()

Now, by default this will enable CORS protection using the default Spring Security configuration which only allows requests from the same host that the server is running on.

To open this up further to remote access we need to add some configuration.

There are many ways of doing this, the three primary methods as follows.

  1. CORS Filter
  2. Configuration source

CORS filter

There is an example of the CORS filter in use within a stock SpringBot application. It is used to configure CORS for the development profile.

@Bean  
@Profile("dev")  
public CorsFilter corsFilter() {  
	// % protected region % \[Customise your development COR's filter here\] off begin  
	UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
	CorsConfiguration config = new CorsConfiguration();  
	config.setAllowCredentials(true);  
	config.addAllowedOrigin("http://localhost:4200");  
	config.addAllowedOrigin("http://localhost:8000");  
	config.addAllowedHeader("\*");  
	config.addAllowedMethod("OPTIONS");  
	config.addAllowedMethod("GET");  
	config.addAllowedMethod("POST");  
	config.addAllowedMethod("PUT");  
	config.addAllowedMethod("DELETE");  
	source.registerCorsConfiguration("/\*\*", config);  

	// % protected region % \[Customise your development COR's filter here\] end  
	return new CorsFilter(source);  
}

This filter allows access from origins https://localhost:4200 and http://localhost:8000. If you have selected to use the CORS filter approach, you can create your own filter similar to the one above.

An alternative example of this can be found in the CORS Support in Spring Framework blog post.

Configuration Source

CORS can be configured by adding a CORS configuration source.

A specific CORS rule set can be defined in the manner as shown in the linked documentation or a more generic approach can be achieved with some built-in defaults.

One of the most useful defaults is applyPermitDefaultValues which enables CORS with some open defaults.

From the documentation:

The following defaults are applied if not already set:
- Allow all origins, i.e. "*".
- Allow “simple” methods GET, HEAD and POST.
- Allow all headers.
- Allow credentials.
- Set max age to 1800 seconds (30 minutes).

To apply the configuration source, it can be defined as its own bean or explicitly referenced inline. If the configuration is simple, inline referencing has the benefit of being more readable.

For example:

http  
        .cors()  
        .configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())

Controller configuration

CORS can also be configured on a per controller level.

This is achieved with the CrossOrigin class and method annotations.

@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

This allows for specific origins to be allowed to access the given controllers and can be used in conjunction with the global configuration to only allow CORS for specific controllers.

This annotation can also be added to the controller methods to allow for more specific CORS rules.