malformed lambda proxy response, can't find the error - aws-lambda

I'm trying to get data from my api using lambda and API Gateway, when sending the get requests I get this error:
Execution failed due to configuration error: Malformed Lambda proxy response
the code returns:
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'Access-Control-Allow-Origin': '*' ,
"isBase64Encoded": False,
'body': json.dumps(data)
}
what am I doing wrong?

The proxy response must be a dictionary which must only contain the following keys:
headers
body
isBase64Encoded
multiValueHeaders
statusCode
In your example you have one additional key Access-Control-Allow-Origin and that's why API Gateway claims that it is a malformed response. The documentation linked above even explicitly states that Access-Control-Allow-Origin must be part of headers:
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.
If you change the response to the following it should work fine:
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'
},
'isBase64Encoded': False,
'body': json.dumps(data)
}

Related

Api gateway and lambda proxy cors problem

I have a problem with CORS in my API gateway lambda proxy. I was trying to struggle with this but still, I get No 'Access-Control-Allow-Origin' header is present on the requested resource or Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I use apollo client on frontend where I put 'Access-Control-Allow-Origin': '*', header:
const authLink = setContext((operation, { headers }) => {
const token = window.localStorage.getItem('accessToken');
return {
headers: {
...headers,
authorization: `Bearer ${token}`,
'Access-Control-Allow-Origin': '*',
}}}
Next, as a proxy, I use 'apollo-server-lambda' where I have below handler config:
const handler = server.createHandler({
expressGetMiddlewareOptions: {
cors: {
origin: '*',
credentials: true,
},}});
My graphql API gateway invokes some lambdas, every lambda is wrapped in below wrapper:
export const middyfy = (handler: any) => {
return middy(handler).use(middyJsonBodyParser()).use(cors());
};
My graphql proxy serverless configuration looks that:
events: [
{
http: {
method: 'post',
path: '/',
integration: 'lambda-proxy',
cors: true,
},
},
{
http: {
method: 'get',
path: '/',
cors: true,
integration: 'lambda-proxy',
},
},
],
My API gateway OPTIONS configuration:
I will be glad for any help
Problem solved, browser message was confusing.
In the end, I had the error: No 'Access-Control-Allow-Origin' header is present on the requested resource. It was tricky because It wasn't any problem with CORS. I was passing wrong a Cognito JWT Token in my authorization header. Finally, I didn't need to pass 'Access-Control-Allow-Origin': '*', on the frontend side and use cors middleware on the lambda side.

AWS Lambda Set-Cookie header not setting in the browser

I'm trying to set a cookie in my aws lambda function response. I don't have any header mapping as I'm using lambda proxy integration with API Gateway. The response code looks like this in the lambda function:
exports.handler = async (event) => {
const response = {
statusCode: 200,
"multiValueHeaders": {
"Set-Cookie": ["gtgm=6c7729687d5ff1a05f1a5dfb15ce3b8fa3f2b590; path=/; expires=Fri, 13-Feb-2032 13:27:44 GMT; secure; HttpOnly; SameSite=None"]
},
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Credentials": true,
},
};
return response;
};
I use fiddler to check the response and I can see the "Set-Cookie..." in the response which leads me to believe that the code above is correct? The issue is that the browser just ignores it and doesn't set any cookies at all except for the AWS DNT cookie. I'm not sure what else to check or if I've missed anything in the cookie config.
This is what my request looks like:
<Button
onClick={() => {
fetch(
"https:mysupercoolapi.com/cookie-test/",
{
// credentials: "include",
headers: {
"Content-Type": "application/json",
// "Access-Control-Allow-Credentials": "true",
},
}
)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}}
>
Cookie Test
</Button>
Not sure what I'm missing or where I'm going wrong.
I magaed to figure this one out myself. The setup above was fine but the browser requires you to have the following set properly in order for it to actually set the cookie:
In the request: Set "credentials" as include. Depending on what library you're using (fetch would be credentials: "include"). You can see it is commented out in my original post above which is not correct.
In the Response header in the Lambda function you need to set you origin e.g., "Access-Control-Allow-Origin": "http://localhost:3002". After testing I switched it to my actual domain. If there's a way to keep this set to the actual domain but still have it work while testing on localhost please let me know.
In API Gateway you need to set the CORS values as in the screenshot below to ensure the preflight (OPTINOS) call is made correctly as well.
The key is to have both the request and response headers configured correctly. This is harder to do when using something like AWS but much easier with something like express.js where you can use the middleware.

