caddy: Caddy does not respond correctly to preflight requests with basic authorization

1. What version of Caddy are you running (caddy -version)?

Caddy 0.9.5 with the cors addon downloaded from the caddy website on debian testing 64bit.

2. What are you trying to do?

I am trying to serve an API with caddy using basic authorization. The frontend (using ember.js) access the API with CORS with preflight requests and the basic authorization. However, caddy responds to the preflight requests with 401 if the request does not contain the authorization header (which you are not allowed to send in the preflight request: CORS spec). If I add the authorization header (which I am actually not allowed to do), then caddy responds correctly. In the following I show a minimal example which does not use a reverse proxy but instead just serves the local directory.

3. What is your entire Caddyfile?

localhost:8080 {
    log access.log
    errors errors.log
    basicauth / user pass
    cors
}

4. How did you run Caddy (give the full command and describe the execution environment)?

I run caddy using ./caddy in the directoy where the caddy file and an index.html are present.

5. Please paste any relevant HTTP request(s) here.

This is the correct preflight request:

$ curl -v 'http://localhost:8080' -X OPTIONS -H 'access-control-request-method: GET' -H 'origin: http://localhost:4200' -H 'access-control-request-headers: authorization'                                        
* Rebuilt URL to: http://localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.52.1
> Accept: */*
> access-control-request-method: GET
> origin: http://localhost:4200
> access-control-request-headers: authorization
> 
< HTTP/1.1 401 Unauthorized
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Www-Authenticate: Basic realm="Restricted"
< X-Content-Type-Options: nosniff
< Date: Fri, 07 Apr 2017 17:37:42 GMT
< Content-Length: 17
< 
401 Unauthorized
* Curl_http_done: called premature == 0

And this is the incorrect preflight request which includes the authorization header:

$curl -v 'http://localhost:8080' -X OPTIONS -H 'access-control-request-method: GET' -H 'origin: http://localhost:4200' -H 'access-control-request-headers: authorization'  -H "Authorization: Basic dXNlcjpwYXNz"
* Rebuilt URL to: http://localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.52.1
> Accept: */*
> access-control-request-method: GET
> origin: http://localhost:4200
> access-control-request-headers: authorization
> Authorization: Basic dXNlcjpwYXNz
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
< Access-Control-Allow-Origin: http://localhost:4200
< Server: Caddy
< Vary: Origin
< Date: Fri, 07 Apr 2017 17:38:47 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< 
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact

6. What did you expect to see?

As far as I know, the first request (excluding the credentials) should be answered as the second request with 200 and an empty response body.

7. What did you see instead (give full error messages and/or log)?

See above: caddy responds with 401

8. How can someone who is starting from scratch reproduce the bug as minimally as possible?

See above: Serve a directory with caddy with basic authorization and try to send an OPTIONS preflight request with does not include the credentials but indicates their presence via access-control-request-headers: authorization. In my actual example, caddy acts as a reverse proxy for a python backend but that doesn’t affect the problem.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 3
  • Comments: 18 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Sorry, found a way to do this:

@basic_matcher {
        path /*
        not method OPTIONS
}

basicauth @basic_matcher { ... }

@noreek your path matcher there is redundant, because that will always be true. Change it to this instead: @basic_matcher not method OPTIONS