Introduction
When using a custom authentication flow with Cognito, several steps occur behind the scenes. Understanding the entire flow can be very beneficial.
The process starts with a user request to initiate authentication. Cognito responds by creating a new session token, which expires after 3 minutes, and sends it to the defineChallenge
Lambda function. This function acts as a state machine for the entire authentication flow. Since this is the first invocation in the session, we return one of the predefined challenge types provided by AWS. The ChallengeNameType
enum in the AWS SDK repository lists all possible challenge types.
Next, Cognito triggers the createChallenge
Lambda function with the challenge type. Here, we implement the handler logic to define how the custom authentication works. Once the challenge parameters are set, we return the public parameters to the client along with the session token.
The user then enters the challenge response, which, along with the username and session token, is sent to the respondToAuthChallenge
command. Cognito takes this response and triggers the verifyChallenge
Lambda function. Here, we verify if the provided challenge response matches the correct answer set in the createChallenge
Lambda function.
If the response is correct, Cognito invokes the defineAuth
Lambda function again, which responds with authentication tokens. Finally, Cognito sends the tokens to the client, completing the authentication process.
Define Challenge Lambda
The defineChallenge
Lambda function is the most complex as it acts as a state machine for the entire authentication flow. Cognito provides the current session in the request, which is an array of all challenge answers.
To create the appropriate response for the user pool, we use three properties on the response object: issueTokens
, failAuthentication
, and challengeName
.
If the session array is empty, we issue the challenge since it's the first time the Lambda has been triggered. Otherwise, we validate the challenge. If the challenge name matches our configuration, we issue tokens if challengeResult
is true and fail authentication if it is false.
Create Challenge Lambda
The createChallenge
Lambda function generates the temporary password for logging in. It also notifies the user of the correct password via email, text message, or push notification. In this example, an email with a login link is sent. The link includes the code and username as query parameters, which the frontend application will use to call the backend API and respond to the challenge.
We set public and private challenge parameters. Public parameters are sent back to the client and should not include the challenge answer, while private parameters are only accessible within the authentication flow.
Verify Challenge Lambda
The verifyChallenge
Lambda function is straightforward. It compares the user input with the correct answer stored in privateChallengeParameters
in the createChallenge
Lambda function and assigns the result to the answerCorrect
property on the response object.
Login Service
In the login method, the AuthFlow
is set to CUSTOM_AUTH
, indicating that Cognito should trigger our defineChallenge
handler and not expect a PASSWORD
field in the AuthParameters
.
Verify Login
The verifyLogin
method splits the authentication process, requiring username and challenge answer separately. The session token from the initialAuth
command in the login method is required. The challenge name should match the one in the defineChallenge
Lambda, and the username and challenge answer are provided. If successful, the command responds with authentication tokens.
Direct Passwordless Authentication without Challenges
An alternative approach to achieving passwordless authentication is to directly issue tokens in the first invocation of the defineChallenge
Lambda function without creating challenges. This method bypasses the intermediate challenge steps.
As a result, when you call the login
service, which initiates the authentication process, the response will already include the authentication tokens. This eliminates the need for a separate verifyLogin
step since the tokens are issued directly in the login
service.
Define Challenge Lambda
Login Service
The login
service will return the authentication tokens directly, as Cognito has been instructed to issue them immediately. This method offers a more streamlined and seamless login experience, where users receive their tokens right after initiating the authentication process.
Conclusion
This guide has provided a comprehensive walkthrough of implementing a passwordless authentication flow using AWS Cognito and custom Lambda functions. By following these steps, you can create a secure and seamless login experience for your users without the need for passwords. Whether you opt for a challenge-based flow or direct authentication, AWS Cognito allows you to tailor the authentication process to meet your application's needs. Happy coding!