I have been following this guide to implement email OTP in AWS Cognito. I have my user pool and lambdas configured as the guide suggests, and am able to successfully trigger the SRP_A
and PASSWORD_VERIFIER
steps. However, I never continue to the CUSTOM_CHALLENGE
flow - the PASSWORD_VERIFIER
step always provides me with AuthenticationResult
that contains the access token, which shows a successful login.
I tried taking away the SRP_A
and PASSWORD_VERIFIER
steps and just trying the CUSTOM_CHALLENGE
, and that works and successfully sends me the OTP to my email. It seems whenever I use PASSWORD_VERIFIER
I always get logged in and skip the rest of the CUSTOM_AUTH
flow, even if many other CUSTOM_AUTH
options are configured. My settings for the define auth lambda are the same as the guide's:
exports.handler = async (event) => {
if (event.request.session && event.request.session.length === 1
&& event.request.session[0].challengeName === 'SRP_A'
&& event.request.session[0].challengeResult === true) {
//SRP_A is the first challenge, this will be implemented by cognito. Set next challenge as PASSWORD_VERIFIER.
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'PASSWORD_VERIFIER';
} else if (event.request.session && event.request.session.length === 2
&& event.request.session[1].challengeName === 'PASSWORD_VERIFIER'
&& event.request.session[1].challengeResult === true) {
//If password verification is successful then set next challenge as CUSTOM_CHALLENGE.
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
} else if (event.request.session && event.request.session.length >= 5
&& event.request.session.slice(-1)[0].challengeName === 'CUSTOM_CHALLENGE'
&& event.request.session.slice(-1)[0].challengeResult === false) {
//The user has exhausted 3 attempts to enter correct otp.
event.response.issueTokens = false;
event.response.failAuthentication = true;
} else if (event.request.session && event.request.session.slice(-1)[0].challengeName === 'CUSTOM_CHALLENGE'
&& event.request.session.slice(-1)[0].challengeResult === true) {
//User entered the correct OTP. Issue tokens.
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
//user did not provide a correct answer yet.
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
}
return event;
};
Even if I change the of the issueTokens
key to false and failAuthentication
to true, if I respond to the PASSWORD_VERIFIER
step with the correct user/pass combo I will still log in successfully and it skips any other challenges.
Is there something configured incorrectly for my user pool? I've tried it with two separate user pools - one with MFA optional and one with MFA disabled, but the CUSTOM_AUTH
flow still doesn't seem compatible with PASSWORD_VERIFIER
.
I figured out the issue here - as you're using the API, the call to respondToAuthChallenge
must also be passed Session
from the previous response. On a separate challenge I passed Session
because I got an error that it was required when I didn't pass it, but for PASSWORD_VERIFIER
you don't need Session
set, likely because you can use it to create a session itself. Since I was continuing from the SRP_A
flow I needed to provide the same session value from that flow.