2

I want to send data from Angular into Logstash HTTP input. Because Logstash HTTP input doesn't handle the CORS preflight request gracefully (can't disable basic authentication for OPTIONS), I want to use nginx in front of Logstash as a proxy. The relevant part of my nginx configuration is as follows:

location / {
    add_header 'Access-Control-Allow-Origin' *;
    add_header 'Access-Control-Allow-Credentials' true;
    add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With';
    add_header 'Access-Control-Expose-Headers' 'Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With,Content-Disposition';

    return 200;

    limit_except OPTIONS {
        auth_basic "Restricted access zone";
        auth_basic_user_file /etc/nginx/htpasswd;
        proxy_pass http://localhost:31311;  # logstash listens here
    }
}

This works fine for allowing the OPTIONS. Relevant cURL output (from Chrome's "Copy as cURL" function:

> OPTIONS / HTTP/1.1
> Host: MUNGED
> Access-Control-Request-Method: PUT
> Origin: MUNGED
> Accept-Encoding: gzip, deflate, br
> Accept-Language: en-IL,en;q=0.9,he-IL;q=0.8,he;q=0.7,en-US;q=0.6
> User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/71.0.3578.80 Chrome/71.0.3578.80 Safari/537.36
> Accept: */*
> Referer: MUNGED
> Connection: keep-alive
> DNT: 1
> Access-Control-Request-Headers: content-type
> 
{ [5 bytes data]
< HTTP/1.1 200 OK
< Server: nginx/1.14.0 (Ubuntu)
< Date: Tue, 25 Dec 2018 20:13:01 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,PUT
< Access-Control-Allow-Headers: Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With
< Access-Control-Expose-Headers: Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With,Content-Disposition

(I'm not sure if Access-Control-Expose-Headers is necessary, but it doesn't work either way...)

However, for some reason, the subsequent PUT fails because the Authorization header isn't sent in the request. The relevant Angular code looks something like:

const headers = new HttpHeaders();                                                                                                                                                  
headers.set('Authorization', 'basic ' + btoa(environment.eventUsername + ':' + environment.eventPassword));                                                                         
headers.set('Content-Type', 'application/json');                                                                                                                                    
this.httpClient.put(environment.eventTrackingUrl, event, { headers: headers }).subscribe();

However, the PUT headers look like this (again using copy to cURL):

> PUT / HTTP/1.1
> Host: MUNGED
> Origin: MUNGED
> Accept-Encoding: gzip, deflate, br
> Accept-Language: en-IL,en;q=0.9,he-IL;q=0.8,he;q=0.7,en-US;q=0.6
> User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/71.0.3578.80 Chrome/71.0.3578.80 Safari/537.36
> Content-Type: application/json
> Accept: application/json, text/plain, */*
> Referer: MUNGED
> Connection: keep-alive
> DNT: 1
> Content-Length: 144
> 
} [144 bytes data]
* upload completely sent off: 144 out of 144 bytes
{ [5 bytes data]
< HTTP/1.1 401 Unauthorized
< Server: nginx/1.14.0 (Ubuntu)
< Date: Tue, 25 Dec 2018 21:03:34 GMT
< Content-Type: text/html
< Content-Length: 606
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Restricted access zone"

No Authorization header... This results in the PUT failing with a 401.

Any clues as to why Angular or the browser isn't sending the Authorization header?

antgel
  • 1,241
  • 1
  • 14
  • 29
  • In your frontend Angular request code, are you setting 'withCredentials: true'? – sideshowbarker Dec 25 '18 at 23:41
  • No, according to the documentation it's deprecated, but the replacement docs don't explain anything about credentials! I actually opened an issue about it tonight... Any insights into this matter? Very confusing. https://github.com/angular/angular/issues/27828 – antgel Dec 25 '18 at 23:51
  • You could send the request directly with the native fetch API instead, with `credentials: 'include'`: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included – sideshowbarker Dec 26 '18 at 00:01
  • I'm not sure that will work in my use case: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials. I need the wildcard because we need to accept requests from whitelabelled domains. – antgel Dec 26 '18 at 06:04
  • If you have a discrete set of known whitelabelled domains, the then way to deal with that is it maintain the set in your server application code and have your server application-code logic check the value of the Origin request header against that set, and if there’s a match, then take the value of the Origin request header and use it to set the value of the Access-Control-Allow-Origin response header. – sideshowbarker Dec 26 '18 at 08:24
  • Thanks, I'm doing `add_header 'Access-Control-Allow-Origin' "$http_origin";` for now in nginx, and it seems to work. Currently testing `fetch()`. – antgel Dec 26 '18 at 08:56
  • @sideshowbarker Well, first somebody pointed out that `withCredentials` isn't deprecated (see https://github.com/angular/angular/issues/27828), but it still didn't work for me, it opened a basic auth dialog... But `fetch()` did work, so thanks a lot! Do you want to add it as an answer, so I can accept it? – antgel Dec 26 '18 at 11:07

0 Answers0