Switching authentication flows
For client side authentication there are three different flows:
-
USER_SRP_AUTH
:TheUSER_SRP_AUTH
flow uses theSRP protocol (Secure Remote Password)where the password never leaves the client and is unknown to the server. This is the recommended flow and is used by default. -
USER_PASSWORD_AUTH
:TheUSER_PASSWORD_AUTH
flow will send user credentials to the backend without applying SRP encryption. If you want to migrate users to Cognito using the "Migration" trigger and avoid forcing users to reset their passwords, you will need to use this authentication type because the Lambda function invoked by the trigger needs to verify the supplied credentials. -
CUSTOM_WITH_SRP
&CUSTOM_WITHOUT_SRP
:Allows for a series of challenge and response cycles that can be customized to meet different requirements.
The Auth flow can be customized when callingsignIn
,for example:
signIn({username,password,options:{authFlowType:'USER_PASSWORD_AUTH'}})
For more information about authentication flows, please visitAWS Cognito developer documentation
USER_PASSWORD_AUTH flow
A use case for theUSER_PASSWORD_AUTH
authentication flow is migrating users into Amazon Cognito
Set up auth backend
In order to use the authentication flowUSER_PASSWORD_AUTH
,your Cognito app client has to be configured to allow it. In the AWS Console, this is done by ticking the checkbox at General settings > App clients > Show Details (for the affected client) > Enable username-password (non-SRP) flow. If you're using the AWS CLI or CloudFormation, update your app client by addingUSER_PASSWORD_AUTH
to the list of "Explicit Auth Flows".
Migrate users with Amazon Cognito
Amazon Cognito provides a trigger to migrate users from your existing user directory seamlessly into Cognito. You achieve this by configuring your User Pool's "Migration" trigger which invokes a Lambda function whenever a user that does not already exist in the user pool authenticates, or resets their password.
In short, the Lambda function will validate the user credentials against your existing user directory and return a response object containing the user attributes and status on success. An error message will be returned if an error occurs. There's a documentationhereon how to set up this migration flow and more detailed instructionshereon how the lambda should handle request and response objects.
CUSTOM_WITH_SRP
&CUSTOM_WITHOUT_SRP
flows
Amazon Cognito user pools supports customizing the authentication flow to enable custom challenge types,
in addition to a password in order to verify the identity of users. These challenge types may include CAPTCHAs
or dynamic challenge questions. TheCUSTOM_WITH_SRP
flow requires a password when callingsignIn
.Both of
these flows map to theCUSTOM_AUTH
flow in Cognito.
To define your challenges for custom authentication flow, you need to implement three Lambda triggers for Amazon Cognito.
Custom authentication flow
To initiate a custom authentication flow in your app, callsignIn
without a password. A custom challenge needs to be answered using theconfirmSignIn
API:
import{signIn,confirmSignIn}from'aws-amplify/auth';
exportasyncfunctionhandleSignIn(username:string){constchallengeResponse='the answer for the challenge';try{const{nextStep}=awaitsignIn({username,options:{authFlowType:'CUSTOM_WITHOUT_SRP',},});if(nextStep.signInStep==='CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE'){try{// to send the answer of the custom challengeconstoutput=awaitconfirmSignIn({challengeResponse});console.log(output);}catch(err){console.log(err);}}}catch(err){console.log(err);}}
import{signIn,confirmSignIn}from'aws-amplify/auth';
exportasyncfunctionhandleSignIn(username){constchallengeResponse='the answer for the challenge';try{const{nextStep}=awaitsignIn({username,options:{authFlowType:'CUSTOM_WITHOUT_SRP',},});if(nextStep.signInStep==='CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE'){try{// to send the answer of the custom challengeconstoutput=awaitconfirmSignIn({challengeResponse});console.log(output);}catch(err){console.log(err);}}}catch(err){console.log(err);}}
CAPTCHA-based authentication
Here is the sample for creating a CAPTCHA challenge with a Lambda Trigger.
TheCreate Auth Challenge Lambda Trigger
creates a CAPTCHA as a challenge to the user. The URL for the CAPTCHA image and the expected answer is added to the private challenge parameters:
import{Handler}from'aws-lambda';
exportconsthandler:Handler=async(event)=>{if(!event?.request?.session||event?.request?.session?.length===0){event.response.publicChallengeParameters={captchaUrl:'url/123.jpg',};event.response.privateChallengeParameters={answer:'5',};event.response.challengeMetadata='CAPTCHA_CHALLENGE';}returnevent;};
exportconsthandler=async(event)=>{if(!Array.isArray(event?.request?.session)||event?.request?.session?.length){event.response.publicChallengeParameters={captchaUrl:'url/123.jpg',};event.response.privateChallengeParameters={answer:'5',};event.response.challengeMetadata='CAPTCHA_CHALLENGE';}returnevent;};
ThisDefine Auth Challenge Lambda Trigger
defines a custom challenge:
import{Handler}from'aws-lambda';
exportconsthandler:Handler=async(event)=>{if(!Array.isArray(event?.request?.session)||event?.request?.session?.length){// If you don't have a session or it is empty then send a CUSTOM_CHALLENGEevent.response.challengeName='CUSTOM_CHALLENGE';event.response.failAuthentication=false;event.response.issueTokens=false;}elseif(event?.request?.session?.length===1&&event?.request?.session[0]?.challengeResult){// If you passed the CUSTOM_CHALLENGE then issue tokenevent.response.failAuthentication=false;event.response.issueTokens=true;}else{// Something is wrong. Fail authenticationevent.response.failAuthentication=true;event.response.issueTokens=false;}
returnevent;};
exportconsthandler=async(event)=>{if(!Array.isArray(event?.request?.session)||event?.request?.session?.length){// If you don't have a session or it is empty then send a CUSTOM_CHALLENGEevent.response.challengeName='CUSTOM_CHALLENGE';event.response.failAuthentication=false;event.response.issueTokens=false;}elseif(event?.request?.session?.length===1&&event?.request?.session[0]?.challengeResult){// If you passed the CUSTOM_CHALLENGE then issue tokenevent.response.failAuthentication=false;event.response.issueTokens=true;}else{// Something is wrong. Fail authenticationevent.response.failAuthentication=true;event.response.issueTokens=false;}
returnevent;};
TheVerify Auth Challenge Response Lambda Trigger
is used to verify a challenge answer:
import{Handler}from"aws-lambda";
exportconsthandler:Handler=async(event,context)=>{if(event?.request?.privateChallengeParameters?.answer===event?.request?.challengeAnswer){event.response.answerCorrect=true;}else{event.response.answerCorrect=false;}returnevent;};
exportconsthandler=async(event,context)=>{if(event?.request?.privateChallengeParameters?.answer===event?.request?.challengeAnswer){event.response.answerCorrect=true;}else{event.response.answerCorrect=false;}returnevent;};