AWSCDK

Adding MFA(TOTP) to AWS Cognito with CDK

user image
Anoop Nair@sudodeznit
October 13th, 20225 min read

Multifactor Authentication (MFA) is important for adding extra layer of security to your applications. We will use CDK to add MFA using TOTP to Cognito and test it on a nextjs app.

cover image

Introduction

Time Based One Time Password (TOTP) is a way of doing two factor authentication, it uses a shared secret and current time as input to generate numeric passwords which are available offline, apps like Google Authenticator, Authy, and even password managers like 1Password

Adding MFA using CDK

To create a Cognito Userpool and add other configurations read this blog.

MFA can be set by specifying the mfa property in userpool. We can set MFA as REQUIRED or OPTIONAL . If set to REQUIRED and using Amplify UI it will show an additional screen for configuring TOTP on user signup and will ask for OTP on user signin. If set to OPTIONAL we will have to handle the MFA flow ourself.

In this post we are looking at adding MFA using TOTP other way of MFA is using OTP from SMS for which user will require a verified phone number.

const deznitUserpool = new cognito.UserPool(this, "deznituserpool", {
  // ...rest
  mfa: cognito.Mfa.REQUIRED,
  mfaSecondFactor: {
    sms: false,
    otp: true,
  },
});

After deploying the changes using cdk deploy we can check the AWS Console for the MFA configuration in Sign-in experience tab in the userpool.

user image

Frontend implementation

We will be creating a basic NextJs app with AWS Amplify you can use any frontend framework/library you like . Let’s create a nextjs app with typescript using the command below.

npx create-next-app@latest --typescript

Adding Amplify

You will need to install and configure Amplify cli on your machine if this is your first time using amplify. Please follow this guide on amplify docs.

First step is to add amplify fro that we just need to run :

amplify init

from the root of the project, it will setup amplify and create a folder named amplify and file named amplify-exports.js in your src folder (if location is not changed).

After installation is complete cd into the folder and install the following packages . @aws-amplify/ui-react gives us some prebuilt ui components that we can easily use to add things like authentication.

npm install aws-amplify @aws-amplify/ui-react

Authenticated page

Now to create a simple authenticated(protected) page replace index.tsx with the code below. Wrapping our component/page withAuthenticator from @aws-amplify/ui-react will make it accessible only if logged in.

import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { Authenticator } from "@aws-amplify/ui-react";
import { Amplify } from "aws-amplify";
import awsExports from "../src/aws-exports";

Amplify.configure({ ...awsExports, ssr: true });

//  customization of form fields
//  https://ui.docs.amplify.aws/react/connected-components/authenticator/customization
const formFields = {
  signIn: {
    username: {
      labelHidden: true,
      placeholder: "Enter email",
      isRequired: true,
      label: "Enter your email",
    },
  },
  signUp: {
    username: {
      labelHidden: true,
      placeholder: "Enter email",
      isRequired: true,
      label: "Enter your email",
    },
  },
};

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <Authenticator formFields={formFields}>
          {({ signOut, user }) => (
            <div className={styles.card}>
              <h1 className={styles.title}>Hello</h1>
              <h4>{user?.attributes?.email}</h4>
              <button className={styles.btn} onClick={signOut}>
                Sign Out
              </button>
            </div>
          )}
        </Authenticator>
      </main>
    </div>
  );
};

export default Home;

We also need to add some styles for the Amplify Ui, import styles in _app.tsx.

// _app.tsx
import "@aws-amplify/ui-react/styles.css";

To start the nextjs dev server npm run dev and we will be able to see the amplify auth ui on the home page.

user image

Importing cognito userpool

As we created cognito userpool using cdk we will have to import the userpool to amplify project. Run the following command from root of the project

amplify import auth

This command will generate all the configuration required by amplify library an next push operation, and this imported cognito auth can be used by other services like lambda or api for authentication and authorization.

To create the configuration file run.

amplify push

Once that operation is complete you should see changes in aws-exports.js file and new properties added.

const awsmobile = {
  aws_project_region: "YOUR_REGION",

  //newly added auth properties
  aws_cognito_region: "YOUR_REGION",
  aws_user_pools_id: "YOUR_USER_POOL_ID",
  aws_user_pools_web_client_id: "YOUR_USER_POOL_WEB_CLIENT_ID",
};

Now we can create a new account using email and password, as we have set MFA as REQUIRED we will be shown a screen to setup TOTP after we verify our email address. The screen will look something like this with a qr code that you have to scan with app like Google Authenticator and enter the numeric code generated by app.

user image

Once the TOTP setup is done everytime you sign in you will be asked for an otp from the authenticator app.

You can also handle all this flow manually with the help of api from aws cognito read the docs here