为什么我的 AWS Lambda 函数返回“无效 JSON”错误?

the*_*new 5 json node.js amazon-cognito aws-lambda

我有一个我几天前写的 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
            })
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

运行测试时,我在传入格式错误的事件数据时收到预期的错误消息,并且在尝试使用相同的电子邮件创建用户两次时收到 Cognito 错误。再次,这是意料之中的。但是,当在用户池中没有用户的情况下传递有效电子邮件时,我得到以下响应(格式为可读性):

Response:
{
  "statusCode": 400,
  "body": {
    "error": true,
    "error_message": "Invalid lambda function output : Invalid JSON"
  }
}
Run Code Online (Sandbox Code Playgroud)

检查此函数连接到的 Cognito 用户池,我看到已成功创建用户。然而,没有像几天前那样向该电子邮件地址发送电子邮件。

记录的所有信息都是说我有一个无效的 JSON 错误,根本没有authRes记录。当移除对 Cognito 的调用和对应的console.log调用时,try 块运行成功。所以问题在于对 Cognito 的调用。

但是为什么这段代码在几天前运行良好时今天却失败了?这是让我非常沮丧的部分。

the*_*new 8

问题根本不在于这个 lambda 函数。这是 AWS 和我用作 Cognito 用户池的自定义消息触发器的 lambda 函数的问题。这是出了问题的地方:

根据 AWS 文档,提供给自定义消息触发器 lambda 的事件数据对于函数调用具有以下形式adminCreateUser

{
  "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>"
  }
}
Run Code Online (Sandbox Code Playgroud)

并且预计从自定义消息触发器 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};
};
Run Code Online (Sandbox Code Playgroud)

几天前测试时,这是有效的,因为事件对象具有上面的形式。codeParameter所发生的情况是,AWS 偷偷地将和字段的内容更改usernameParameter为以下内容:

{
    ...
    "codeParameter": "{####}",
    "usernameParameter": "{username}",
    ...
}
Run Code Online (Sandbox Code Playgroud)

因此 lambda 函数正在返回null,因为这些字符串未通过验证,并且null不是有效的 JSON。

因此,临时修复方法是验证这些新字符串。然而,这引起了一些担忧。为什么 AWS 突然更改了事件对象而不更新文档?其次,我应该如何验证这些字符串是否可以安全地注入客户的电子邮件地址?我知道我可以清理,usernameParameter但是怎么样codeParameter,因为它很可能包含危险字符,例如< > & ' "因为它是用随机符号生成的密码?如果我自己生成密码,我可以确定它不会包含来自恶意行为者的数据,因此无需进行清理。但如果它来自 AWS,谁能说这些价值观不会受到损害呢?因此,为什么我首先添加了验证步骤,如果这些值已更改,该步骤应该会失败。这正是发生的事情。

简而言之,我的所有代码都按预期运行。AWS 在没有通知的情况下突然更改了他们的事件对象。


lob*_*dik 5

对我来说,问题是由于不使用async函数作为处理程序引起的。一旦我添加了异步(或者只是返回一个承诺也可以),错误就消失了