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.
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
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.
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
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
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.
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.
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