How to attach response header in VTL template

My application is in serverless framework and I am using vtl template as a lambda resolver. My app stack is AppSync, Lambda on Node JS runtime, Serverless framework and vtl templates.
I am trying to figure how I can add custom response headers from my lambda to client and really appreciate any input on the same. Please find my code below so far:
Lambda
const securityHeaders = {
"content-type": "application/json; charset=utf-8",
"x-content-type-options": "nosniff",
"cache-control": "no-store, no-cache, must-revalidate, proxy-revalidate",
};
callback(null, {
statusCode: 200,
headers: securityHeaders,
body: JSON.stringify({
data,
})
});
return;
serverless yml
functions:
getData:
handler: src/handler.getData
events:
- http:
path: getData
method: post
custom:
configValidationMode: off
appSync:
schema: ['graphql-schemas/data.graphql']
authenticationType: AMAZON_COGNITO_USER_POOLS
mappingTemplates:
- dataSource: GetData
type: Query
field: getData
request: "data-request.vtl"
response: "data-response.vtl"
data-response.vtl
## return the body
#if($ctx.result.statusCode == 200)
##if response is 200
$ctx.result.body
#else
##if response is not 200, append the response to error block.
$utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
The above code giving me the result in the postman but I am not able to see my custom headers in the response section. I think I am missing on how to include headers in the response vtl.
It's late but still I also need to add same response headers in Every GraphQL operation. Please refer https://aws.amazon.com/about-aws/whats-new/2022/02/aws-appsync-support-custom-response-headers/ and https://docs.amazonaws.cn/appsync/latest/devguide/http-helpers-in-utils-http.html.
I created VTL template and attached to each resolver in responseMappingTemplate. It works for me.

API Gateway, blocked by CORS policy: No 'Access-Control-Allow-Origin' header

I know this question might be duplicated, but none of the existing question point to anything I'm not doing...
I've deployed an API using the serverless framework, but I'm having trouble with CORS.
I'm doing a get request using axios:
axios.get('https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z')
.then(response => {
this.data = response.data;
})
.catch(error => console.log(error))
And I'm getting the following error:
Access to XMLHttpRequest at 'https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I've already done:
Made sure there's an OPTIONS method in API Gateway with a method response that looks like this:
Made sure I deployed those changes.
Also, the response of my Lambda function is returning the following headers:
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "http://localhost:8080",
"Access-Control-Allow-Credentials": "true",
},
Body: string(jsonEvents),
}, nil
I also tried setting Access-Control-Allow-Origin to '*'
My serverless.yml file has cors: true on each of the function events:
functions:
deploymentFrequency:
handler: bin/update/deployment-frequency
events:
- http:
path: deployment-frequency
method: post
cors: true
fetchDeploymentFrequency:
handler: bin/fetch/deployment-frequency
events:
- http:
path: deployment-frequency
method: get
cors: true
What am I missing? Nothing seems to work. The request works fine from Postman and it looks to be including the headers, so this seems to be an issue with the OPTIONS method.
My configuration is:
(event, context, callback) => {
callback(null, {
statusCode: (code || 200),
body: JSON.stringify(resp),
headers: { 'Access-Control-Allow-Origin': '*'},
});
}
and it works fine for me. I use to have the same issue as you before, but as long as you define your function with CORS: true and your response contains the header, you should be fine.
Note: Im didnt understand the sintax "map[string]string" and credentials should not be necessary at this case.
It turns out I was ignoring the status code from the response :(
I realized I was actually getting two errors:
A 406 status code for a missing Content-Type header
The CORS error
The first error was caused because I wasn't passing the Content-Type header to the request (I had a check in my code I completely forget that expects that header).
The second error was caused because I didn't add the Access-Control-Allow-Origin header to the error response of my function.
Enable Lamba proxy integration
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
},
Body: string(jsonEvents),
}, nil
In your terminal, go to the root project path and run:
npm i cors
And, after you need put this code in your index.js:
const cors = require("cors");
app.use(cors());

CORS issues with Serverless Lambda and API Gateway

