I'm trying to implement passwordless authentication in Cognito using lambda triggers. I've implemented the three lambda functions:
- DefineAuthChallenge
- CreateAuthChallenge
- VerifyAuthChallenge
The issue is when I'm invoking initiateAuth API multiple events trigger the CreateAuthChallenge lambda. Hence, causing the user to receive more than one OTP for a given request. Below is the implementation of CreateAuthChallenge lambda.
` public class CreateAuthChallengeHandler implements RequestStreamHandler {
private static final String SENDER_EMAIL = "user@gmail.com";
private static final String SUBJECT = "OTP";
public ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
LambdaLogger logger = context.getLogger();
JsonNode mainNode = objectMapper.readTree(input);
logger.log("input: " + mainNode.toString());
JsonNode session = mainNode.get("request").get("session");
JsonNode userAttributes = mainNode.get("request").get("userAttributes");
JsonNode responseNode = mainNode.get("response");
int sessionLength = objectMapper.convertValue(session, ArrayList.class).size();
String secretCode;
if (sessionLength == 0) {
logger.log("sessionLength: " + sessionLength);
secretCode = getSecretCode();
SesClient sesClient = SesClient.builder().region(Region.AP_SOUTH_1).build();
try {
logger.log("sending email ...");
SendEmailRequest emailRequest = SendEmailRequest.builder()
.source(SENDER_EMAIL)
.destination(Destination.builder().toAddresses(userAttributes.get("email").asText()).build())
.message(Message.builder()
.subject(Content.builder().data(SUBJECT).build())
.body(Body.builder().text(Content.builder().data(secretCode).build()).build()).build())
.build();
SendEmailResponse sendEmailResponse = sesClient.sendEmail(emailRequest);
logger.log("sendEmailResult: " + sendEmailResponse.messageId());
} catch (SesException e) {
logger.log("Email sending failed. Error message: " + e.getMessage());
} finally {
sesClient.close();
}
} else {
JsonNode previousChallenge = session.get(sessionLength - 1);
secretCode = previousChallenge.get("challengeMetadata").asText();
logger.log("secretCode: " + secretCode);
}
((ObjectNode) responseNode).putPOJO("publicChallengeParameters", Map.of(
"email", userAttributes.get("email").asText()));
((ObjectNode) responseNode).putPOJO("privateChallengeParameters", Map.of(
"secretLoginCode", secretCode));
((ObjectNode) responseNode).put("challengeMetadata", secretCode);
objectMapper.writeValue(output, mainNode);
logger.log("output: " + output.toString());
}
}
`
pom.xml
`
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>XXXXXXXX</groupId>
<artifactId>XXXXXXX</artifactId>
<version>4.7-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ses</artifactId>
<version>2.25.40</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
`
Below are DefineAuthChallenge are logs:
2024-04-30T17:51:29.041+05:30 INIT_START Runtime Version: java:21.v15 Runtime Version ARN: arn:aws:lambda:ap-south-1::runtime:XXXXXX 2024-04-30T17:51:29.709+05:30 START RequestId: 81165b64-2ee5-4d40-83df-a3fc21faa856 Version: $LATEST 2024-04-30T17:51:30.032+05:30 input: {"version":"1","region":"ap-south-1","userPoolId":"XXXXXX","userName":"XXXXXX","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXXX"},"triggerSource":"DefineAuthChallenge_Authentication","request":{"userAttributes":{"sub":"f113dd3a-1021-7084-2495-8c4eb4853463","email_verified":"True","custom:login-path":"XXXXXX","cognito:user_status":"CONFIRMED","name":"XXXXXX","email":"XXXXXX"},"session":[],"userNotFound":false},"response":{"challengeName":null,"issueTokens":null,"failAuthentication":null}} 2024-04-30T17:51:30.230+05:30 output: {"version":"1","region":"ap-south-1","userPoolId":"XXXXXX","userName":"XXXXXX","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXXX"},"triggerSource":"DefineAuthChallenge_Authentication","request":{"userAttributes":{"sub":"f113dd3a-1021-7084-2495-8c4eb4853463","email_verified":"True","custom:login-path":"XXXXXX","cognito:user_status":"CONFIRMED","name":"XXXXXX","email":"XXXXXX"},"session":[],"userNotFound":false},"response":{"challengeName":"CUSTOM_CHALLENGE","issueTokens":false,"failAuthentication":false}} 2024-04-30T17:51:30.231+05:30 END RequestId: 81165b64-2ee5-4d40-83df-a3fc21faa856 2024-04-30T17:51:30.231+05:30 REPORT RequestId: 81165b64-2ee5-4d40-83df-a3fc21faa856 Duration: 521.66 ms Billed Duration: 522 ms Memory Size: 512 MB Max Memory Used: 111 MB Init Duration: 667.20 ms
Below are the CreateAuthChallenge logs:
2024-04-30T17:51:35.423+05:30 INIT_START Runtime Version: java:21.v15 Runtime Version ARN: arn:aws:lambda:ap-south-1::runtime:XXXXXX 2024-04-30T17:51:36.085+05:30 START RequestId: a9efd773-08b6-4b4b-a2b3-d1fb7e3fb502 Version: $LATEST 2024-04-30T17:51:36.407+05:30 input: {"version":"1","region":"ap-south-1","userPoolId":"XXXX","userName":"user@gmail.com","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXX"},"triggerSource":"CreateAuthChallenge_Authentication","request":{"userAttributes":{"sub":"XXXXXX","email_verified":"True","custom:login-path":"https://test.com","cognito:user_status":"CONFIRMED","name":"XXXXXXX","email":"XXXXXX"},"challengeName":"CUSTOM_CHALLENGE","session":[],"userNotFound":false},"response":{"publicChallengeParameters":null,"privateChallengeParameters":null,"challengeMetadata":null}} 2024-04-30T17:51:36.604+05:30 sessionLength: 0 2024-04-30T17:51:36.784+05:30 SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". 2024-04-30T17:51:36.784+05:30 SLF4J: Defaulting to no-operation (NOP) logger implementation 2024-04-30T17:51:36.784+05:30 SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. 2024-04-30T17:51:40.811+05:30 sending email ... 2024-04-30T17:51:43.649+05:30 sendEmailResult: 0109018f2ef42538-54f24435-4449-46c6-afb7-8750fbf5de56-000000 2024-04-30T17:51:43.710+05:30 output: {"version":"1","region":"ap-south-1","userPoolId":"XXXXXX","userName":"XXXXXX","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXXX"},"triggerSource":"CreateAuthChallenge_Authentication","request":{"userAttributes":{"sub":"XXXXXX","email_verified":"True","custom:login-path":"https://XXXXXX","cognito:user_status":"CONFIRMED","name":"XXXXXX","email":"XXXXXX"},"challengeName":"CUSTOM_CHALLENGE","session":[],"userNotFound":false},"response":{"publicChallengeParameters":{"email":"XXXXXX"},"privateChallengeParameters":{"secretLoginCode":"6605"},"challengeMetadata":"6605"}} 2024-04-30T17:51:43.724+05:30 END RequestId: a9efd773-08b6-4b4b-a2b3-d1fb7e3fb502 2024-04-30T17:51:43.724+05:30 REPORT RequestId: a9efd773-08b6-4b4b-a2b3-d1fb7e3fb502 Duration: 7639.15 ms Billed Duration: 7640 ms Memory Size: 512 MB Max Memory Used: 169 MB Init Duration: 661.01 ms 2024-04-30T17:52:25.756+05:30 START RequestId: d39e6945-45a2-4f81-a0b3-c860afdb47d4 Version: $LATEST 2024-04-30T17:52:25.757+05:30 input: {"version":"1","region":"ap-south-1","userPoolId":"XXXXXX","userName":"XXXXXX","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXXX"},"triggerSource":"CreateAuthChallenge_Authentication","request":{"userAttributes":{"sub":"XXXXXX","email_verified":"True","custom:login-path":"https://XXXXXX","cognito:user_status":"CONFIRMED","name":"XXXXXX","email":"XXXXXX"},"challengeName":"CUSTOM_CHALLENGE","session":[],"userNotFound":false},"response":{"publicChallengeParameters":null,"privateChallengeParameters":null,"challengeMetadata":null}} 2024-04-30T17:52:25.757+05:30 sessionLength: 0 2024-04-30T17:52:25.766+05:30 sending email ... 2024-04-30T17:52:25.958+05:30 sendEmailResult: 0109018f2ef4cb42-08000c0f-1d0f-411c-bc90-473fa1542e0d-000000 2024-04-30T17:52:25.965+05:30 output: {"version":"1","region":"ap-south-1","userPoolId":"XXXXXX","userName":"XXXXXX","callerContext":{"awsSdkVersion":"aws-sdk-java-1.12.641","clientId":"XXXXXX"},"triggerSource":"CreateAuthChallenge_Authentication","request":{"userAttributes":{"sub":"XXXXXX","email_verified":"True","custom:login-path":"https://XXXXXX","cognito:user_status":"CONFIRMED","name":"XXXXXX","email":"XXXXXX"},"challengeName":"CUSTOM_CHALLENGE","session":[],"userNotFound":false},"response":{"publicChallengeParameters":{"email":"XXXXXX"},"privateChallengeParameters":{"secretLoginCode":"5757"},"challengeMetadata":"5757"}} 2024-04-30T17:52:25.966+05:30 END RequestId: d39e6945-45a2-4f81-a0b3-c860afdb47d4 2024-04-30T17:52:25.966+05:30 REPORT RequestId: d39e6945-45a2-4f81-a0b3-c860afdb47d4 Duration: 210.20 ms Billed Duration: 211 ms Memory Size: 512 MB Max Memory Used: 171 MB
If you see the logs, the 'Init Duration' is not much but what is taking time is the code execution.
REPORT RequestId: a9efd773-08b6-4b4b-a2b3-d1fb7e3fb502 Duration: 7639.15 ms Billed Duration: 7640 ms Memory Size: 512 MB Max Memory Used: 169 MB Init Duration: 661.01 ms
My Code for sending email is taking around 9 seconds. 6 seconds jus to initialize SesClient and another 3 seconds to send email:
try(SesClient sesClient = SesClient.builder().region(Region.AP_SOUTH_1).build()) { logger.log("sending email ..."); SendEmailRequest emailRequest = SendEmailRequest.builder() .source(SENDER_EMAIL) .destination(Destination.builder().toAddresses(userAttributes.get("email").asText()).build()) .message(Message.builder() .subject(Content.builder().data(SUBJECT).build()) .body(Body.builder().text(Content.builder().data(secretCode).build()).build()).build()) .build(); SendEmailResponse sendEmailResponse = sesClient.sendEmail(emailRequest); logger.log("sendEmailResult: " + sendEmailResponse.messageId()); } catch (SesException e) { logger.log("Email sending failed. Error message: " + e.getMessage()); }
Is there any way to optimize this piece of code or should I move the email part to another lambda which runs in async.