CORS in Spring Boot with Kotlin

Thomas Basche
3 min readAug 11, 2021

--

Having recently starting work on a Spring Boot/Kotlin application I struggled for far too long to find some conclusive and production-worthy examples for configuring CORS properly. My exact problem:

1. I have a backend service running Spring/Kotlin that has some REST API’s that are to be called from a frontend application (e.g. React, Vue etc.) with authentication.

2. I want to configure my cross-origin resource sharing (CORS) in such a way as to be secure and production-ready. I should be able to configure the ‘origin’ in ‘cross-origin’ depending on the environment I deploy the service to.

I love the internet. I love StackOverflow especially. But some of the guidance on CORS there is incredibly misguided for production-ready applications. Namely around telling you to just disable it. Or allow any origin through to your application. While this is great for toy apps, or for learning web development it’s certainly suboptimal when millions of people depend on it being secure and robust! So you definitely should have CORS set-up and not disabled. Install a nice big front door, and only let your friends in. With that out of the way, here’s all you need.

This assumes you have an entry in your application.yml properties file like:

cors:
originPatterns: ${CORS_ORIGINS}

This way we can have different origins for different environments. So the environment variable might look like:

CORS_ORIGINS=https://dev.frontend.com,http://localhost:8000

I opted for comma-separation as it was just simpler to handle in the code than going for a YAML list. You will rarely change this configuration once it’s set anyway, so I just opted for the simplest solution within the application itself.

The WebServerConfiguration then loads the config at runtime and adds these as allowed origins to your generic web server configuration.

  • addMapping specifies which URI’s qualify for CORS. We want all paths to be included.
  • The allowedOriginPatterns method takes care of wildcard domains as well (e.g. https://*.myfrontend.com). This method is the one that sends back the Access-Control-Allow-Origin header. The wildcard origin is useful for something like AWS Amplify PR Previews, where your in-development frontend might have a dynamic subdomain (the exact URL isn’t always known!)
  • allowedMethods is self-explanatory — allow all HTTP verbs. I’ve seen a tendency to directly specify which ones are implemented, but this means it never has to be touched again. We should also keep in mind this is a pretty generic configuration; it shouldn’t care about the contents of the rest of the application.
  • Finally, allowCredentials is there to send the Access-Control-Allow-Credentials header in the response which informs the browser that it’s ok to expose the response to frontend code if credentials are included.

If you have Spring Security (or some other security configuration) set up, there is a final piece to this puzzle. Namely around allowing the preflight requests (i.e. the OPTIONS requests) to be ignored from your authentication. Why should it be ignored? Browsers don’t tend to send auth information alongside preflight requests and the CORS specification does call out that OPTIONS is a safe action that shouldn’t be able to change any resources.

OPTIONS is an HTTP/1.1 method that is used to determine further information from servers, and is a safe method, meaning that it can’t be used to change the resource.

Think of the preflight OPTIONS request like a knock on our big front door. We see who it is through the peephole, and then let in the GET request with proper authentication and authorisation. We want to subdue the attack dog and let our friendly neighbourhood request knock on the door.

As you can see, we allow anything that looks like a PreFlightRequest through. If you’re not using Spring Security, this may be an issue in other auth frameworks.

I hope this has helped. You should have no excuse for disabling CORS now! I’m certainly no expert in Spring (or Kotlin for that matter) but it’s been an experience to get something reasonable up and running and I wanted to share that with the rest of the internet 😄

--

--