The core question here is: "how do I allow custom headers in a CORS GET request that is handled with the Serverless framework?". If you know the answer to that, pass Go, collect $200 and please answer that question. If it's not a question with a straight answer, here are the details:
I am writing an app using the Serverless framework on AWS Lambda (the API is managed through AWS API Gateway. Frankly, I'm not entirely sure what that means or what benefit that provides me but that's what Serverless automatically configured for me). I am attempting to create an open API which requires CORS to be enabled. I am using the Lambda Proxy integration. I have followed the practices found here. They have brought me partial success. My app currently has CORS enabled if I do not include my custom headers. However, it still does not work with custom headers.
When I send the following request to my API:
var data = null;
var xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "https://api.spongebobify.com/");
xhr.setRequestHeader("text", "hey");
xhr.send(data);
... I get this error:
Failed to load https://api.spongebobify.com/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://forum.serverless.com' is therefore not allowed access.
This error message is confirmed if I check the "response headers" using Chrome dev tools: there is no Access-Control-Allow-Origin in the response headers.
However, if I send the same request with the setRequestHeader()
commented out, it works perfectly (yes, I know it returns a 403 error: that is intentional behavior).
Here's what I think is happening. My service has two potential CORS problems: domain related (a request not coming from the origin domain) and custom header related (a header not safe-listed by the CORS spec, more here). Somehow, the Serverless framework trips up on the second issue which causes it not even get to the point where it issues the appropriate headers to allow all ("*") domains.
Here is my serverless.yml config file:
# serverless.yml
service: spongebobify
provider:
name: aws
runtime: nodejs6.10
stage: dev
region: us-east-1
functions:
app:
handler: handler.endpoint
events:
- http: GET /
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
- Startlower
- Text
- Access-Control-Allow-Headers
- Access-Control-Allow-Origin
allowCredentials: false
and here is the function that I am trying to run. You can see my many attempts to set the headers properly. I'm 60% convinced that a fix will come via the serverless.yml
file at this point.
"use strict";
const spongebobify = require("spongebobify");
module.exports.endpoint = (event, context, callback) => {
let startLower = event.headers.startlower === "false" ? false : true;
try {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Headers": "content-type,origin,text,startlower",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"content-type": "text/plain",
"Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
},
body: spongebobify(event.headers.text, startLower)
};
callback(null, response);
} catch (err) {
console.log(err);
const response = {
statusCode: 403,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Headers": "content-type,origin,X-text,startlower",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"content-type": "text/plain",
"Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
},
body: "Malformed request."
};
callback(null, response);
}
};
You can replicate my problem my running the above XMLHttpRequest
in the dev console on the following sites:
Enable CORS support on a REST API resourceSign in to the API Gateway console at https://console.aws.amazon.com/apigateway . Choose the API from the APIs list. Choose a resource under Resources. This will enable CORS for all the methods on the resource.
To enable CORS for the Lambda proxy integration, you must add Access-Control-Allow-Origin:domain-name to the output headers. domain-name can be * for any domain name. Save this answer.
I think the issue is that you're mixing the short form of the HTTP event (- http: GET /
) with the long form that adds additional options.
Try using this:
functions:
app:
handler: handler.endpoint
events:
- http:
method: GET
path: /
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
- Startlower
- Text
- Access-Control-Allow-Headers
- Access-Control-Allow-Origin
allowCredentials: false
The main changes are:
1) Adding method
and path
keys on the http
event object, and
2) Indenting the cors
object another level. It was previously at the top level of the http
event.
Let me know if this helps :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With