Amazon Cognito User Pool – Custom challenge triggers

There is an option to enable custom authentication flows in Amazon Cognito user pools using AWS Lambda triggers. If you need to implement one extra challenge before allowing users to access the system, Custom Challenge triggers can help you.

Overview

Again, Amazon Cognito provides an option to trigger AWS Lambdas to customize workflows and the user experience with the system. And custom authentication flow helps to add one or many customs challenges before authorizing a user or generating token in Amazon Cognito

However, we need to force the user to select his account in few applications before we create the OAuth token. Thus, we use a custom authentication flow to add account selection. In addition, you cannot use admin authentication flow with this. Anyhow, there are three event triggers which can be used to add more than one challenges.

Cognit Triggers

It is possible to make advanced customizations to Cognito user pool with AWS Lambda functions to trigger with different events to achieve better user experience. Likewise, you may create an AWS Lambda function in c# or your preferred language that supported by Lambda and then trigger that function during user pool operations.
Furthermore, Custom Authentication Challenge Lambda Triggers are three of those triggers. At least two of them is required if you use two of them.
If you want to know more about Amazon AWS C# Lambda, please check one of my earlier posts – Let’s deploy a simple AWS C# Lambda

Unable to use

You can only use these triggers with Custom Authentication flow

Custom Authentication Flow

Custom Authentication Challenge Lambda Triggers
Custom Authentication Challenge Lambda Triggers

These three lambdas trigger issue and verify their own challenges as part of a user pool custom authentication flow to incorporate new challenge types. For instance, these challenge types may include CAPTCHAs or dynamic challenge questions.

Adding three C# Lambda

In this section, I will create three simple amazon lambdas in dotnet core to demonstrate the custom authentication flow for selecting an account.

All these three lambdas receive JObject as input and also return JObject as output. JObject represents a JSON object that contains a collection of JProperties. You can deserialize this to your model but in this example I keep the example as simple as possible.

Define Auth Challenge

This trigger initiates the custom authentication flow. When any request comes with CUSTOM_CHALLENGE auth flow, this lambda will trigger and define what actions are required. Therefore, I have added a minimum c# asp.net core implementation of first lambda below.

public JObject FunctionHandler(JObject input, ILambdaContext context){
	var response = input;
	var requestSession = input["request"]["session"];
	if (requestSession.HasValues)
	{
		var customChallenge = requestSession
			.First(s => s["challengeName"].ToString() == "CUSTOM_CHALLENGE");

		if (customChallenge.HasValues && customChallenge["challengeResult"].Value<bool>()){
			var selectAccount = requestSession
				.First(s => s["challengeMetadata"].ToString() == "SELECT_ACCOUNT");
				
			if (selectAccount.HasValues && selectAccount["challengeResult"].Value<bool>()){
				response["response"]["issueTokens"] = true;
				response["response"]["failAuthentication"] = false;
			}
			else{
				response["response"]["failAuthentication"] = true;
			}
		}
		else{
			ChallengeContinue(response);
		}
	}
	else{
		ChallengeContinue(response);
	}
	return response;
}
private static void ChallengeContinue(JObject response)
{
	response["response"]["challengeName"] = "CUSTOM_CHALLENGE";
	response["response"]["issueTokens"] = false;
	response["response"]["failAuthentication"] = false;
}

It is also confirming the challenge result. Moreover, if all the challenges are fulfilled in the session, this lambda should return issueTokens true.
On the contrary, failAuthentication should be true to inform Cognito to not to generate any token for the user. You can find the full implementation here.

Create Auth Challenge

Basically, “Create Auth Challenge” will be invoked only if a custom challenge has been specified as part of the Define Auth Challenge trigger. Again, if no custom challenge is specified Cognito will ignore this event

public JObject FunctionHandler(JObject input, ILambdaContext context){
	var response = input;
	
	response["response"]["publicChallengeParameters"] = JToken.FromObject(
		new Dictionary<string, string>{
			{"10", "Demo UK"},
			{"20", "Demo Singapore"}
		});

	response["response"]["privateChallengeParameters"] = JToken.FromObject(20);;
	response["response"]["challengeMetadata"] = "SELECT_ACCOUNT";
	return response;
}

In this trigger, we defined the publicChallengeParmeters and privateChallengeParameters. In brief Public property contains information that can be visible to the user. On the contrary, private parameters are passed to verify user inputs. Please check the details in Github repo

Verify Auth Challange

Amazon Cognito invokes this trigger to verify if the response from the end user for a custom Auth Challenge is valid or not

AWS Documentation
public JObject FunctionHandler(JObject input, ILambdaContext context){
	var response = input;
	
	var challengeAnswer = input["request"]["challengeAnswer"].ToString();
	var privateChallengeParams = input["request"]["privateChallengeParameters"];
	if (privateChallengeParams.HasValues){
		var answerCorrect = privateChallengeParams
			.ToObject<Dictionary<string, string>>()
			.Any(a=> a.Key == challengeAnswer);
		response["response"]["answerCorrect"] = answerCorrect;
	}
	return response;
}

Finally, this lambda verify the answers and set a boolean parameter answerCorrect with the result of the success. And then, it going back to trigger “Define Auth Challenge” event again. The sample code can be found here.

Selecting lambdas

Now, go to your Cognito user pool. Select the Triggers tab, and find the following three events and select your published lambdas as below

Hooking lambdas with Cognito triggers

Sign-in with Custom challenge – Select Account

You can generalize authentication into two common steps with the user pool InitiateAuth and RespondToAuthChallenge API methods.
In this flow, a user authenticates by answering successive challenges until authentication either fails or the user is issued tokens. These two API calls can be repeated to include different challenges.

Client application take two Steps

To illustrate, I have tried a desktop client to verify the workflow.

First it is asking for user credentials
User is forced to select account before generating token

You can download the source code from my GitHub repository.
Besides, please have a look into the code to find the minimum implementation. Eventually, a user is selecting an account after successfully validating the password. InitiateAuth and RespondToAuthChallenge are used to fullfil the challenge

Read more

In order to get depth knowledge about custom challenge events please check the following AWS Documentation pages

2 thoughts on “Amazon Cognito User Pool – Custom challenge triggers”

  1. Hi, I was able to configure my app to use custom challenge, but is it possible to use this for only specific condition and use password after that.
    I am working on an app where I create user using adminCreateUser and use custom challenge to sign in user. The user redirects from another service using oauth so it make sense to login the user using this custom challenge. But I also want user to set password so that user can sign in using that password in the login form. I am unable to set password for user.

I would like to hear your thoughts