Guides & tutorials

How to create Lambda triggers to use Custom Auth flows with Amazon Cognito

Import this tutorial scenario in your workspace
Table of contents

In this tutorial, we will cover how to create Lambda functions for custom authentication flows. This is useful if you want to:

  • Automatically log in a user once they sign up.
  • Create passwordless flows.

The approach we will follow with these Lambda functions assume that this is a first-time login in a signup flow or that you have a custom flow that authenticates the user. If you don’t have any scenarios yet, have a look at our templates.

1. Create Lambda functions

Go to your Lambda console, where we will create 3 Lambda functions:

1.1 Define auth challenge function

Create the first function, name it “defineCustomAuthChallenge” and add the following code:

function isArenguChallenge(cr) {
    return cr.challengeName === 'CUSTOM_CHALLENGE'
        && cr.challengeMetadata === 'ARENGU_CLAIM_JWT';
}

exports.handler = async (event) => {

    const userFound = event.request.userNotFound === false;

    if (!userFound) {
        event.response.failAuthentication = true;
        return event;
    }

    const noChallenges = !event.request.session || event.request.session.length === 0;
    
    if (noChallenges) {
        event.response.challengeName = 'CUSTOM_CHALLENGE';
        return event;
    }

    const arenguChallenge = event.request.session.find((cr) => isArenguChallenge((cr)));
    
    if (!arenguChallenge) {
        event.response.failAuthentication = true;
        return event;
    }
    
    event.response.issueTokens = arenguChallenge.challengeResult;
    return event;
};

1.2 Create auth challenge function

Create the second function, name it “createCustomAuthChallenge” and add the following code:

exports.handler = async (event) => {

    event.response = {
        challengeMetadata: 'ARENGU_CLAIM_JWT',
        // Cognito requires at least one public param
        publicChallengeParameters: {
            "jwt": "required"
        },
        privateChallengeParameters: {
            "arengu_challenge": "claim_jwt"
        },
    };

    return event;
};

1.3 Verify auth challenge response function

Create the third function, name it “verifyCustomAuthChallenge” and add the following code:

const JWT_SECRET = process.env.JWT_SECRET;
const jwt = require('jsonwebtoken');

exports.handler = async (event) => {
  
  const arenguChallenge = event.request.privateChallengeParameters
      && event.request.privateChallengeParameters.arengu_challenge === 'claim_jwt';

  if (!arenguChallenge) {
      event.response.answerCorrect = false;
      return event;
  }
  
  try {
    const decodedJwt = jwt.verify(event.request.challengeAnswer, JWT_SECRET);
    
    if (decodedJwt.sub === event.request.userAttributes.sub) {
      event.response.answerCorrect = true;
    } else {
      event.response.answerCorrect = false;
    }
  } catch(err) {
    event.response.answerCorrect = false;
  }

  return event;
};
This Lambda function requires installing the "jsonwebtoken" node module. You can directly upload this .zip that includes the module.

As we are using JSON Web Tokens (JWT) to communicate with Amazon Cognito, we need to create a secret key to sign the JWT. Go to Configuration -> Environment variables -> Edit and add a variable with Key “JWT_SECRET_KEY” and a long random string as Value.

2. Add Lambda triggers to your Cognito user pool

Add a Lambda trigger under your User pool properties

Click on Add Lambda trigger

  • Select “Custom authentication
  • Select “Define auth challenge
  • Select “defineCustomAuthChallenge” lambda function

Create 2 additional Lambda triggers with the 2 remaining Lambdas functions:

  1. Create auth challenge -> createCustomAuthChallenge
  2. Verify auth challenge response -> verifyCustomAuthChallenge

3. Create custom JWTs to log in users

Once your Lambda triggers are created, you login users by generating custom JWTs in your backend with the following information:

  • Secret or private key: the same secret from the "Verify auth challenge response" Lambda function.
  • Subject: the sub of the user (eg. 05975eb2-136b-44af-9834-b3d2b7e22ae5)

And using the Amplify SDK with the following methods:

import { Auth } from "aws-amplify";

Auth.configure({
  // other configurations
  // ...
  authenticationFlowType: "CUSTOM_AUTH"
});

const username = "YOUR_USER_USERNAME";
const token = "YOUR_GENERATED_JWT";

Auth.signIn(username)
  .then(user => {
    Auth.sendCustomChallengeAnswer(user, token)
      .then(o => console.log(o))
      .catch(err => console.log(err));
  })
  .catch(err => console.log(err));

In Arengu, and if you've started from a template, you will only need to add the secret to the "Sign JSON web token" action:

And configure the Ending screen settings, that will automatically use the Amplify SDK if it's configured in your site:

You might like to read

See more tutorials

Getting started with Arengu

Arengu allows you to build all your user flows connected to your current stack, and avoids coding all the UI, complex integrations, validations or logic from scratch. Try it for free and start building faster and scaling your application needs as they grow.