DEV Community

Cover image for CORS in Spring Boot with Kotlin
Brandon Wie
Brandon Wie

Posted on

CORS in Spring Boot with Kotlin

Cross-Origin Resource Sharing (CORS) is a crucial aspect of web security that allows or restricts resources on a web server depending on where the request is coming from. We’ll explore how CORS works, its implications, and how to configure it in Spring Boot with Kotlin.


Understanding Headers Related to CORS

CORS relies on specific HTTP headers to control resource sharing between different origins. Here are the most important ones:

  1. Request Headers:

    • Origin: Indicates the origin (protocol + domain + port) from where the request originates. Example:
     Origin: http://localhost:3000
    
  2. Response Headers:

    • Access-Control-Allow-Origin: Specifies the allowed origin(s) for the requested resource. If set to *, it allows all origins.
     Access-Control-Allow-Origin: http://localhost:3000
    
  • Access-Control-Allow-Methods:
    Lists the allowed HTTP methods for cross-origin requests (e.g., GET, POST, PUT).

     Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    
  • Access-Control-Allow-Headers:
    Specifies the allowed headers for requests, such as Authorization or Content-Type.

     Access-Control-Allow-Headers: Authorization, Content-Type
    
  • Access-Control-Allow-Credentials:
    Allows cookies and other credentials to be included in cross-origin requests. Must be set to true explicitly.

     Access-Control-Allow-Credentials: true
    
  1. Preflight Requests:
    • Browsers send an additional OPTIONS request to verify whether the actual request is permitted. The server responds with the necessary CORS headers.

Why Does CORS Affect Browsers but Not Mobile Apps?

CORS is a browser-enforced security mechanism. Here’s why:

  1. Web Security Model:

    • Browsers implement the same-origin policy, which restricts scripts on one origin from accessing resources on another origin.
    • CORS headers allow servers to relax these restrictions for specific origins.
  2. Mobile and Other Clients:

    • Non-browser clients like mobile apps, Postman, or cURL do not enforce the same-origin policy. They directly send HTTP requests without validating CORS headers.
    • This is why CORS is primarily a browser-related concern.
  3. Implications:

    • While CORS protects users from malicious cross-origin scripts, backend APIs still need proper authentication and authorization mechanisms to secure data.

How to Set Up CORS in Spring Boot with Kotlin

In Spring Boot, you can configure CORS globally or at the controller level. Here’s an example of setting it up in Kotlin:

Global Configuration with @Bean

The following Kotlin code configures CORS for all endpoints:

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import org.springframework.web.filter.CorsFilter

@Configuration
class CorsConfig {

    @Bean
    fun corsConfigurationSource(): UrlBasedCorsConfigurationSource {
        val configuration = CorsConfiguration()

        // Allow requests from specific origins
        configuration.allowedOrigins = listOf("http://localhost:3000") // Replace with specific origin for production

        // Allow credentials (cookies, authorization headers, etc.)
        configuration.allowCredentials = true

        // Allow specific HTTP methods
        configuration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")

        // Allow specific headers
        configuration.allowedHeaders = listOf("Authorization", "Content-Type")

        // Apply CORS settings to all paths
        val source = UrlBasedCorsConfigurationSource()
        source.registerCorsConfiguration("/**", configuration)

        return source
    }
}
Enter fullscreen mode Exit fullscreen mode

CORS with Controller-Level Annotations

You can also configure CORS for specific controllers or endpoints:

import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class ExampleController {

    @CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")
    @GetMapping("/example")
    fun exampleEndpoint(): String {
        return "CORS Configured!"
    }
}
Enter fullscreen mode Exit fullscreen mode

What Is Affected by allowCredentials?

Impacts of allowCredentials

The allowCredentials setting controls whether browser-managed credentials, such as cookies, are included in cross-origin requests:

  1. Cookies:

    • When allowCredentials = true, cookies (e.g., session tokens) are sent automatically with the request if the client sets withCredentials: true.
    • If allowCredentials = false, cookies are not included in the request, even if the client specifies withCredentials: true.
  2. TLS Client Certificates:

    • allowCredentials enables the inclusion of TLS client certificates in cross-origin requests.
  3. Basic Authentication Headers:

    • Automatically included in requests when allowCredentials = true and the request requires Basic Authentication.

What Is NOT Affected by allowCredentials?

The allowCredentials setting does not affect manually added headers like the Authorization header used for Bearer tokens:

  1. Authorization Header:

    • Bearer tokens included in the Authorization header are manually set by the client and are unaffected by allowCredentials.
    • Example:
     axios.get("https://api.example.com/data", {
         headers: {
             Authorization: `Bearer ${token}`
         }
     });
    
  2. Custom Headers:

    • Any headers explicitly added to the request (e.g., X-Custom-Header) are unaffected by allowCredentials.

Why Authorization Is Unaffected

The browser’s role in enforcing CORS focuses on automatically managing credentials, such as cookies and certificates. Headers explicitly added by the client are outside the scope of browser-enforced CORS rules, so they are unaffected by allowCredentials.


Best Practices for CORS Configuration

  1. Specific Origins:

    • Avoid using * for allowedOrigins if allowCredentials = true. The CORS specification disallows this combination.
    • Example:
     configuration.allowedOrigins = listOf("http://localhost:3000")
     configuration.allowCredentials = true
    
  2. Credentials:

    • If your application relies on cookies or other credentials, ensure allowCredentials = true and configure Access-Control-Allow-Credentials on the server.
    • Cookies will only work if the client sets withCredentials: true (e.g., in Axios).
  3. Preflight Requests:

    • Handle OPTIONS requests by including appropriate headers in your CORS configuration.
  4. Avoid Wildcards in Production:

    • Using * for allowedMethods and allowedHeaders is acceptable for development but should be replaced with specific values in production.
  5. Security First:

    • CORS headers are not a substitute for authentication and authorization. Ensure proper security mechanisms are in place on the backend.

Takeaway

  • CORS is a browser-enforced security feature that allows or restricts cross-origin resource sharing.
  • CORS does not affect mobile apps and non-browser clients, but backend APIs should still use authentication.
  • In Spring Boot with Kotlin, you can configure CORS globally or at the controller level.
  • Follow best practices, like setting specific origins and ensuring compatibility between allowCredentials and allowedOrigins, to avoid security issues.

By understanding and properly configuring CORS, you can build secure and flexible web applications that seamlessly handle cross-origin requests.


References

  1. MDN Web Docs: CORS
  2. Spring Framework Documentation: CORS Support
  3. Fetch API - Request.credentials
  4. What is CORS?

Top comments (0)