AWS Enabling CORS for my API triggering a Lambda - amazon-web-services

I managed to create an AWS Lambda that does two things: writes on a dynamo DB and sends an SMS to a mobile number. Then I call this Lambda through a API Gateway POST call, and it works great from the Test section on AWS console but it gives error both on Postman and my own website. I inserted a callback to handle CORS on Lambda and deployed + enabled CORS on my API via console and deployed it but still get errors:
Errors via postman call: {
"message": "Internal server error"
}
Errors via my website (jquery ajax POST call): Lambda calling failed: {"readyState":4,"responseText":"{"message": "Internal server error"}","responseJSON":{"message":"Internal server error"},"status":500,"statusText":"error"}
This is my lambda code
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
const SNS = new AWS.SNS();
const tableName = "#####";
let params = {
PhoneNumber: 'mynumber####',
Message: 'Someone wrote!'
};
exports.handler = (event, context, callback) => {
dynamodb.putItem({
"TableName": tableName,
"Item" : {
"Id": {
N: event.Id
},
"Type": {
S: event.Type
}
}
}, function(err, data) {
if (err) {
console.log('Error putting item into dynamodb failed: '+err);
}
else {
console.log('Success in writing, now starting to send SMS');
return new Promise((resolve, reject) => {
SNS.publish(params, function(err, data) {
if(err) {
console.log("Error in sending sms alarm");
reject(err);
}
else {
console.log("SMS alarm sent!");
resolve(data);
}
})
})
}
});
callback(null, {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
});
};
What am I doing wrong? I don't think permissions on lambda are the problem here, since testing it on console works both on writing on dynamo both sending the sms to my cellphone.
If I click from here on API Endpoint I get the same error {"message": "Internal server error"}

SOLUTION: instead of making an AWS HTTP API, make a AWS REST API, that is much more complex and offers more personalization for CORS, letting you set them and headers.

Related

Publishing message from Lambda function to AWS IoT is not working with aws-sdk

I'm trying to publish a message from a lambda function to my AWS IoT Thing, by using the aws-sdk which should use HTTP by default instead of MQTT to publish a message.
Then the Function gets invoked no error message is logged to cloudwatch unfortunately which makes it even harder to debug.
Steps I took:
created a Policy for the message
created a small React frontend to read messages and test it with IoT test client.
wrote lambda and passed the Thing url as env var like this: "https://xxxxxdi.eu-central-1.amazonaws.com"
The Lambda function has also a attached Policy which should grant it to write to AWS IoT.
{
"arn": null,
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "iot:Publish",
"Resource": "arn:aws:iot:eu-central-1:*:*:topic/PairCreated",
"Effect": "Allow"
}
]
},
"id": null,
"name": "PublishMessage",
"type": "inline"
}
Here is the code of the lambda function. The credentials for the object of type IotData are looking ok
import wrap from '#dazn/lambda-powertools-pattern-basic'
import Log from '#dazn/lambda-powertools-logger'
import { IotData } from 'aws-sdk'
export const handler = wrap(async (event: any = {}, context: any) => {
Log.info('event', event)
const iotData = new IotData({endpoint: process.env.IOT_ENDPOINT, region: 'eu-central-1', logger: new Log})
const params: IotData.PublishRequest = {
topic: process.env.IOT_THING_NAME || '',
payload: JSON.stringify(event),
qos: 0,
retain: false
}
Log.debug('params', { params })
iotData.publish(params, (err, res) => {
if (err) {
Log.error("Error publishing to IOT", { err })
return context.fail(err)
};
Log.info('res', res)
return context.succeed();
});
})
The version I'm using: "aws-sdk": "^2.1053.0"
What am I missing?
I found out why the message was not sent.
The callback inside of iotData.publish was not executed correctly because the lambda function was terminated before it could be executed. Here are my changes to the code.
await iotData.publish(params, (err, res) => {
if (err) {
Log.error("Error publishing to IOT", { err })
return context.fail(err)
};
return context.succeed(res);
}).promise()
This returns a Promise we can await so the callback gets executed

AWS api gateway working in browser but returning 502 in fiddler

