I used Cognito triggers to solve this problem.
So the solution is to write a pre-authentication lambda function which triggers when user try to sign-in.
The lambda function looks like this
const {
CognitoIdentityProvider,
UserStatusType,
} = require("@aws-sdk/client-cognito-identity-provider");
exports.handler = async (event, context, callback) => {
const cognitoIdp = new CognitoIdentityProvider();
const params = {
UserPoolId: event.userPoolId,
Filter: 'email = "' + event.request.validationData.Username + '"',
};
const { Users: users } = await cognitoIdp.listUsers(params);
if (users && users.length > 0) {
// User exists
const user = users[0];
if (user.UserStatus === UserStatusType.UNCONFIRMED) {
throw Error("USER_UNCONFIRMED");
}
const emailVerifiedAttribute = user.Attributes.find(
(attr) => attr.Name === "email_verified"
);
if (emailVerifiedAttribute && emailVerifiedAttribute.Value === "false") {
throw Error("EMAIL_NOT_VERIFIED");
}
}
return event;
};
This helps a user to know whether the user exists in Cognito user pool and they are not confirmed.
This exception can be tracked like this
const onError = (err) => {
if (err instanceof Error) {
let msg;
switch (err.name) {
case 'UserLambdaValidationException':
if (err.message.includes('USER_UNCONFIRMED')) {
msg = 'User not confirmed, Redirecting to verification page';
//Add logic to redirect to verification page and resend code
} else {
msg = 'Something went wrong, Please try again later';
}
break;
default:
msg = err.message;
break;
}
}
};
With this info we can redirect our page to confirmation page and use Cognito's resendConfirmationCode
method to send the verification code.
export async function resendCode(username) {
return new Promise((resolve, reject) => {
const cognitoUser = getCognitoUser(username);
cognitoUser.resendConfirmationCode((err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
This helps to confirm the Cognito user.