Pre Signup Validation on AWS Cognito

  1. The validation will only live in this particular backend. If we create another app that interacts with the same user pool, we will have to repeat this logic there too.
  2. We’re adding bloat to the request handler, we could keep this handler clean by avoiding unnecessary code if we can.
  1. We can add custom validation when registering a new user to the user pool. In our case, it’s to check if the submitted email is already in use.
  2. The logic is baked into the user pool. Any signup event for this user pool will be validated regardless of where it’s coming from.

Implementation

Lambda

Make sure that the lambda is in the same region as the user pool. The following is the lambda’s code:

const { CognitoIdentityProviderClient, ListUsersCommand } = require("@aws-sdk/client-cognito-identity-provider");exports.handler = async (event, context, callback) => {
// TODO implement
const params = {
UserPoolId: 'us-west-2_rxOJKcUKc',
Filter: `email = "${event.request.userAttributes.email}"`
};
const client = new CognitoIdentityProviderClient();
const listUsersCommand = new ListUsersCommand(params);
const data = await client.send(listUsersCommand);if (data?.Users?.length > 0) {
callback(new Error("Email is already taken"), event);
} else {
callback(null, event);
}
};

Layer

This section is only applicable if you’re using the v3 SDK at the time of writing this article. If you’re not using the v3 SDK, you can skip ahead to the permissions section.

{
...
"scripts": {
...
"build": "rm -rf nodejs && rm cognito-base-layer.zip && npm install && mkdir nodejs && mv node_modules nodejs && zip -r cognito-base-layer.zip . && cp cognito-base-layer.zip ~/Downloads"
},
...
}
  1. Delete the previous nodejs directory and cognito-base-layer.zip file from the previous build.
  2. Install the packages.
  3. Create a directory called nodejs.
  4. Move the node_modules folder into nodejs.
  5. Zip the current directory into a zip file called cognito-base-layer.zip.
  6. Copy the zip file to the desired location (Optional).

Permissions

We’re not done yet. This lambda currently doesn’t have the correct permissions to list users from a Cognito user pool. Let’s grant the correct permissions to the lambda function. First, take note of the lambda function’s role.

{
"Version": "2012-10-17",
"Statement": []
}
{
"Effect": "Allow",
"Action": "cognito-idp:ListUsers",
"Resource": "arn:aws:cognito-idp:<region>:<account_id>:userpool/<userpool_id>"
}

Triggers

Navigate to the user pool’s console and click on the “Triggers” tab:

Result

That’s all there is to the setup for our use case. Now we can go ahead and try to sign up with an email address that is already used in the user pool. Doing so should return an error that looks like this:

UserLambdaValidationException: PreSignUp failed with error Email is already taken.
...
{
'$fault': 'client',
'$metadata': {
httpStatusCode: 400,
requestId: '3bc8f968-cbf5-4960-857f-e48daa312870',
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
},
__type: 'UserLambdaValidationException'
}

Bonus

“response”: { “autoConfirmUser”: “boolean”, “autoVerifyPhone”: “boolean” “autoVerifyEmail”: “boolean” }

event.response.autoConfirmUser = true event.response.autoVerifyPhone = true event.response.autoVerifyEmail = true

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store