Why does my serverless Lambda function reject Cache-Control header? - amazon-web-services

I'm using FineUploader to upload files to S3. While utilizing the DELETE functionality I get the following error:
XMLHttpRequest cannot load
https://xxxxxxx.execute-api.us-east-1.amazonaws.com/prod/deleteS3File?.
Request header field Cache-Control is not allowed by
Access-Control-Allow-Headers in preflight response.
The lambda function was created using the awesome Serverless Framework with the following configuration:
functions:
deleteS3File:
handler: handler.deleteS3File
events:
- http:
path: deleteS3File
method: POST
integration: lambda
cors: true
response:
headers:
Access-Control-Allow-Origin: "*"
Any idea what this error means for a Lambda function and how to tackle it?

The POST verb preflights an OPTIONS verb that you don't support.
So, you need to create a method for OPTIONS that will return status code 200 (success) and with the expected headers.
For both the OPTIONS and POST, try the following headers:
Access-Control-Allow-Origin: "*"
Access-Control-Allow-Methods: "GET, HEAD, OPTIONS, POST, PUT, DELETE"
Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Cache-Control, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
you may fine tune the headers later to allow just what you need

Related

No 'Access-Control-Allow-Origin' header for axios post request to aws lambda function

I'm having trouble making a post request to a lambda function with axios in my web app. I get back the error message "Access to XMLHttpRequest at 'lambdalink' from origin 'http://localhost:4200' 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."
Code for the axios request:
const config = {
method: 'post',
url: 'lambdalink',
headers: {
'Access-Control-Allow-Origin': '*',
},
data: {
info: JSON.stringify(data)
}
};
Axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
When I make the request without the data param in the config the request goes through just fine, but once I add data to it I get that error. My CORS configuration for the lambda function is as follows:
Access-Control-Allow-Origin:
"*", "http://localhost:4200/", "http://localhost:4200"
Access-Control-Allow-Headers:
"access-control-allow-origin"
Access-Control-Allow-Methods:
"POST"
I've tried different combinations of these settings, but no luck. Am I missing something server side or am I just not handling CORS correctly in the browser?

Add X-Frame-Options header to all URLs using CloudFront functions

I have a single page application and I'm trying to prevent clickjacking by adding X-Frame-Options header to the HTML responses. My website is hosted on S3 through CloudFront.
The CloudFront distribution is configured to send index.html by default:
Default root object
index.html
In the Error Pages section I configured 404 page to also point to the index.html. This way all URLs that are not in S3 return the default HTML, i.e. /login, /admin etc.
Update The 403 status code is also configured:
Then I have created a CloudFront function as described here and assigned it to the Viewer response:
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['x-frame-options'] = {value: 'DENY'};
return response;
}
This works, but only for /:
curl -v https://<MYSITE.com>
....
< x-frame-options: DENY
For other URLs it doesn't work - the x-frame-options header is missing:
curl -v https://<MYSITE.com>/login
....
< x-cache: Error from cloudfront
My question is - why my cloudfront function does not append a header in the error response, and what can I do to add it?
I understand that your questions are:
Q1: Why does the CloudFront function work for /?
Q2: Why doesn't the CloudFront function work for other url path?
Please refer to the responses below:
A1: Since you might specify a Default Root Object [1] (e.g.index.html) which returning the object when a user requests the root URL. When CloudFront returns the object with 200 ok, the CloudFront Function will be invoked on the viewer response event.
A2: You might not give the s3:ListBucket permissions in your S3 bucket policy(e.g. OAI). As the result, you will get Access Denied(403) errors for missing objects instead of 404 Not Found errors. Namely, the Error Pages you have configured isn't applied to this case, and the CloudFront Function won't be invoked because the HTTP status code is higher than 399[2].
[Updated] Suggestion:
Since CloudFront does not invoke edge functions for viewer response events when the origin returns HTTP status code 400 or higher. However, Lambda#Edge functions for origin response events are invoked for all origin responses. In this senario, I'll suggest that we should use Lambda#Edge instead of CloudFront Functions.
For your convenience, please refer to the sample code of l#e:
exports.handler = async (event, context) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY',
}];
return response;
};
FYI. Here is my curl test result:
# PATH: `/`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___
# PATH: `/login`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/login
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___

IE 11 not sending cookies with CORS get request

