48

I have the following get request defined in my service:

createAuthorizationHeader(user, pass) {
  let headers: HttpHeaders = new HttpHeaders;
  headers = headers.append('Accept', 'application/json, text/plain, */*');
  headers = headers.append('Authorization', 'Basic ' + btoa(user + ':' + pass));
  headers = headers.append('Content-Type', 'application/json; charset=utf-8');
    console.log(headers);
    return this.http.get(this._loginUrl, {
      headers: headers
    });
}

The result is:

OPTIONS https://localhost:8443/delivery/all 401 ()

Failed to load https://localhost:8443/delivery/all: Response for preflight does not have HTTP ok status.

HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false, …}

I also have made the same post request with Postman, but everything works, so the local server works.

I can't figure out what am I doing wrong with my request.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
KalinJa
  • 483
  • 1
  • 4
  • 8

6 Answers6

85

The CORS contract requires that authentication not be required on the pre-flight OPTIONS request. It looks like your back-end is requiring authentication on the OPTIONS request and the GET.

When you access your back-end with Postman, you are only sending a GET. The browser will send an OPTIONS request first (without your authentication header), and look for the response to have an "Access-Control-Allow-Origin" header that matches the origin of the page making the request.

If the response to the OPTIONS request is not a 2xx, or the header is not present, or the header value does not match the requesting page's origin, you will get the error that you are experiencing, and the GET request will not be made.

TLDR; change your back-end to not require authentication for the OPTIONS method when handling the login url.

GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
  • 7
    Do you have any additional information for how to update IIS to not require authentication for the OPTIONS method? – Daryl Oct 12 '18 at 15:38
  • @Daryl we are doing that on the application level (rather on IIS). let me know if you need further details on how that could be done – itavq Oct 19 '18 at 08:15
  • 4
    So the solution is to change the back-end to not require authentication for OPTIONS, but there are no details on how one does that? Please expand, if possible. – PoloHoleSet Jun 13 '20 at 00:47
  • That's completely dependent on the server / development environment on the server side. If you have a question about how to do it in a particular server-side environment, please ask another question here on SO – GreyBeardedGeek Jun 13 '20 at 19:17
11

If .Net Web API project, edit web.config and remove the tag below.

<handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
Diogo Rodrigues
  • 1,312
  • 15
  • 15
8

By default, browsers won't allow users to perform Cross-origin request.

From Angular to avoid this error, we can use the proxy configuration.

Suppose if angular ui is working on localhost:4200 and it wants to call the rest end point url, e.g: https://localhost:8443/delivery/all

The below steps will address this issue.

  1. Create a proxy.config.json file in your angular application root folder. (Where package.json file exist) Proxy does is to simply take the browser request at the same domain+port where you frontend application runs and then forwards that request to your backend API server. CORS is a browser security issue and does not apply when doing “backend to backend” communication as is the case with a proxy in between.

    The content should look as follows.

    Sample:

    {
        "/delivery/all/*": {
        "target": "https://localhost:8443",
        "secure": false,
        "logLevel": "debug",
        "changeOrigin": true
        }
    }
    

    All requests made to /delivery/all/... from within our application will be forwarded to https://localhost:8443/delivery/all/

    Note: the changeOrigin property. You will definitely have to set this to true when you’re using some virtual proxies (such as configured with Apache2) on your backend

  2. Add proxy configuration while running application. Angular supports "--proxy-config" where you can supply the proxy configuration file. If you are using the "npm start" option for testing, you have to add "--proxy-config" option as given below. It has to be added in package.json file.

     "scripts": {
    
    "ng": "ng",
    "start": "ng serve --proxy-config proxy.config.json",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
    }
    

    If you are running directly using "ng serve" then it has be modified as given below. "ng serve --proxy-config proxy.config.json"

  3. The HTTP call inside our Angular application can be changed to this. Server will automatically forward the request to "https://localhost:8443/delivery/all".

    this.http.get('https://localhost:8443/delivery/all') .map(res => res.json());

    to

    this.http.get('/delivery/all') .map(res => res.json());

here is link for more information: https://sudhasoftjava.wordpress.com/2018/10/05/proxy-configuration-in-angular/

Sudhakar
  • 3,104
  • 2
  • 27
  • 36
5

What we've done is add a custom configuration for Cors. And then enable it through the security configuration in the java server. This Server was only enabled for direct REST calls as explained in the answer.

@Configuration
public class CorsConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                        .allowedHeaders("*");
            }
        };
    }
}

And this goes into the web security configuration:

@Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.csrf().disable()
                .antMatcher("/**")
                .authorizeRequests().and()
                .httpBasic()
                .and()
                .authorizeRequests().anyRequest().authenticated().and().cors();
    }
isherwood
  • 58,414
  • 16
  • 114
  • 157
thunder88
  • 51
  • 1
1

React app before POST request send OPTIONS request for check your API.
You need add in nginx.conf this block.

location /api/ {
    if ($request_method = 'OPTIONS') {
        return 204;
    }
    proxy_pass http://localhost:8080;
}
T30
  • 11,422
  • 7
  • 53
  • 57
e60wep
  • 11
  • 1
0

add this line to the end of your server header configuration

status_header(200);

should fix the issue

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17