我有一个几天前写的lambda函数,在测试时表现很好。今天要对其进行测试(不更改任何代码)之后,我收到以下错误:"Invalid lambda function output : Invalid JSON"
。
这是功能代码(Node.js 10.x):
const AWS = require("aws-sdk");
const joi = require("@hapi/joi");
const Cognito = new AWS.CognitoIdentityServiceProvider();
exports.handler = async (event) => {
// NOTE: Cognito expects Username to be the user's email
// Vars
const userPoolId = process.env.COGNITO_USER_POOL_ID;
const {email : UNSAFE_EMAIL, language : UNSAFE_LANGUAGE = "en-US"} = event;
// Normalize email and language
const UNSAFE_TRIMMED_EMAIL = UNSAFE_EMAIL.trim();
const UNSAFE_TRIMMED_LANGUAGE = UNSAFE_LANGUAGE.trim();
// Validate UNSAFE_INPUTS
const languageRegex = /^[a-z]{2}-[A-Z]{2}$/;
const schema = joi.object().keys({
email: joi.string().trim().email({minDomainSegments: 2}).required(),
language: joi.string().trim().min(2).max(5).regex(languageRegex).required()
});
const validationResult = joi.validate({
email: UNSAFE_TRIMMED_EMAIL,
language: UNSAFE_TRIMMED_LANGUAGE
}, schema);
if(validationResult.error) {
console.log(JSON.stringify(validationResult.error, null, 2));
return {
statusCode: 400,
body: JSON.stringify({
error: true,
error_message: "Invalid"
})
}
}
// Validation successful, change variable names to reflect
const VALIDATED_EMAIL = UNSAFE_TRIMMED_EMAIL;
const VALIDATED_LANGUAGE = UNSAFE_TRIMMED_LANGUAGE;
// Cognito params
// Username is the user's email
// email is also required in UserAttributes in order to send confirmation
// DesiredDeliveryMediums is required to send confirmation
const params = {
UserPoolId: userPoolId,
Username: VALIDATED_EMAIL,
UserAttributes: [
{
Name: "email",
Value: VALIDATED_EMAIL
},
{
Name: "custom:language",
Value: VALIDATED_LANGUAGE
}
],
DesiredDeliveryMediums: ["EMAIL"]
}
// Attempt to create user in Cognito
try {
const authRes = await Cognito.adminCreateUser(params).promise();
console.log("Success: ", JSON.stringify(authRes, null, 2));
return {
statusCode: 200,
body: JSON.stringify({
success: true
})
}
} catch(err) {
console.log("Error: ", JSON.stringify(err, null, 2));
return {
statusCode: 400,
body: JSON.stringify({
error: true,
error_message: err.message
})
}
}
};
运行测试,在传递格式错误的事件数据时收到预期的错误消息,并且尝试两次创建具有相同电子邮件的用户时出现Cognito错误。同样,这是预期的。但是,当在用户池中传递没有用户的有效电子邮件时,我得到以下响应(为便于阅读而设置):
Response:
{
"statusCode": 400,
"body": {
"error": true,
"error_message": "Invalid lambda function output : Invalid JSON"
}
}
在此功能连接到的Cognito用户池中,我发现已经成功创建了一个用户。但是,几天前还没有电子邮件发送到该电子邮件地址。
所有记录的信息是说我有一个无效的JSON错误,根本没有记录authRes
。当删除对Cognito的调用和相应的console.log
调用时,try块将成功运行。因此,问题在于致电Cognito。
但是,为什么几天前代码运行良好,今天的代码为什么会失败?那就是让我非常沮丧的部分。
答案 0 :(得分:0)
这个问题根本与lambda函数无关。 AWS和我用作Cognito用户池的自定义消息触发器的lambda函数是一个问题。这是哪里出了问题:
根据AWS文档,提供给adminCreateUser
函数调用的自定义消息触发器lambda的事件数据具有以下形式:
{
"version": 1,
"triggerSource": "CustomMessage_AdminCreateUser",
"region": "<region>",
"userPoolId": "<userPoolId>",
"userName": "<userName>",
"callerContext": {
"awsSdk": "<calling aws sdk with version>",
"clientId": "<apps client id>",
...
},
"request": {
"userAttributes": {
"phone_number_verified": false,
"email_verified": true,
...
},
"codeParameter": "####",
"usernameParameter": "username"
},
"response": {
"smsMessage": "<custom message to be sent in the message with code parameter and username parameter>"
"emailMessage": "<custom message to be sent in the message with code parameter and username parameter>"
"emailSubject": "<custom email subject>"
}
}
并且希望从“自定义消息触发器” lambda返回的数据具有与事件相同的形式-仅更改了response
对象。
这就是我为lambda编写的内容:
const email_message = require("./email_message");
exports.handler = async (event) => {
// Vars
const {codeParameter, usernameParameter} = event.request;
console.log("Cognito Event: ", event);
// Check that codeParameter equals "####" and usernameParameter equals "username"
// This is to ensure that no compromised values are entered into the html
if(!(codeParameter === "####" && usernameParameter === "username")) {
return null;
}
const newRes = {
smsMessage: `Welcome: confirmation code is ${codeParameter} and username is ${usernameParameter}`,
emailMessage: email_message({codeParameter, usernameParameter}),
emailSubject: "Welcome To Our Site"
}
return {...event, response: newRes};
};
这在几天前进行测试时有效,因为事件对象具有上面的形式。发生的是,AWS偷偷地将codeParameter
和usernameParameter
字段的内容更改为以下内容:
{
...
"codeParameter": "{####}",
"usernameParameter": "{username}",
...
}
由于这些字符串未通过验证,因此lambda函数返回了null
-并且null
不是有效的JSON。
因此,临时解决方法是改为验证这些新字符串。但是,这引起了一些担忧。为什么AWS突然更改事件对象而没有对文档进行任何更新?其次,如何验证这些字符串可以安全地注入客户的电子邮件地址?我知道我可以清理usernameParameter
,但是codeParameter
可能会包含危险的字符,例如< > & ' "
,因为它是由随机符号生成的密码,因此FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job networkJob = dispatcher.newJobBuilder()
.setService(NetworkJobService.class)
.setTag(Const.NETWORK_JOB_TAG)
.setReplaceCurrent(true)
.setLifetime(Lifetime.FOREVER)
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
.setRecurring(true)
.setTrigger(Trigger.executionWindow(0, 0))
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
dispatcher.schedule(networkJob);
怎么样?如果我自己生成密码,则可以确保它不会包含来自恶意行为者的数据,因此无需清理。但是,如果它来自AWS,那么谁会说这些价值没有受到损害呢?因此,为什么我首先添加了验证步骤,如果这些值已更改,该步骤应该会失败。到底发生了什么。
因此,简而言之,我所有的代码都表现出预期的效果。 AWS突然更改了事件对象,恕不另行通知。