I don't know how to start, I'm struggling with CORS errors when using the API gateway with Cognito as an authorizer. I have spent a lot of time on it and I believe I have read the whole internet to figure out what is going on and wrong.
So let's start with a simple example. My lambda:
import json
def handler(event, context):
print('Lambda is here')
return dict(
statusCode=200,
headers={
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Credentials': 'true',
},
body=json.dumps({'message': 'lambda works'})
)
HTML code for testing CORS and authorization:
<head>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<script>
axios.get(
"api_gateway_url",
)
.then(response =>alert(response.data.message))
.catch(error => {
console.log(error)
alert("ERROR")
})
</script>
</body>
For API gateway without authentication, the response is:
For API gateway with authentication, the response is:
The response code depends on the configuration of the API gateway. I have changed everything that I can many many times, I was testing it setting by setting and nothing helps me. I have added CORS headers in places like Method Response, Integration Response, and Gateway Responses, under Enable CORS configuration. Depending on the configuration I have a few status codes 401, 403, and 500 and always CORS error occurs.
While testing the requests haven't touched the lambda, so the above errors weren't the results of lambda execution.
I have tested it by deploying it with CDK I created an example code you can also try in your local environment. The link to the code https://gitlab.com/magnus250/cognito-api-gateway-cdk-problems/
Okay, I have finally solved my problem. #sampath-dilhan #chris-smith thank You. So let me describe how I achieved it.
I think the whole API gateway is bugged and you never know what exactly you have configured. The base development of the API gateway was provided by CDK and then I try different configurations to enable CORS from the console. I read the whole internet about CORS configuration and I tried to use this knowledge to enable CORS, always re-deploying API and waiting for some time until testing.
Below I put the CDK configuration, that works for me. For people who struggle with problems, I recommend always destroying your stack and re-deploy it in a clean environment, because as I already mentioned I believe that API Gateway is bugged. The code also has domain and certificate definitions.
def create_api_gateway(
self,
prefix: str,
domain: str,
certificate_arn: str,
cognito_user_pool_arn: str,
handler: _lambda.Function,
) -> None:
base_api = apigateway.RestApi(
self,
f"{prefix}-authorized-rest-api",
deploy_options=apigateway.StageOptions(
throttling_burst_limit=100,
throttling_rate_limit=100,
caching_enabled=False,
cache_cluster_enabled=False,
),
default_cors_preflight_options=apigateway.CorsOptions(
allow_headers=[
"Content-Type",
"X-Amz-Date",
"Authorization",
"X-Api-Key",
],
allow_methods=["*"],
allow_credentials=True,
allow_origins=["*"],
),
domain_name=apigateway.DomainNameOptions(
certificate=certificatemanager.Certificate.from_certificate_arn(
self,
f"{prefix}-certificate",
certificate_arn=certificate_arn,
),
domain_name=f"api.{domain}",
endpoint_type=apigateway.EndpointType.EDGE,
),
)
response_types = [
apigateway.ResponseType.DEFAULT_4_XX,
apigateway.ResponseType.DEFAULT_5_XX,
]
for response_type in response_types:
base_api.add_gateway_response(
f"{prefix}-api-gateway-{response_type.response_type}",
type=response_type,
response_headers={
"Access-Control-Allow-Origin": "'*'",
"Access-Control-Allow-Headers": "'*'",
"Access-Control-Allow-Methods": "'*'",
"Access-Control-Allow-Credentials": "'true'",
},
)
user_pool = cognito.UserPool.from_user_pool_arn(
self, f"{prefix}-user-pool", user_pool_arn=cognito_user_pool_arn
)
authorizer = apigateway.CognitoUserPoolsAuthorizer(
self,
f"{prefix}-authorizer",
authorizer_name=f"{prefix}-authorizer",
cognito_user_pools=[user_pool],
)
get_widgets_integration = apigateway.LambdaIntegration(handler)
resource = base_api.root.add_resource("{proxy+}")
resource.add_method(
"ANY",
get_widgets_integration,
authorizer=authorizer,
)
cdk.CfnOutput(self, f"{prefix}-api-url", value=base_api.url)
zone = route53.HostedZone.from_lookup(self, f"{prefix}-hosted-zone", domain_name=domain)
route53.ARecord(
self,
f"{prefix}-dns-record",
record_name="api",
zone=zone,
target=route53.RecordTarget.from_alias(route53_targets.ApiGateway(base_api)),
)
Related
I've got an API Gateway in front of a Lambda.
Successful responses from the Lambda (HTTP 2xx) have the response body forwarded.
However, for error responses (HTTP 5xx and others), the API Gateway transforms the response body using response templates.
Is there a way to avoid this? To have the original error response body from the Lambda?
In my Lambda I have this:
return callback(generalError, {
statusCode: 500,
headers:{
"content-type": "application/json"
},
body: JSON.stringify({
error: 'INTERNAL_ERROR',
description: error.message,
})
});
However, as output from the Gateway I get this:
{ "error": "Internal server error" }
Which doesn't match. The Lambdas response. It does match the response template in API Gateway:
{"message":$context.error.messageString}
However, is there a way to just proxy the original Lambda response instead of having this transformation in place?
I've found the reason why it doesn't work.
If you set a callback to a 500 error, including an object with an error field, somehow this will become the full response, regardless of the other output or real error.
Avoiding using an error field does the trick! I renamed it to serviceError and now I'm getting the responses I expected.
I'm currently attempting to create a mock integration using the python AWS CDK for the purpose of running integration tests in an app.
This app needs to query an external API to validate a payload. The endpoint expects a Content-Type application/vnd.nasa.cmr.umm+json;version=1.6.3. For now, I would like the MockIntegration to always return a 200 status code with an empty response.
This is what I tried:
from aws_cdk import aws_apigateway
cmr_api = aws_apigateway.RestApi(
self, f"integration-test-cmr-api-try",
)
aws_apigateway.Resource(
self, "mock-cmr-validation", parent=cmr_api.root, path_part="ingest"
).add_resource(
"validate"
).add_method(
http_method="POST",
integration=aws_apigateway.MockIntegration(
request_templates={
"application/vnd.nasa.cmr.umm+json;version=1.6.3": json.dumps({"statusCode": 200})},
integration_responses=[
{
"statusCode": "200",
"responseTemplates": {"application/json": json.dumps({})},
}
],
),
method_responses=[
{
"statusCode": "200",
"responseModels": {
"application/json": aws_apigateway.Model.EMPTY_MODEL
},
}
],
)
Unfortunately a request to the generated gateway returns:
curl --request POST 'https://trfjg7ckha.execute-api.us-east-1.amazonaws.com/prod/ingest/validate' \
--header 'Content-Type: application/vnd.nasa.cmr.umm+json;version=1.6.3' \
--data-raw '{}'
Status code 500, {"message": "Internal server error"}.
Setting only application/json (or even application/vnd.nasa.cmr.umm+json) as the key in MockIntegration.request_templates, and using that as the Content-Type in the request, returns the intended result (status code 200, empty response). This makes me wonder if the issue can potentially be in setting the ;version=1.6.3 with the Content-Type?
Any help would be greatly appreciated!
After some further experimentation, I was able to get pass this issue by using application/vnd.nasa.cmr.umm+json in the request_template (without the version). When that is the request_template content type, any application/vnd.nasa.cmr.umm+json;version=XXX matches and the correct mock integration is triggered!
I am facing this error XMLHttpRequest error. while making an HTTP post call to my API-AWS API Gateway. My current Flow is Flutter web -> API gateway -> lambda -> rds.
I know there are already a couple of question-related to this like but as suggested in one of the answers to add some headers in response to lambda. but it doesn't work for me.
After doing some research I found out that the problem is regarding to CORS. now disabling cors in chrome is a temporary fix and suggested in this question.
some other solution that I found after researching suggested to enabled cors in my API and also in the frontend part I have added headers but none of them works.
fetchData() async {
String url =
"myUrl";
Map<String, String> headers = {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
};
String json = '{"emailId":"emailId"}';
http.Response response =
await http.post(Uri.parse(url), headers: headers, body: json);
print(response.body);
return response.body;
}
what is the correct way of solving this problem?
1- Go to flutter\bin\cache and remove a file named: flutter_tools.stamp
2- Go to flutter\packages\flutter_tools\lib\src\web and open the file chrome.dart.
3- Find '--disable-extensions'
4- Add '--disable-web-security'
I have Solved my problem, and not going to delete this question because there aren't many well-defined solutions to this problem.
For Future viewer who is using flutter web and AWS API-gateway.
if you encounter this problem it means its from backend side not from flutter side
XMLHttpRequest error. is caused due to CORS
The solution to the problem you have to enable CORS in api-gateway follow this link.
but if you are using proxy integration with lambda and api-gateway then in that case enabling CORS doesn't going to help, you have to pass on headers from the response of lambda function. like
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": true, // Required for cookies, authorization headers with HTTPS
"Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale",
"Access-Control-Allow-Methods": "POST, OPTIONS"
},
body: JSON.stringify(item)
};
the format needs to be the same. also, one particular question that helps me a lot to understand this whole issue is going through the various answer of the question link.
Now comes my problem, what I'm doing wrong i that i am passing "Access-Control-Allow-Origin": "*", from frontend and enabling CORS in API gateway also send similar headers which are creating a problem for me
Access to XMLHttpRequest at 'API-URL' from origin 'http://localhost:63773' 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. //this particular line
So after changing my function to this everything works perfectly fine
fetchData() async {
String url =
"API-url";
Map<String, String> headers = {
"Content-Type": "text/plain",
};
String json = '{"emailId":"emailId"}';
Map<String, String> map = Map();
map["emailId"] = "fake#gmail.com";
http.Response response = await http
.post(Uri.parse(url), headers: headers, body: jsonEncode(map))
.then((value) {
print("onThen> " + value.body.toString());
}).onError((error, stackTrace) {
print("onError> " +
error.toString() +
" stackTrace> " +
stackTrace.toString());
});
}
In flutter web api Access-Control-Allow-Origin use in header to might resolve this issue.
header("Access-Control-Allow-Origin: header");
in your backend php file add this code
<?php
header("Access-Control-Allow-Origin: *");
finish!
I'm uploading an image to s3, through a lambda, and everything works well, with no errors, but the response from API Gateway is 500 Internal Server Error.
I configured my api-gateway following this tutorial: Binary Support for API Integrations with Amazon API Gateway.
My lambda receives the base64Image, decode it and successfully upload to s3.
This is my lambda code:
def upload_image(event, context):
s3 = boto3.client('s3')
b64_image = event['base64Image']
image = base64.b64decode(b64_image)
try:
with io.BytesIO(image) as buffer_image:
buffer_image.seek(0)
s3.upload_fileobj(buffer_image, 'MY-BUCKET', 'image')
return {'status': True}
except ClientError as e:
return {'status': False, 'error': repr(e)}
This is what i'm receiving:
{
"message": "Internal server error"
}, with a 500 status code.
Obs: I'm not using lambda proxy integration.
You need to return a header in the response, e.g. in Python:
return {
"statusCode": 200,
'headers': { 'Content-Type': 'application/json' },
"body": json.dumps(body)
}
That example looks like it falls short on mapping the responses section in favor of a pass through. In which case changing your return to: return {'status': True, 'statusCode': 200} might work.
Generally speaking there are two paths when building a response with ApiGateway-Lambda. One is the lambda-proxy (where your lambda function defines the response), the other is where ApiGateway transforms your responses and generates the appropriate headers/status based on a mapping.
The path from the example is for the latter.
Personally I would change:
return {'status': True}
to return {'status': "Success"} And create a regex that looks for the word "Success" and "Error" respectively.
I have used this blog post successfully with this technique (it also describes at length the differences between the two approaches). Once you get one mapping working you could adjust it as is more appropriate for your implementation.
EDIT: hot tip these decorators are awesome and make python & lambda even cleaner/easier but mostly for the proxy setup
I'm trying to trigger Email using SES and API gateway without using Lambda function. It will contain pdf file and text. I'm able to trigger Email using QueryString parameters, but as i need to send files also I want to map from the body. Tried body mapping in API-gateway as below,
BODY MAPPING TEMPLATE
{
'Destination': {
ToAddresses:'xxxx#example.com'
},
'Message': {
'Body': {
'Text': {
'Data': 'message body ! hurray !!!',
'Charset': 'UTF-8'
}
},
'Subject': {
'Data': 'subject data',
'Charset': 'UTF-8'
}
},
'Source': 'yyy#example.com'
}
RESPONSE FROM SES
`{
"Output": {
"__type": "com.amazon.coral.service#SerializationException",
"Message": null
},
"Version": "1.0"
}`
Questions
Is it possible to achieve this without using lambda?
Am I using the body mapping correctly?
Could anyone please throw light on how to achieve this? Any help highly appreciated.
I got stuck on the same problem and finally made it. I'll share what I learned.
According to this document, request body should be in form of x-www-form-urlencoded, such as:
Action=SendRawEmail
&Destinations.member.1=allan%40example.com
&RawMessage.Data=RnJvbTp1c2VyQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQoNCk1lc3 ...
Also you have to set Content-Type:application/x-www-form-urlencoded header. If not, it causes exception. With this header, you can't send request with query parameter(since it includes ?) , you also have to set "Action Type" to "Use path override" and "/" in Integration Request section of API gateway console.
I also confirmed that I could send email with attachments using SendRawEmail action.
So my answer to original questions:
Is it possible to achieve this without using lambda? => Yes!
Am I using the body mapping correctly? => No, you have to use x-www-form-urelencoded request body.
Hope this will help.