在DotNet中创建AWS Cognito PreSignup Lambda

9
使用 .Net Core 1.0 Lambda,我希望能够创建一个 Lambda 函数来处理 AWS Cognito 用户池的 PreSignUp 触发器。
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

public class PreSignUp_SignUp
{
  public string userPoolId { get; set; }
  public const string EmailKey = "email";
  public const string PhoneNumber = "phone_number";
  public Dictionary<string,string> userAttributes { get; set; }
  public Dictionary<string, string> validationData { get; set; }
}

public class PreSignup_SignUpResponse
{
  public bool autoConfirmUser { get; set; }
}

public class Function
{
  public PreSignup_SignUpResponse FunctionHandler(PreSignUp_SignUp input, ILambdaContext context)
  {
      return new PreSignup_SignUpResponse { autoConfirmUser = true };
  }
}

尽管使用示例请求调用Lambda时该请求成功并返回响应:

{
  "datasetName": "datasetName",
  "eventType": "SyncTrigger",
  "region": "us-east-1",
  "identityId": "identityId",
  "datasetRecords": {
    "SampleKey2": {
      "newValue": "newValue2",
      "oldValue": "oldValue2",
      "op": "replace"
    },
    "SampleKey1": {
      "newValue": "newValue1",
      "oldValue": "oldValue1",
      "op": "replace"
    }
  },
  "identityPoolId": "identityPoolId",
  "version": 2
}

在通过.Net AmazonCognitoIdentityProviderClient实际执行注册时,我收到了一个错误信息:
Amazon.CognitoIdentityProvider.Model.InvalidLambdaResponseException:无法识别的Lambda输出
我猜这意味着我的响应(可能甚至是请求)的格式不正确。
有人有AWS Cognito中PreSignUp触发器的工作示例吗?
3个回答

17

在Cognito触发器文档中指定的内容,cognito触发请求/响应必须包含整个有效负载:

http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html

在诊断此问题时,我发现创建一个函数处理程序以接受JObject并记录并返回相同对象是最好的起点,例如:

public JObject FunctionHandler(JObject input, ILambdaContext context)
{
    context.Logger.LogLine("Input was: " + input);
    return input;
}

这个功能将payload记录在Cloudwatch日志中,然后帮助你朝向所需的强类型结构。

在我的情况下,为了创建一个简单的函数,自动验证所有提供的凭据,我最终创建了以下类型。

public abstract class AbstractTriggerRequest
{
    [JsonProperty("userAttributes")]
    public Dictionary<string, string> UserAttributes { get; set; }
}

public abstract class AbstractTriggerResponse
{
}

public class TriggerCallerContext
{
    [JsonProperty("awsSdkVersion")]
    public string AwsSdkVersion { get; set; }
    [JsonProperty("clientId")]
    public string ClientId { get; set; }
}

public abstract class AbstractTriggerBase<TRequest, TResponse>
    where TRequest: AbstractTriggerRequest
    where TResponse: AbstractTriggerResponse
{
    [JsonProperty("version")]
    public int Version { get; set; }
    [JsonProperty("triggerSource")]
    public string TriggerSource { get; set; }
    [JsonProperty("region")]
    public string Region { get; set; }
    [JsonProperty("userPoolId")]
    public string UserPoolId { get; set; }  
    [JsonProperty("callerContext")]
    public TriggerCallerContext CallerContext { get; set; }
    [JsonProperty("request")]
    public TRequest Request { get; set; }
    [JsonProperty("response")]
    public TResponse Response { get; set; }
    [JsonProperty("userName", NullValueHandling = NullValueHandling.Ignore)]
    public string UserName { get; set; }
}

public class PreSignUpSignUpRequest : AbstractTriggerRequest
{
    [JsonProperty("validationData")]
    public Dictionary<string,string> ValidationData { get; set; }
}

那么Lambda函数最终会具有以下签名:

public class Function
{
    public PreSignUp_SignUp FunctionHandler(PreSignUp_SignUp input, ILambdaContext context)
    {
        context.Logger.LogLine("Auto-confirming everything!");

        input.Response = new PreSignUpSignUpResponse {
            AutoConfirmUser = true,
            // you can only auto-verify email or phone if it's present in the user attributes
            AutoVerifyEmail = input.Request.UserAttributes.ContainsKey("email"),
            AutoVerifyPhone = input.Request.UserAttributes.ContainsKey("phone_number") 
        };

        return input;
    }
}

希望这能帮助任何遇到在为Cognito编写Lambda触发器时遇到问题的人。


这是一个很好的答案,但也请查看DaddyCoder的回复,了解.netcore 3.1应用程序的更新语法,因为有一个新的JSON序列化器,你可能会使用它。 - Chris Matthews

7

除非您仍在使用旧的性能较低的 Amazon.Lambda.Serialization.Json.JsonSerializer ,否则前两个回答现在已经不准确了。 这个旧的序列化器使用 Newtonsoft.Json,而新的 Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer 实现了最新的 System.Text.Json

因此,JObject 参数不再适用,并应改为使用 JsonElement。 如果您尝试在此新序列化器中使用 JObject,则会出现错误,因为序列化器不知道如何处理这个对象。

您应该阅读 此文档 以更好地理解所有内容,但是您可以使用 GetProperty("[insert property name here]") 访问 JsonElement 的属性。

例如:

public async Task<JsonElement> FunctionHandler(JsonElement input, ILambdaContext context)
{
    var request = input.GetProperty("request");
    var userAttributes = request.GetProperty("userAttributes");
    string email = userAttributes.GetProperty("email").GetString();

    return input;
}

以这种方式,你无需构造整个类来适应所需的请求和响应参数,只需获取和设置所需的属性即可。

2
这是正确的。JsonElement类还可以很好地与日志记录配合使用,因此按照Bittercoder建议的起始点记录输入,您仍然可以使用语法:context.Logger.LogLine("Input was: " + input);来查看请求有效负载在Cloudwatch日志中的样子。 - Chris Matthews

4

这里已经有另一个很好的答案了。然而我不是一名专业的.NET开发人员,所以这个解决方案对我更有意义。

class AutoVerifyEmail
{
    public AutoVerifyEmail() { }

    public JObject AutoVerifyEmailPreSignup(JObject input, ILambdaContext context)
    {
        //Console.Write(input); //Print Input

        input["response"]["autoVerifyEmail"] = true;
        input["response"]["autoConfirmUser"] = true;

        return input;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接