CORS Issue - AWS Gateway API, ION, Pedestal Service

The client application (shadow-cljs re-frame &reagent) that I am running on my laptop is not able to access the ION server with AWS Gateway. Showing the following error on the Chrome browser console:

–>> Access to XMLHttpRequest at ‘https://xxxxxxxxx.execute-api.us-west-2.amazonaws.com/dev/login’ from origin ‘http://localhost:9875’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

–>> xhrio.js:631 POST https://xxxxxxxx.execute-api.us-west-2.amazonaws.com/dev/login net::ERR_FAILED

I use Pedestal for the server. The service def is

(def service {
::http/routes routes
::http/resource-path “/public”
::http/chain-provider provider/ion-provider
::http/allowed-origins {:creds true :allowed-origins [“http://localhost:9875”]}
::cors/allow-origin {:creds true :allowed-origins (constantly true)}})

This error happens only with Chrome; With curl the API works fine, With -v flag, the response is as follows:

Using HTTP2, server supports multi-use

  • Connection state changed (HTTP/2 confirmed)
  • Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
  • Using Stream ID: 1 (easy handle 0x7fb795806600)

POST /dev/login HTTP/2
Host: me03wrbls1.execute-api.us-west-2.amazonaws.com
User-Agent: curl/7.54.0
Accept: /
Content-Type: application/json
Content-Length: 28

  • Connection state changed (MAX_CONCURRENT_STREAMS updated)!
  • We are completely uploaded and fine
    < HTTP/2 200
    < date: Mon, 07 Sep 2020 07:41:37 GMT
    < content-type: text/plain
    < content-length: 26
    < x-amzn-requestid: fd26d15a-f784-476d-846d-e423282df94b
    < x-permitted-cross-domain-policies: none
    < x-xss-protection: 1; mode=block
    < access-control-allow-origin: ‘http://localhost:9875
    < strict-transport-security: max-age=31536000; includeSubdomains
    < x-frame-options: DENY
    < content-security-policy: object-src ‘none’; script-src ‘unsafe-inline’ ‘unsafe-eval’ ‘strict-dynamic’ https: http:;
    < x-download-options: noopen
    < x-amz-apigw-id: SfCXtErhPHcFTyQ=
    < x-content-type-options: nosniff
    < access-control-allow-methods: POST
    < x-amzn-trace-id: Root=1-5f55e431-c2977ea2ed55ecd69347a9fd;Sampled=0
    <…then I see the response correct.

I also have enabled CORS at the AWS gateway as per the AWS doc. But the recommendation for ANY is to set it at the program level.

Has anyone encountered this situation? My google research shows it is a common problem. But strictly in the context of Pedestal, Datomic ion, AWS Gateway API & Chrome client, any possible solutions? Any response is much appreciated.

Thanks & Regards,
H

If you set an APIGateway resource to proxy all methods to the ion server, then there’s no need to configure CORS at the ApiGateway level since the OPTION requests will be routed to the server and you should let the application deal with it.

In your service-map you have both ::http/allowed-origins and ::cors/allow-origin. I think you only need ::http/allowed-origins, having ::cors/allow-origin shouldn’t do anything.

Can you check if the preflight request is reaching the server and is accepted?

curl -H "Origin: http://localhost:9875" -X OPTIONS --verbose https://me03wrbls1.execute-api.us-west-2.amazonaws.com/dev/login

Thank you for the response.

Can you check if the preflight request is reaching the server and is accepted? Yes. It is getting accepted while using the curl on the terminal. And getting the right response too.

However, on Chrome, the response is the same!!

–>> Access to XMLHttpRequest at ‘https://xxxxxxxxx.execute-api.us-west-2.amazonaws.com/dev/login’ from origin ‘http://localhost:9875’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

–>> xhrio.js:631 POST https://xxxxxxxx.execute-api.us-west-2.amazonaws.com/dev/login net::ERR_FAILED

Ok. I made it working!!. First, thank you Daniel for your response. It helped as it validated what I was doing.

I deleted my old API and created a new one. This time, I didn’t enable CORS at the API. The resource had only one method - ANY, and deployed it.

I cleaned up my service def as follows:

(def service {
::http/routes routes
::http/resource-path “/public”
::http/chain-provider provider/ion-provider
::http/allowed-origins {:creds true :allowed-origins (constantly true)}})

Not sure this helped, but added this as part of the service handler response:

:headers {“Access-Control-Allow-Methods” "‘OPTIONS, POST’ ",
“Access-Control-Allow-Origin” “’*’”
“Access-Control-Allow-Headers” “‘Content-Type’”
“Access-Control-Allow-Credentials” true}

That’s it! I think the fact that I enabled CORS at the GW caused the issue.

Note: I was aware of the following comment from AWS docs:

“When applying the above instructions to the ANY method in a proxy integration, any applicable CORS headers will not be set. Instead, your backend must return the applicable CORS headers, such as Access-Control-Allow-Origin.”

But another comment from another AWS doc inspired me to remove the CORS at the API level:

“If you configure CORS for an API, API Gateway ignores CORS headers returned from your backend integration.”

Again, thanks for the responses.
-H

Good to hear you got it working.

If you configure both an ANY method and an OPTIONS method on the same ApiGateway resource, I think the most specific one gets selected.

I see. Thanks