1

I am writing an Angular 4 application that makes an authenticated connection to a Tomcat 7 server. The webapp on this Tomcat server has a REST API. I have a local copy of the server on one of my machines, so I am using a request url like this:

http://xyz.local:8080/PoolParty/api/projects

I am using Safari for the request and if I check the Disable Cross-Origin Restrictions menu item then I get a json package returned and I can display it in the browser. If I don't check that menu item then I get an error:

Failed to load resource: Preflight response is not successful

I have checked the CORS filter configuration in the server, and it looks like this:

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
      <param-name>cors.allowed.origins</param-name>
      <param-value>*</param-value>
  </init-param>
  <init-param>
      <param-name>cors.allowed.methods</param-name>
      <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Which I think is correct from the other cases I've seen here and on other sites.

My angular request looks like this:

   private sendRequest(verb: RequestMethod, url: string, body?: Product): Observable<Product> {
      let headers = new Headers();
      headers.set("withCredentials", "true");
      headers.set("Authorization", "Basic ***********");
      headers.set("Access-Control-Allow-Origin", "*");
      headers.set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      headers.set("Content-Type", "text/plain");

      return this.http.request(new Request({
        method: verb,
        url: url,
        body: body,
        headers: headers
      })).map(response => response.json());
    }

So I have a couple of questions:

  1. What else do I have to do in configuring my Tomcat server to send CORS headers?
  2. Am I doing the right things in the request that I'm sending?

Update on Thursday 24 August:

Here are the headers and responses in the Web Inspector Network window (Safari) when I select the row with the OPTIONS method:

Request & Response:
====================
Method:      OPTIONS
Cached:      No
Status:      ___
Code:        ___
Encoded:     ___
Decoded:     ___
Compressed:  No

Request Headers:
================
Name                            Value
User-Agent                      Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4
Cache-Control                   max-age=0
Referer                         http://localhost:4200/ 
Origin                          http://localhost:4200
Access-Control-Request-Method   GET
Access-Control-Request-Headers  authorization
Accept                          */*

Response Headers
    No Response Headers

I've attached a couple of screenshots. The first shows the request and response. The second shows the "Preflight" error messages from the console that I've mentioned above.

request and response details from browser devtools

Error messages logged for the preflight:

error messages for preflight

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Ian Piper
  • 45
  • 1
  • 8
  • You’re adding an `Authorization` request header, so you must also set `cors.allowed.headers`: ` cors.allowed.headers Authorization ` But you also must fix your frontend code: You don’t want to send `Access-Control-Allow-Origin` or `Access-Control-Allow-Headers` request headers (those are *response* headers for servers to send) and instead of a `withCredentials` header, you want to set the `withCredentials` param. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS – sideshowbarker Aug 23 '17 at 03:41
  • Thanks for those pointers. I went to the web.xml file for the Tomcat application and added the cors.allowed.headers "Authorization" parameter. I also removed all headers apart from headers.set("Authorization", "Basic **************"); from my Angular request, and added a parameter withCredentials: true to the request in place of a withCredentials header. Sadly it has made no difference. In Safari, if I enable the "Disable Cross-Origin Restrictions" menu item it works, and if I disable that menu item it fails: "Failed to load resource: Preflight response is not successful". Maddening! – Ian Piper Aug 24 '17 at 08:30
  • You probably want to go into your browser devtools and open the Network tab and reload and then check the details of the OPTIONS request the browser’s sending, and get the value of the Access-Control-Request-Headers and Access-Control-Request-Method the browser is sending, and then check the details of the server response to that OPTIONS, and get the value of the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers the server is returning, and then use https://stackoverflow.com/posts/45694196/edit to edit/update the question and paste all that info back into the question – sideshowbarker Aug 24 '17 at 08:51
  • It would be nice to be able to inspect that preflight stuff. Is there a way for me to know exactly what my Angular app is sending in the preflight request, and what the Tomcat server sends back? Sorry, just seen the response. I will try your suggestion; thanks very much for helping with this. – Ian Piper Aug 24 '17 at 08:52
  • Yeah there is a way: Go into the Network tab in your browser devtools as described above. All the info is right there – sideshowbarker Aug 24 '17 at 08:53
  • OK, the added details indicate that for the OPTIONS request you’re not actually getting any response back for the server at all. And the screenshot shows that the browser has logged two error messages to the console. So you should go into the Console tab and see what the error messages are—it may be the same error message twice—and consider also pasting the error message(s) back into the question – sideshowbarker Aug 24 '17 at 09:27
  • I've made some progress, thanks to your help. I removed **all** of the request headers, and left the **withCredentials: true** in the request. I think maybe this removes the need for a preflight request; don't know for sure. Anyway, in Chrome and Firefox this works; I see a basic auth dialog and when I log in my application works. However, in SafariI see no login box, it just fails. And if I try to put the Authorization: Basic header in the request headers it fails in all browsers (presumably because we're back to preflight requests). I can move on now though, so many thanks for your help. – Ian Piper Aug 24 '17 at 11:11
  • Glad to hear you made progress. As far as why the preflight’s failing, from what I can glean it’s for the general reason explained in the answer at https://stackoverflow.com/questions/45405983/http-custom-authorization-header-in-request-with-springboot-and-angular2/45406085#45406085 (which is a common problem; the explanation there applies in general, not Spring-specific), and the solution is to configure the API endpoint to not require authentication for OPTIONS requests—since that authentication will always fail in case of CORS preflights, because the browser sends no credentials for those – sideshowbarker Aug 24 '17 at 11:22
  • But incidentally what’s very odd about the Safari devtools screenshots posted to the question is that they seem to indicate that because the preflight fails, the browser is blocking devtools access to the response. Safari devtools should not be doing that—even if the browser blocks your frontend code from seeing the response, you should still be able from browser devtools to have full access to all the details of the response. Try looking at a failed preflight in Firefox and Chrome devtools, and you’ll see you can still get to all the response details there. – sideshowbarker Aug 24 '17 at 11:26

0 Answers0