Enabling CORS for Cowboy REST API
Asked Answered
R

4

11

How can I enable CORS for cowboy rest handler? I tried to add options/2 method, like this:

options(Req, State) ->
    {[
       {<<"access-control-allow-origin">>, <<"*">>},
       {<<"access-control-allow-methods">>, <<"GET, OPTIONS">>}
     ], Req, State}.

but this causes errors like:

Error in process <0.263.0> with exit value: {{case_clause,{[{<<27 bytes>>,<<1 byte>>},{<<28 bytes>>,<<12 bytes>>}],{http_req,#Port<0.2636>,ranch_tcp,keepalive,<0.263.0>,<<7 bytes>>,{1,1},{{127,0,0,1},56522},<<9 bytes>>,undefined,9090,<<8 bytes>>,undefined,<<0 bytes>>,undefined,<<0 bytes>>,[],[{<<4 bytes>>,<<14 bytes>>},{<<10 bytes>>,<<74 bytes>>},{<<6 bytes>>,<<63 bytes>>},{<<15 bytes>>,<<14 bytes>>},{<<15 bytes>>,<<13 bytes>>},{<<6 bytes>>,<<4 bytes>>},{<<29 bytes>>,<<3 bytes>>},{<<30 bytes>>,<<16 bytes>>},{<<10 bytes>>,<<10 bytes>>}],[{<<10 bytes>>,[<<10 bytes>>]}],undefined,[],waiting,undefined,<<0 bytes>>,false,waiting,[],<<0 bytes>>,undefined},undefined...

Where is my mistake?

Randi answered 25/3, 2013 at 13:21 Comment(0)
C
15

Cowboy documentation says you need to set header by using set_resp_headers, not return a list of headers:

 %% If you need to add additional headers to the response at this point,
 %% you should do it directly in the options/2 call using set_resp_headers.

So your code should look like:

options(Req, State) ->
    Req1 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"GET, OPTIONS">>, Req),
    Req2 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<"*">>, Req1),
    {ok, Req2, State}.

You can test with

curl -H "Origin: http://example.com" \
  -H "Access-Control-Request-Method: GET" \
  -H "Access-Control-Request-Headers: X-Requested-With" \
  -X OPTIONS --verbose \
http://localhost:8080
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r     zlib/1.2.5
> Host: localhost:8080
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: GET
> Access-Control-Request-Headers: X-Requested-With
>
< HTTP/1.1 200 OK
< connection: keep-alive
< server: Cowboy
< date: Mon, 25 Mar 2013 15:59:11 GMT
< content-length: 0
< access-control-allow-methods: GET, OPTIONS
< access-control-allow-origin: *
<
* Connection #0 to host localhost left intact
* Closing connection #0
Coshow answered 25/3, 2013 at 16:0 Comment(1)
Where should the options/2 be implemented? The documentation is not clear about which file we should implement it in.Keyway
P
1

My REST allows acceess to resources only and only if token is provided. So I had to implement is_authorized/2 callback which is different for OPTIONS and GET requests. See below:

1) Implement options/2 as follows

options(Req0, State) ->
  % cors
  Req1 = cowboy_req:set_resp_header(
    <<"access-control-allow-methods">>, <<"GET, OPTIONS">>, Req0),
  Req2 = cowboy_req:set_resp_header(
    <<"access-control-allow-origin">>, <<"*">>, Req1),
  Req3 = cowboy_req:set_resp_header(
    <<"access-control-allow-headers">>, <<"authorization">>, Req2),
  {ok, Req3, State}.

2) Implement is_authorized/2 as follows

is_authorized(Req, State) -> 
  case cowboy_req:method(Req) of
    <<"GET">> ->
      case cowboy_req:parse_header(<<"authorization">>, Req) of
        {bearer, <<Token/binary>>} ->
          {true, Req, Token};
        _ ->
          {{false, <<"Basic realm=\"cowboy\"">>}, Req, State}
      end;
    <<"OPTIONS">> ->
      {true, Req, State}
  end.

3) to_json/2 method sending access-control-allow-origin

to_json(Req0, State) ->
  R = #{ foo => [<<"bar">>] },
  % cors
  Req1 = cowboy_req:set_resp_header(
      <<"access-control-allow-origin">>, <<"*">>, Req0),
  Body = jsx:encode(R),
  {Body, Req1, State}.
Prate answered 15/2, 2017 at 12:23 Comment(0)
E
0

You can try this:

Req1 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<"*">>, Req0 -> Worked for me.
Exaggerated answered 25/9, 2019 at 17:2 Comment(1)
This question is 6.5 years old with a high quality accepted answer. This provided answer, was flagged for review as a Low Quality Post. Here are some guidelines for How do I write a good answer?. This provided answer may be correct, but it could benefit from an explanation. Code only answers are not considered "good" answers. From review.Repent
S
0

In addition to the answers found here, Cowboy 2.* appears to have a middleware feature which you can use to intercept all REST handler requests to a server, and add the required response headers: https://ninenines.eu/docs/en/cowboy/2.7/guide/middlewares/

An example CORS middleware implementation can be found in this Github gist: https://github.com/ninenines/cowboy/blob/master/examples/markdown_middleware/src/markdown_middleware_app.erl

Sloat answered 4/1, 2020 at 22:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.