Using a .Net Core 1.0 Lambda I want to be able to create a Lambda function which handles the PreSignUp trigger from an AWS Cognito User pool.
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 };
}
}
Though the request succeeds and returns a response when invoking the Lambda with an example request of:
{
"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
}
When performing an actual SignUp via the .Net AmazonCognitoIdentityProviderClient I get back an error:
Amazon.CognitoIdentityProvider.Model.InvalidLambdaResponseException : Unrecognizable lambda output
Which I'm guessing means I have not got the shape of the response (and possibly even request) correct.
Does anyone have an example of a .Net Lambda function that works for the PreSignUp trigger in AWS Cognito?
To add a user pool Lambda trigger with the console Go to the Amazon Cognito console , and then choose User Pools. Choose an existing user pool from the list, or create a user pool. Choose the User pool properties tab and locate Lambda triggers. Choose Add a Lambda trigger.
NET Core. At the time of writing, . NET Core 3.1 is the latest . NET framework with native support with AWS Lambda.
AWS Lambda provides the following libraries for C# functions: Amazon. Lambda. Core – This library provides a static Lambda logger, serialization interfaces and a context object.
The cognito trigger requests/responses must contain the entire payload as specified in the Cognito trigger documentation:
http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
I have found while diagnosing this issue the best place to start is by creating a function handler that takes a JObject and then logs and return's that same object e.g.
public JObject FunctionHandler(JObject input, ILambdaContext context)
{
context.Logger.LogLine("Input was: " + input);
return input;
}
This captures the payload in cloudwatch logs and then helps steer you towards the strongly typed structured required.
In my case for PreSignUp I ended up creating the following types to make a simple function which auto-verifies all supplied credentials.
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; }
}
The Lambda function then ends up with the following signature:
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;
}
}
Hopefully this helps anyone else running into issues writing Lambda triggers for Cognito.
The previous two responses are now inaccurate unless you still use the old, less performant Amazon.Lambda.Serialization.Json.JsonSerializer
. This old serializer uses Newtonsoft.Json
while the new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer
implements the recent System.Text.Json
.
As a result, a JObject
parameter is no longer appropriate and should instead be replaced with JsonElement
. If you try to use JObject
with this new serializer, you will get an error since the serializer doesn't know how to deal with this object.
You should read this to gain a better understanding of how it all works, but you access properties of the JsonElement
using GetProperty("[insert property name here]")
.
For example:
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;
}
This way, you don't need to construct entire classes to accommodate the required request and response parameters, just get and set the properties you need.
There is already another great answer in here. However I'm not a expert .NET developer so this solution makes more sense to me.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With