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

webghufk  于 2023-05-19  发布在  其他
关注(0)|答案(4)|浏览(221)

我有一个几天前写的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的调用。
但为什么这段代码在几天前还能完美工作的时候,今天却失败了呢?这就是让我非常沮丧的部分。

k3bvogb1

k3bvogb11#

问题根本不是这个lambda函数。这是AWS的一个问题,我使用lambda函数作为Cognito用户池的自定义消息触发器。以下是出错的地方:
根据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>"
  }
}

而且,从自定义消息触发器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偷偷地将codeParameterusernameParameter字段的内容更改为以下内容:

{
    ...
    "codeParameter": "{####}",
    "usernameParameter": "{username}",
    ...
}

因此lambda函数返回null,因为这些字符串没有通过验证-并且null不是有效的JSON。
所以临时的解决方法是验证这些新字符串。然而,这引起了一些关注。为什么AWS突然改变了事件对象,而没有更新文档?第二,我应该如何验证这些字符串在客户的电子邮件地址中注入是安全的?我知道我可以清理usernameParameter,但codeParameter怎么样,因为它很可能包含危险的字符,如< > & ' ",因为它是用随机符号生成的密码?如果自己生成密码,我可以确保它不会包含来自恶意行为者的数据,因此不需要进行清理。但是,如果它来自AWS,谁能说这些价值观不会受到损害呢?这就是为什么我首先添加了验证步骤,在这些值被更改的情况下应该失败。事实就是这样
总之,我的所有代码都按预期运行。AWS在没有通知的情况下突然更改了他们的事件对象。

l7wslrjt

l7wslrjt2#

对我来说,我的一个同事正在尝试自定义授权器,因为这是我们在应用程序中需要采取的下一步。他取了一个与自定义授权无关的Lambda,并将其添加到Cognito用户池>常规设置>触发器>预身份验证和后身份验证字段中,以观察Lambda是否在CloudWatch中被触发,但随后没有将其从用户池中删除。
显然,lambda not 提供了任何类型的预/后授权,导致从Lambda发回的无效响应,导致“Invalid JSON”响应。
具体来说,开发人员控制台中收到的响应是“400 Bad Request”,响应为:

{
  "__type":"InvalidLambdaResponseException",
  "message":"Invalid lambda function output : Invalid JSON"
}

在我验证了没有人更改内置的Amplify身份验证lambda之后,这提示我在Cognito中进行了挖掘,这要归功于_new的答案。

3npbholx

3npbholx3#

我有一个类似的问题很长一段时间。我感到困惑的是,来自sam应用程序的一个lambda启动时没有问题,而另一个却给出了这样的错误。事实证明,您需要增加超时时间。默认超时为3秒。尝试将其增加到10秒或更长时间。我把它与20秒的差距,因为λ仍然死亡后执行

v6ylcynt

v6ylcynt4#

对我来说,这个问题是由于没有使用async函数作为处理程序造成的。我一添加async(或者只返回一个promise也可以),错误就消失了

相关问题