I am making an ajax GET call currently from localhost:8090 with following:
$.ajax({
url: 'https://dev-855592.okta.com/api/v1/sessions/me',
method: 'GET',
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
success : function(data){
console.log(data);
},
error : function(data){
console.log('Session not found');
},
});
As this is an Okta call, it requires cookies to be sent with the request.
But IE 11 is not sending any cookies in the request.
I tried floowing things already:
"Access Data Sources Across Domains : Enabled" in ie11 settings in trusted sites. This solution is working.
But I don't want any impact on end-user and assuming end user has already accepted third party cookies, what should be the best way to achieve this?
If you want to enable CORS requests in IE 11, your server must attach the following headers to all responses:
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL
Access-Control-Allow-Headers: Overwrite, Destination, Content-Type, Depth, User-Agent, Translate, Range, Content-Range, Timeout, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Location, Lock-Token, If
Access-Control-Expose-Headers: DAV, content-length, Allow
Optionally you can also attach the Access-Control-Max-Age header specifying the amount of seconds that the preflight request will be cached, this will reduce the amount of requests:
Access-Control-Max-Age: 3600
You could refer to this link about implementing CORS for a specific server.

Not getting a specific header from AWS API Gateway

I have built an SPA which has react as its front-end and the serverless(AWS lambda) framework has its back-end.
The react app has been deployed in S3. This app communicates to the lambda functions(serverless) through AWS API Gateway.
Since the react app which is in S3 domain and the back-end (AWS API Gateway, AWS lambda, DynamoDB) is in different domain. CORS problem occurs.
To rectify the CORS problem, I have hardcoded the Access-Control-Allow-Origin header as react app's domain URL. So, the backend understands the origin is from known source and resolves the origin-CORS error.
But, There is another header called "Access-Control-Allow-Credentials" which has to be true to access request's cookies.
IF Access-Control-Allow-Origin is "*", then Access-Control-Allow-Credentials must be false, for privacy concern.
so, I have set Access-Control-Allow-Origin to React's domain, then Access-Control-Allow-Credentials may be true.
I added Access-Control-Allow-Credentials header in API-gateway response method and in integration response method. The problem is I am not getting the Access-Control-Allow-Credentials header in client side as response. I am getting all other headers except this one. Testing API Gateway console gives the header, but testing on postman and in browser, does not receive the above header.
The headers in client are
but on testing AWS API Gateway gives this .
The problem is Access-Control-Allow-Credentials header is not retrieved as response in browser and in postman, but appears when testing in AWS API Gateway console. What might be the problem ?
Please post exported Swagger for your API. I can confirm there is nothing special about "Access-Control-Allow-Credentials" header, so you should be able to map this according to your needs.
Example API:
swagger: "2.0"
info:
version: "2016-09-17T00:36:34Z"
title: "foo"
host: "45c24yfor1.execute-api.us-east-1.amazonaws.com"
basePath: "/test"
schemes:
- "https"
paths:
/:
get:
consumes:
- "application/json"
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Cookie:
type: "string"
Access-Control-Allow-Credentials:
type: "string"
Set-Cookie:
type: "string"
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Credentials: "'test'"
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
type: "mock"
definitions:
Empty:
type: "object"
title: "Empty Schema"
Command:
curl -v https://45c24yfor1.execute-api.us-east-1.amazonaws.com/test
< HTTP/1.1 200 OK
< Date: Sat, 17 Sep 2016 00:36:46 GMT
< Content-Type: application/json
< Content-Length: 0
< Connection: keep-alive
< Access-Control-Allow-Credentials: test
< x-amzn-RequestId: d04135bc-7c6e-11e6-a593-559956e50e8a
< X-Cache: Miss from cloudfront
< Via: 1.1 b63769e2d89c89274acd908e4bfcb9f4.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: 3Nevesi15lEGox0jrFq8B2HEknHbbFfISlg4yv7Lw3X90S2sUIWE_g==

401 return from an API Gateway Custom Authorizer is missing 'Access-Control-Allow-Origin' header