Solved
The below issue was simply caused by the body property of the response object constructed in my Lambda. I was forgetting to stringify the data, returning body: data instead of body: JSON.stringify(data). This problem with the response appeared to trigger an error with API Gateway which caused the request failures with some rather confusing error messages.
Problem
I'm working on a ecommerce site using React, Serverless and the Stripe API. My front-end React app is making a GET request using Axios to my Lambda function which has been exposed via API Gateway. The Lambda function in turn queries the Stripe API and returns the Stripe response data to my React app. However, I am experiencing CORS issues as my React app tries to call the Lambda, it receives the following error:
Failed to load: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 502.
Querying the Lambda endpoint in Insomnia returns a 502 response with { "message": "internal server error" }. But executing the serverless invoke command from the Lambda function from the terminal successfully returns the Stripe data.
I am enabling cors for the function in my serverless.yml file and including 'Access-Control-Allow-Origin': '*' in my Lambda code response as advised in this Serverless blog post, I have also attempted adding a various combinations of the following headers to my Lambda response and my Axios request based on suggestions found on this issue on Axios and this issue on Serverless. I've deleted and redeployed the service multiple times and
Lambda response headers
headers: {
'Access-Control-Expose-Headers': 'Access-Control-Allow-Origin',
'Access-Control-Allow-Credentials': true,
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
React/Axios config
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
crossDomain: true
}
Currently my code is as follows:
React app
import Axios from 'axios';
import { GET_PRODUCTS_URL } from '../config';
export default () => {
return Axios
.get(GET_PRODUCT_URL, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
crossDomain: true
})
.then(({ data }) => data)
.catch(console.log);
}
Lambda
import Stripe from 'stripe';
const apiKey = process.env.STRIPE_SECRET_KEY;
const stripe = Stripe(apiKey);
module.exports.handler = (event, context, callback) => {
return stripe.products
.list()
.then(({ data }) => {
const response = {
statusCode: 200,
headers: {
'Access-Control-Expose-Headers': 'Access-Control-Allow-Origin',
'Access-Control-Allow-Credentials': true,
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: data,
};
callback(null, response);
})
.catch(console.log);
}
Serverless.yml
service: srd
provider:
name: aws
runtime: nodejs6.10
stage: ${opt:stage, self:custom.defaultStage}
region: eu-west-1
memorySize: 256
timeout: 6
versionFunctions: true
plugins:
- serverless-plugin-optimize
- serverless-plugin-scripts
package:
individually: true
functions:
get-products:
handler: lambda/get-products.handler
name: srd-get-products-${self:provider.stage}
description: 'get srd products from stripe'
environment:
STRIPE_PUBLISHABLE_KEY: ${self:custom.stripe_publishable_key.${self:provider.stage}}
STRIPE_SECRET_KEY: ${self:custom.stripe_secret_key.${self:provider.stage}}
events:
- http:
path: products
method: get
cors: true
custom:
defaultStage: dev
stripe_publishable_key:
dev: ${file(env.yml):STRIPE_DEV_PUBLISHABLE_KEY}
live: ${file(env.yml):STRIPE_LIVE_PUBLISHABLE_KEY}
stripe_secret_key:
dev: ${file(env.yml):STRIPE_DEV_SECRET_KEY}
live: ${file(env.yml):STRIPE_LIVE_SECRET_KEY}
I've been at this for hours, any insights much appreciated.
Edit/Additional
Making an options request from the CLI returns the following:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
Date: Mon, 19 Mar 2018 06:52:12 GMT
x-amzn-RequestId: 0d10c9d1-2b42-11e8-b270-a723e367048e
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent
Access-Control-Allow-Methods: OPTIONS,GET
Access-Control-Allow-Credentials: false
X-Cache: Miss from cloudfront
Via: 1.1 e40b14deb4a844594f746b751f632677.cloudfront.net (CloudFront)
X-Amz-Cf-Id: eMvu3Ke7m7GNFCFgIOGVJmoObFwYMeEt4o8AByoipfMn1nXIi9Vo0g==
The HTTP response code in the CORS error message says 502. This means the server you are requesting is unavailable.
In an event of a 502, the browser cannot make successful OPTIONS requests, so even if your CORS setup in the server is correct, you will still get a CORS error since it was not resolved properly.
Look at your server and make sure it is running as it should. Once the 502 error is fixed, try again and you should be good to go. Try making a manual OPTIONS request, similar to that of what the browser would make, and see if you get the same error.

Resources