I am trying to protect a lambda function using a custom authorizer (which validates a jwt) on AWS API Gateway. I also want to pass any queryStringParameters into my lambda function, so I want to keep my Integration request to be Lambda Proxy.
The custom authorizer is set, tested and working
Problem:
When I call this endpoint (after deploying), I receive a HTTP 500 response.
{
"message": "Internal server error"
}
When I remove my custom authorizer OR deselect the lambda Proxy integration, I receive a normal output from my lambda
def lambda_handler(event, context):
return {
'status': 400,
'body' : json.dumps('hello World'),
'headers': {
'Access-Control-Allow-Origin': "*"
},
'isBase64Encoded': 'false'
}
Has anyone come across this? AWS documentation on this issue is sparse :/
Thank you!
I'm hitting myself in the head after this. The reason is because of what my lambda function was returning. It should've been
def lambda_handler(event, context):
return {
'statusCode': 400,
...
As opposed to
def lambda_handler(event, context):
return {
'status': 400,
...
If the AWS docs were better/i wasn't as obtuse :D
Related
I have an API Gateway that i'm trying to add JWT authorization to. I have setup the authorization lambda based on this AWS resource. I have tested the lambda direct and also through the 'test' button and both generate the proper response policy document just fine.
But when i test through the endpoint with postman i always get 401
{
"message": "Unauthorized"
}
I turned on CloudWatch logging and also turned on X-Ray Tracing and the request is never logged at all. I see the logs for the test invocations and i see other logs for other non-authorized resources in CloudWatch and X-Ray.
If i disable the authorizer and deploy the request goes through just fine.
What gives?
Here is my authorizer code.
export async function handler(event) {
console.log(event);
let response = generatePolicy(null, 'Deny', event.methodArn);
let token = event.authorizationToken;
if (token) {
const jwt = await validateJwtSignature(token.substring(7)); // Strip "Bearer " from the begning of the token..
if (jwt) {
response = generatePolicy(jwt, 'Allow', event.methodArn);
}
}
console.log(JSON.stringify(response));
return response;
};
// Help function to generate an IAM policy
// Ex. generatePolicy('user', 'Deny', event.methodArn);
// Ex. generatePolicy('user', 'Allow', event.methodArn);
function generatePolicy(jwt, effect, resource) {
const authResponse = {
principalId: (jwt || {}).sub
};
if (effect && resource) {
authResponse.policyDocument = {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: effect,
Resource: resource
}
]
};
}
return authResponse;
}
Turns out the AWS guide that is was using is not clear on step # 16 here where it says to use authorizationToken as the Token source.
This is wrong. It should be Authorization as that is the right header. The guide made me think that the Token Source and the property passed to the lambda were the same thing.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
This is a working authorizer. Rather annoying that neither CloudWatch nor x-ray logging indicated that the request was rejected for that reason.
I have an application written in React JS, running on localhost, which makes API calls to API Gateway in AWS. API Gateway forwards requests to a lambda function, which returns a response. I have enabled CORS on AWS side.
At the moment whenever I click a 'request' button in the application I get a response from the gateway. Here is my current python code:
import json
def lambda_handler(event, context):
response = {}
response['result'] = "Success"
response['message'] = "Updated successfully!"
return {
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
"body": json.dumps(response)
}
And here's the body of the request:
{
"ID": "1101",
"RequestDate": "2021-02-28"
}
This works fine. I get the 'message' value from this response and can display it without problems.
Next I want to display the information containing some data coming from the request. For example instead of Updated successfully I would like to get the RequestDate from the request and return Updated successfully on 2021-02-28.
I added these two lines:
def lambda_handler(event, context):
body = json.loads(event['body'])
request_date = body['RequestDate']
response = {}
response['result'] = "Success"
response['message'] = "Updated successfully!"
return {
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
"body": json.dumps(response)
}
As soon as I make this change I get the following code in my application:
Access to fetch at url from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This only happens when I add request_date = body['RequestDate']. I tried returning the body only and it was working fine as well.
In my react js application I add following headers as well:
async callAPI(url, method, data) {
let result = await fetch(url, {
method: method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
data
})
})
return result.json().then(body => this.notify(body['message']));
}
I tried enabling CORS and deploying the resource again but to no avail. I have added Access-Control-Allow-Origin to allowed headers in AWS. As I mentioned it works fine with post method prior to adding that one line. What could be wrong here and how can I remedy it?
EDIT:
One more thing. I get this error only from my application running on localhost. Curling or using any REST client works fine.
EDIT2:
Added fetch code
Setting up CORS in Lambda is dependent on how you setup API Gateway. API Gateway has several modes [REST, HTTP, WebSocket]. In the case of REST, API Gateway does some pre-processing on the incoming request like parameter validation before passing to Lambda. HTTP Proxy is just that, a pass through to Lambda, and Websockets is not really for this discussion.
I am assuming it is because you are using API Gateway in standard configuration and you have not enabled CORS on API Gateway. The code you provided above will work for API Gateway Configured for HTTP.
If you are using the CDK, or CloudFormation then you must configure CORS there, else the easiest is to use the console.
Go to your API in the AWS Console, select resources, select your method or service, from the actions menu enable CORS. And then publish your updated API.
Here is a link to the AWS documentation that outlines how to do it.
Some advice, when testing endpoints via a browser it is best not to use Localhost or 127.0.0.1 it has unintended consequences. Edit your hosts file and give yourself a domain name, and use that domain name instead.
CORS is there for a reason to prevent Cross Site Scripting or Cross Site Origin Attacks. Try not to use *, testing sure, but production no. If you have code say on S3, and your REST services on say API Gateway you can front both with CloudFront using the same Origin, and let CloudFront know to forward based on URL (e.g /API) to APIGateway and the other to S3. Thus everything has the same domain.
This is what I have on my server and it works fine. Using fetch on the front-end.
Server Code:
import json
result = {"hello": "world"}
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*"
},
'body': json.dumps(result)
}
Front-end Code using ES6
url = "Your url"
path = "Your desired path"
data = {"hello": "world"}
const response = await fetch(url + path, {
method: "POST",
cache: "no-cache",
mode: "cors",
body: JSON.stringify(data),
});
Add statusCode inside response from lambda
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
'body': json.dumps(response)
}
In case there is any error in your lambda it will return default 5XX response without any CORS headers and in such cases browser will complain for cors headers not found.
You can add your current code in try block and in except block you can print the error and return some default response like below
return {
'statusCode': 500,
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
'body': json.dumps({'message':'Unexpected error'})
}
Add some log statements in your lambda and check api gateway configuration for Lambda proxy setting.
Make sure you check your api gateway logs and lambda function logs for some more details regarding error.
I am trying to achieve redirection from cloudfront to API gateway based on the path.
I have my UI with a cloudfront distribution with source being S3 bucket hosted on for eg.
www.example.com
and it serves the requests.
I want the URLs with the pattern
www.example.com/share/*
to be redirected to API Gateway. The API Gateway has redirection internally where it points to other URLs.
If I use the API gateway endpoint directly in the browser, it fetches the expected result.
I am unsure of how to redirect from cloudfront to API Gateway. I have put cloudwatch logs on API Gateway and can see that cloudfront to API Gateway Redirection isn't working.
I have tried adding API Gateway as origin and add the same as a behaviour in cloudfront, but no success.
You can do redirect by using Lambda#Edge in CloudFront. AWS provides an example redirect function here.
Example below
'use strict';
exports.handler = (event, context, callback) => {
/*
* Generate HTTP redirect response with 302 status code and Location header.
*/
const response = {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: 'http://URL/PATH',
}],
},
};
callback(null, response);
};
However it seems like perhaps rather than doing a redirect you should consider using a different a secondary origin in your CloudFront distribution to serve the API Gateway path if it is part of your application.
To do this you would need to add a custom domain to your API Gateway with the domain name of your site and then within CloudFront add an origin with a matching pattern of /share to forward to your API Gateway.
Take a read of this page for more information.
One way would be to use lambda#edge for a viewer request to return 302 HTTP code to the client with redirection url.
Generating an HTTP Redirect (Generated Response)
In python, such a lambda could look like (from docs):
def lambda_handler(event, context):
# logic to check the url and generate new url.
response = {
'status': '302',
'statusDescription': 'Found',
'headers': {
'location': [{
'key': 'Location',
'value': '<your-api-gateway-url>'
}]
}
}
return response
I have a lambda (LAMBDA_PROXY) function with API gateway which is implemented in Golang (go-chi router).
when I invoke lambda from postman or browser, it gives 502 Bad Gateway status. In CloudWatch, I found following error message
Error while generating proxy response: Status code not set on response: errorString null
You need to respond with the correct response when using the "Use Lambda as Proxy" checkbox. Check out this document.
This may be more appropriate
The error you are getting is because, when you're using the API Gateway + Lambda proxy integration, the return value from the lambda function must be in the following JSON format:
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
Since you are using Golang, you can edit the return value of your main.go function by adding the following snippet as follows :
return events.APIGatewayProxyResponse{
Body: string(body),
StatusCode: 200
}, nil
Hope this helps!
I am new to AWS, I am getting {"message": "Internal server error"} while running the Lambda function with API Gateway in Postman.
I have checked the CloudWatchLogs, there is no error showing in the logs. But Postman returning {"message": "Internal server error"} this error.
It happens when you don't return the correct API Gateway format.
Try to return this in your Lambda:
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": "{'Test': 'Test'}",
"headers": {
'Content-Type': 'text/html',
}
}
According to the log message you provide, it looks like the log from your Lambda function. I recommend you to enable logging feature on API Gateway side or you can use the test invoke feature on API Gateway console. They both are able to help you to debug your API.
Here is the common issues which might be able to help you diagnose the issue.
1. It doesn't have a right permission to allow API Gateway invoke your Lambda function.
2. It is set as AWS Service Proxy to your Lambda function, the response from your Lambda function doesn't return the response in the proper format.
Ref: https://forums.aws.amazon.com/thread.jspa?messageID=916452
The error can be caused by incorrect quotation marks.
A. Examples that produce the same error in Postman tests:
1. def lambda_handler(event, context):
return {
'statusCode': 200,
"body": {"one": 1000}
}
2. def lambda_handler(event, context):
return {
'statusCode': 200,
"body": "{"one": 1000}"
}
B. Examples that do not produce the error:
3. def lambda_handler(event, context):
return {
'statusCode': 200,
"body": "{'one': 1000}"
}
4. def lambda_handler(event, context):
return {
'statusCode': 200,
"body": '{"one": 1000}'
}
So, the type of quotation marks used after "body": is the reason for the error in this case. Note that while the Amazon lambda console does not produce an error for example 1., Postman says { "message": "Internal server error" }
Resolved it by adding isBase64EncodedFlag to my lambda response
results = {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(res),
"isBase64Encoded": False
}