I have a lambda function which would basically authenticate against the password stored in aws secret manager. The secret manager path would be the username and it will have the value for password. password will need to be passed in the header and username in the query. When I access the url https://{myawsurl}.execute-api.{region}.amazonaws.com/demo/{username} in a browser, I get the error password is missing in the header(which is expected). When I hit the url using fiddler I get 502 all the time.
My api gateway is simply a GET to the lambda function below:
const aws = require("aws-sdk");
const sm = new aws.SecretsManager({ region: 'us-east-1' })
const getSecrets = async (SecretId) => {
return await new Promise((resolve, reject) => {
sm.getSecretValue({ SecretId }, (err, result) => {
if (err) {
reject(err);
}
else {
resolve(JSON.parse(result.SecretString));
}
});
});
}
const main = async (event) => {
console.log("Event: ", event);
try {
const username = event.queryStringParameters ? event.queryStringParameters.username : (event.pathParameters ? event.pathParameters.username : null);
if (username === null || username === undefined || username.trim().length === 0) {
if (username === null || username === undefined || username.trim().length === 0) {
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: "username is missing in the url. Please add `/?username={username}` or `/{username}` in the url"
};
}
}
const password = event.headers ? event.headers.password : null;
if (password === null || password === undefined || password.trim().length === 0) {
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: "password is missing in the header"
};
}
const secrets = await getSecrets(username);
if (password !== secrets.password) {
return {
statusCode: 403,
headers: {
"Content-Type": "application/json"
},
body: "Incorrect username/password"
};
}
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: "User is Authenticated"
};
} catch (e) {
return {
statusCode: 404,
headers: {
"Content-Type": "application/json"
},
body: e.message
};
}
}
exports.handler = main;
My fiddler request is below:
GET https://{myawsurl}.execute-api.{region}.amazonaws.com/demo/{username} HTTP/1.1
password: MyTestPassword
I saw other posts where they mentioned about having a statusCode and body being a string. I have those but still getting error...
I added/removed the headers: { "Content-Type": "application/json"}, from the response and it made no difference..
EDIT: One another thing noticed is whenever I access the api gateway url via browser, it gets logged in my api's log group. But when it is accessed using fiddler it doesn't log. Not sure why...
EDIT: After the suggestion from #ArunK, I used Postman and I found it returns the expected response from the api gateway. I assume some settings in Fiddler may be causing this to happen..
Looks like the issue related to the TLS version supported by Fiddler. You need to include tls 1.0 and 1.2 since AWS API Gateway support these TLS Versions.
From the docs:
A security policy is a predefined combination of minimum TLS version
and cipher suite offered by Amazon API Gateway. You can choose either
a TLS version 1.2 or TLS version 1.0 security policy.
Go to Tools -> Options -> Https and verify the following exists under Protocols - <client>;ssl3;tls1.0;tls1.1;tls1.2
More about Fiddler and Modern TLS Versions.

Sending http request from aws lambda to google firebse funcitons

I have set up firebase functions to receive http requests and have verified that the same is working. Now im trying to send http request to firebase from aws lambda function. But there is no response either in aws lambda or in the firebase functions log. This is my aws lambda code:
const postData = JSON.stringify({
"queryresult" : {
"parameters": {
"on": "1",
"device": "1",
"off": ""
}
}
});
const options = {
hostname: 'https://<the firebase function endpoint>',
port: 443,
path: '',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, postData)
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
// Write data to request body
req.write(postData);
req.end();
}
The promise part here is suppose to execute the console logs but it is not getting executed. Is there something that i'm missing here. The host is the URL that we obtain when we deploy a function. Or is there some firebase or aws related plan problem. I'am using the spark plan in firebase. Thankyou.

Permission Issue at an AWS API using Lambda

I'm testing my newly deployed AWS API using https://www.apitester.com/.
As you can see i cant access the API. The API is deployed and the Lambda code looks as following.
const AWS = require('aws-sdk');
var bucket = new AWS.S3();
exports.handler = (event, context, callback) => {
let data =JSON.parse(event.body);
var params = {
"Body": data,
"Bucket": "smartmatressbucket",
// "Key": filePath
};
bucket.upload(params, function(err, data){
if(err) {
callback(err, null);
} else {
let response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(data),
"isBase64Encoded": false
};
callback(null, response);
}
});
};
Looking at the response log, it seems the API Gateway generates "ForbiddenException". I believe the most possible reason is using an incorrect API URL (eg- https://ogk2hm09j0.execute-api.eu-central-1.amazonaws.com/).
Suppose you configure the Lambda function to a GET method of a resource name "resourceA". Then you deploy the API to a stage named "dev". Then the correct URL should be https://ogk2hm09j0.execute-api.eu-central-1.amazonaws.com/dev/resourceA
But looking at the API URL in the logs, it seems the stage name or the resource name is not specified.

Error Handling in AWS API Gateway and Lambda always return 502

I use serverless to implement Lambda and Api gateway.
When I implement Error Handling, below code always get 502 bad gateway.
handler.js
module.exports.hello = (event, context, callback) => {
const response = {
statusCode: 400,
headers: {
"Content-Type" : "application/json"
},
body: JSON.stringify({
"status": "error",
"message": "Missing Params"
})
};
callback(response);
};
CloudWatch do log error.
{
"errorMessage": "[object Object]"
}
I code this way by following the method "Custom error object serialization" in below AWS blog.
Ref
I change callback first parms to null and work fine. Ref
module.exports.hello = (event, context, callback) => {
const response = {
statusCode: 400,
headers: {
"Content-Type" : "application/json"
},
body: JSON.stringify({
"status": "error",
"message": "Missing Params"
})
};
callback(null, response);
};
This is a common pattern in Node.js and its called Error-First Callbacks.
Basically, if you pass a first argument into your callback, it will be considered and handled as an Error.
As you mentioned, once you put a callback(null, response);, it all worked as expected, since the first argument is null.