In order to prevent users who have not logged in to call my lambda function through the AWS API Gateway, I'm using the Custom Authorizer lambda solution.
If the request is authorized (200) and I get a response from the called lambda everything works fine and I get the Access-Control-Allow-Origin header.
But if the request is not authorized, I get a 401 that has no Access-Control-Allow-Origin header, therefore preventing me from reading the 401 status of the response and redirecting the user to the log-in page.
I believe this is because the Custom Autorization mechanism is unaware that the request needs to use CORS. Does anyone know that this is actually the issue? Are you aware of any possible solution?
I'm happy to announce the new Gateway Responses feature which allows you to customize the error responses for requests that don't call your integration. This allows you to ensure that CORS headers are included, even on failed auth requests.
Read more in our documentation, which includes a CORS example.
Yes, this is a known bug with API Gateway custom authorizers. Thanks for bringing this to our attention. The team will update this post when we've deployed a fix. Apologies for the inconvenience.
The easiest way to resolve this for all 4XX error (including 401 errors) is to go to "Gateway Responses" and then select "Default 4XX" and then add the the header "Access-Control-Allow-Origin" with the value '*'.
See screenshot:
This works for me (inline in AWS::APIGateway: definition)
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Dev
GatewayResponses:
UNAUTHORIZED:
StatusCode: 401
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
ACCESS_DENIED:
StatusCode: 403
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
StatusCode: 500
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
RESOURCE_NOT_FOUND:
StatusCode: 404
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
The available GatewayResponses Naming are:
DEFAULT_INTERNAL
DEFAULT_4XX
DEFAULT_5XX
RESOURCE_NOT_FOUND
UNAUTHORIZED
ACCESS_DENIED
AUTHORIZER_FAILURE
AUTHORIZER_CONFIGURATION_ERROR
MISSING_AUTHENTICATION_TOKEN
INVALID_SIGNATURE
EXPIRED_TOKEN
INTEGRATION_FAILURE
INTEGRATION_TIMEOUT
API_CONFIGURATION_ERROR
UNSUPPORTED_MEDIA_TYPE
REQUEST_TOO_LARGE
BAD_REQUEST_PARAMETERS
BAD_REQUEST_BODY
THROTTLED
QUOTA_EXCEEDED
INVALID_API_KEY
WAF_FILTERED
So you could specify the Response customization for these Controlled AWS responses.
Because it took me a while to figure out how to put it all together in Cloud Formation, here is a snippet showing how to set it up.
...
MyApi:
Type: "AWS::ApiGateway::MyApi"
Properties:
Description: My API
Name: "my-api"
MyApiAuthorizer:
Type: "AWS::ApiGateway::Authorizer"
Properties:
Name: "my-api-authorizer"
IdentitySource: "method.request.header.Authorization"
ProviderARNs:
- !GetAtt MyUserPool.Arn
RestApiId: !Ref MyAApi
Type: COGNITO_USER_POOLS
MyApiGatewayResponse:
Type: "AWS::ApiGateway::GatewayResponse"
Properties:
ResponseParameters:
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
ResponseType: UNAUTHORIZED
RestApiId: !Ref MyApi
StatusCode: "401"
If, like me, you are running into problems with API Gateway V2, specifically with an HTTP API - the ANY method doesn't seem to work with the plug and play CORS offering. I had to individually create a route for each method (annoying because they all call the same lambda function, but oh well).
Adding to the answers above, if you're not using Cloudformation/SAM template, you can save yourself some manual steps using this python script:
import boto3
import sys
if len(sys.argv) != 3:
print("usage: python script.py <API_ID> <STAGE>")
exit()
client = boto3.client('apigateway')
response = client.put_gateway_response(
restApiId=sys.argv[1],
responseType='UNAUTHORIZED',
statusCode='401',
responseParameters={
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
}
)
response = client.create_deployment(
restApiId=sys.argv[1],
stageName=sys.argv[2])
To fix this for our SPA (we are using AWS Cognito authorizer) we added the next Response Headers in DEFAULT 4xxx and DEFAULT 5xxx Gateway responses:
Access-Control-Allow-Origin '{url_of_your_front-end}'
Access-Control-Allow-Headers '{url_of_your_front-end}'
we set {url_of_your_front-end} instead of '*', because the browser didn't like it :D
as an additional we set Access-Control-Allow-Credentials 'true' header to make a browser happy.
Picture with all headers
Adding the response header "Access-Control-Allow-Origin" for the Unauthorized response should fix this issue
Select the Api
Select the Gateway Responses
Select Unauthorized
Edit the response header and add Access-Control-Allow-Origin with value "*"