Fixing 401s with CORS Preflights and Spring Security

1. Overview

In this short tutorial, we’re going to learn how to solve the error “Response for preflight has invalid HTTP status code 401”, which can occur in applications that support cross-origin communication and use Spring Security.

First, we’ll see what cross-origin requests are and then we’ll fix a problematic example.

2. Cross-Origin Requests

Cross-origin requests, in short, are HTTP requests where the origin and the target of the request are different. This is the case, for instance, when a web application is served from one domain and the browser sends an AJAX request to a server in another domain.

To manage cross-origin requests, the server needs to enable a particular mechanism known as CORS, or Cross-Origin Resource Sharing.

The first step in CORS is an OPTIONS request to determine whether the target of the request supports it. This is called a pre-flight request.

The server can then respond to the pre-flight request with a collection of headers:

  • Access-Control-Allow-Origin: Defines which origins may have access to the resource. A ‘*’ represents any origin
  • Access-Control-Allow-Methods: Indicates the allowed HTTP methods for cross-origin requests
  • Access-Control-Allow-Headers: Indicates the allowed request headers for cross-origin requests
  • Access-Control-Max-Age: Defines the expiration time of the result of the cached preflight request

So, if the pre-flight request doesn’t meet the conditions determined from these response headers, the actual follow-up request will throw errors related to the cross-origin request.

It’s easy to add CORS support to our Spring-powered service, but if configured incorrectly, this pre-flight request will always fail with a 401.

3. Creating a CORS-enabled REST API

To simulate the problem, let’s first create a simple REST API that supports cross-origin requests:

@RestController
@CrossOrigin("http://localhost:4200")
public class ResourceController {

    @GetMapping("/user")
    public String user(Principal principal) {
        return principal.getName();
    }
}

The @CrossOrigin annotation makes sure that our APIs are accessible only from the origin mentioned in its argument.

4. Securing Our REST API

Let’s now secure our REST API with Spring Security:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .httpBasic();
    }
}

In this configuration class, we’ve enforced authorization to all incoming requests. As a result, it will reject all requests without a valid authorization token.

5. Making a Pre-flight Request

Now that we’ve created our REST API, let’s try a pre-flight request using curl:

curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200" 
  -X OPTIONS http://localhost:8080/user
...
< HTTP/1.1 401
...
< WWW-Authenticate: Basic realm="Realm"
...
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Credentials: true
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
...

From the output of this command, we can see that the request was denied with a 401.

Since this is a curl command, we won’t see the error “Response for preflight has invalid HTTP status code 401” in the output.

But we can reproduce this exact error by creating a front end application that consumes our REST API from a different domain and running it in a browser.

6. The Solution

We haven’t explicitly excluded the preflight requests from authorization in our Spring Security configuration. Remember that Spring Security secures all endpoints by default.

As a result, our API expects an authorization token in the OPTIONS request as well.

Spring provides an out of the box solution to exclude OPTIONS requests from authorization checks:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ...
        http.cors();
    }
}

The cors() method will add the Spring-provided CorsFilter to the application context which in turn bypasses the authorization checks for OPTIONS requests.

Now we can test our application again and see that it’s working.

7. Conclusion

In this short article, we’ve learned how to fix the error “Response for preflight has invalid HTTP status code 401” which is linked with Spring Security and cross-origin requests.

Note that, with the example, the client and the API should run on different domains or ports to recreate the problem. For instance, we can map the default hostname to the client and the machine IP address to our REST API when running on a local machine.

As always, the example shown in this tutorial can be found over on Github.

Related posts:

A Guide to Spring Cloud Netflix – Hystrix
Ép kiểu trong Java (Type casting)
Java Program to Test Using DFS Whether a Directed Graph is Weakly Connected or Not
Java Program to Check if a Directed Graph is a Tree or Not Using DFS
Consumer trong Java 8
Working With Maps Using Streams
Spring REST API + OAuth2 + Angular (using the Spring Security OAuth legacy stack)
Java Program to Implement Stein GCD Algorithm
Java Program to Implement Levenshtein Distance Computing Algorithm
Java Program to Implement Gauss Seidel Method
Spring Security Basic Authentication
Java Program to Compute Discrete Fourier Transform Using Naive Approach
Java Program to Perform LU Decomposition of any Matrix
Java Program to Find the Peak Element of an Array O(n) time (Naive Method)
Bootstrapping Hibernate 5 with Spring
So sánh HashMap và Hashtable trong Java
Java Program to Implement a Binary Search Algorithm for a Specific Search Sequence
Hướng dẫn Java Design Pattern – Decorator
JUnit5 Programmatic Extension Registration with @RegisterExtension
Java Program to Implement LinkedHashSet API
Java Program to find the maximum subarray sum O(n^2) time(naive method)
Practical Java Examples of the Big O Notation
Java Program to implement Sparse Vector
REST Web service: Tạo ứng dụng Java RESTful Client với Jersey Client 2.x
Overflow and Underflow in Java
Array to String Conversions
Java Program to Implement Flood Fill Algorithm
Spring WebClient Filters
Java Program to Implement Shoelace Algorithm
Java Program to Implement Sorted Singly Linked List
Quản lý bộ nhớ trong Java với Heap Space vs Stack
Debug a HttpURLConnection problem