Note: If you create or update an SMS MFA configuration for your Cognito user pool, the Cognito service will send a test SMS message to an internal number in order to verify your configuration. You will be charged for these test messages by Amazon SNS.
For information about Amazon SNS pricing, see [Worldwide SMS Pricing](https://aws.amazon.com/sns/sms-pricing/).
MFA (Multi-factor authentication increases security for your app by adding an authentication method and not relying solely on the username (or alias) and password. AWS Amplify uses Amazon Cognito to provide MFA. Please see [Amazon Cognito Developer Guide](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa.html) for more information about setting up MFA in Amazon Cognito.
Once you enable MFA on Amazon Cognito, you can configure your app to work with MFA.
## Setup TOTP
With TOTP (Time-based One-time Password), your app user is challenged to complete authentication using a time-based one-time (TOTP) password after their username and password have been verified.
You can setup TOTP for a user in your app:
```ts
import { CognitoUserSession } from 'amazon-cognito-identity-js';
import { Auth } from 'aws-amplify';
type SetupTOTPAuthParameters = {
user: string;
challengeAnswer: string;
mfaType?: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA';
};
export async function setupTOTPAuth({ user, challengeAnswer, mfaType }: SetupTOTPAuthParameters) {
// To setup TOTP, first you need to get a `authorization code` from Amazon Cognito
// `user` is the current Authenticated user
const secretCode = await Auth.setupTOTP(user);
// You can directly display the `code` to the user or convert it to a QR code to be scanned.
// E.g., use following code sample to render a QR code with `qrcode.react` component:
// import QRCodeCanvas from 'qrcode.react';
// const str = "otpauth://totp/AWSCognito:"+ username + "?secret=" + secretCode + "&issuer=" + issuer;
//
// ...
// Then you will have your TOTP account in your TOTP-generating app (like Google Authenticator)
// Use the generated one-time password to verify the setup
try {
const cognitoUserSession: CognitoUserSession = await Auth.verifyTotpToken(
user,
challengeAnswer
);
// don't forget to set TOTP as the preferred MFA method
await Auth.setPreferredMFA(user, 'TOTP');
} catch (error) {
// Token is not verified
}
// ...
// Finally, when sign-in with MFA is enabled, use the confirmSignIn method
// to pass the TOTP code and MFA type.
const OTPCode = '123456'; // Code retrieved from authenticator app.
await Auth.confirmSignIn(user, OTPCode, mfaType); // Optional, MFA Type e.g. SMS_MFA || SOFTWARE_TOKEN_MFA
}
```
```js
import { Auth } from 'aws-amplify';
async function setupTOTPAuth(user, challengeAnswer, mfaType) {
// To setup TOTP, first you need to get a `authorization code` from Amazon Cognito
// `user` is the current Authenticated user
const OTPCode = '123456'; // Code retrieved from authenticator app.
// You can directly display the `code` to the user or convert it to a QR code to be scanned.
// E.g., use following code sample to render a QR code with `qrcode.react` component:
// import QRCodeCanvas from 'qrcode.react';
// const str = "otpauth://totp/AWSCognito:"+ username + "?secret=" + code + "&issuer=" + issuer;
//
// ...
// Then you will have your TOTP account in your TOTP-generating app (like Google Authenticator)
// Use the generated one-time password to verify the setup
try {
const cognitoUserSession = await Auth.verifyTotpToken(user, challengeAnswer);
// don't forget to set TOTP as the preferred MFA method
await Auth.setPreferredMFA(user, 'TOTP');
} catch (error) {
// Token is not verified
}
// ...
// Finally, when sign-in with MFA is enabled, use the confirmSignIn method
// to pass the TOTP code and MFA type.
await Auth.confirmSignIn(user, code, mfaType); // Optional, MFA Type e.g. SMS_MFA || SOFTWARE_TOKEN_MFA
}
```
## Setup MFA type
Multiple MFA types supported by Amazon Cognito. You can set the preferred method in your code:
```ts
import { Auth } from 'aws-amplify';
export async function setupMFAType() {
// You can select preferred mfa type, for example:
// Select TOTP as preferred
try {
const user = await Auth.currentAuthenticatedUser();
const data = await Auth.setPreferredMFA(user, 'TOTP');
console.log(data);
} catch (error) {
console.log('error setting up MFA type', error);
}
// Select SMS as preferred
await Auth.setPreferredMFA(user, 'SMS');
// Select no-mfa
await Auth.setPreferredMFA(user, 'NOMFA');
}
```
```js
import { Auth } from 'aws-amplify';
const user = await Auth.currentAuthenticatedUser();
async function setupMFAType(user) {
// You can select preferred mfa type, for example:
// Select TOTP as preferred
try {
const data = await Auth.setPreferredMFA(user, 'TOTP');
console.log(data);
} catch (error) {
console.log('error setting up MFA type', error);
}
// Select SMS as preferred
await Auth.setPreferredMFA(user, 'SMS');
// Select no-mfa
await Auth.setPreferredMFA(user, 'NOMFA');
}
```
## Retrieve current preferred MFA type
You can get current preferred MFA type in your code:
```ts
import { Auth } from 'aws-amplify';
export async function getPreferredMFAType() {
try {
// Will retrieve the current mfa type from cache
// bypassCache is optional, by default is false.
// If set to true, it will get the MFA type from server side instead of from local cache.
const user = await Auth.currentAuthenticatedUser();
const data = await Auth.getPreferredMFA(user, { bypassCache: false });
console.log('Current preferred MFA type is: ' + data);
} catch (err) {
console.log(err)
}
}
```
```js
import { Auth } from 'aws-amplify';
async function getPreferredMFAType() {
try {
// Will retrieve the current mfa type from cache
// bypassCache is optional, by default is false.
// If set to true, it will get the MFA type from server side instead of from local cache.
const user = await Auth.currentAuthenticatedUser();
const data = await Auth.getPreferredMFA(user, { bypassCache: false });
console.log('Current preferred MFA type is: ' + data);
} catch (err) {
console.log(err)
}
}
```
## Advanced use cases
### Sign-in with custom auth challenges
When signing in with user name and password, you will either sign in directly or be asked to pass some challenges before getting authenticated.
The `user` object returned from `Auth.signIn` will contain `challengeName` and `challengeParam` if the user needs to pass those challenges. You can call corresponding functions based on those two parameters.
ChallengeName:
- `SMS_MFA`: The user needs to input the code received from SMS message. You can submit the code by `Auth.confirmSignIn`.
- `SOFTWARE_TOKEN_MFA`: The user needs to input the OTP(one time password). You can submit the code by `Auth.confirmSignIn`.
- `NEW_PASSWORD_REQUIRED`: This happens when the user account is created through the Cognito console. The user needs to input the new password and required attributes. You can submit those data by `Auth.completeNewPassword`.
- `MFA_SETUP`: This happens when the MFA method is TOTP(the one time password) which requires the user to go through some steps to generate those passwords. You can start the setup process by `Auth.setupTOTP`.
- `SELECT_MFA_TYPE`: This happens when both SMS and TOTP methods are enabled but neither is set as preferred. You can set up your front-end application to let users choose what type they want to use
in the current session and set it using `user.sendMFASelectionAnswer` function.
The following code is only for demonstration purpose:
```ts
import { Auth } from 'aws-amplify';
type SignInParameters = {
username: string;
password: string;
mfaType?: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA';
};
type UserInput = {
username: string;
email: string;
phone_number: string;
};
export async function signIn({ username, password, mfaType }: SignInParameters) {
try {
const user = await Auth.signIn(username, password);
if (user?.challengeName === 'SMS_MFA' || user?.challengeName === 'SOFTWARE_TOKEN_MFA') {
// You need to get the code from the UI inputs
// and then trigger the following function with a button click
const code: string = await getCodeFromUserInput();
// If MFA is enabled, sign-in should be confirmed with the confirmation code
const loggedUser = await Auth.confirmSignIn(
user, // Return object from Auth.signIn()
code, // Confirmation code
mfaType // MFA Type e.g. SMS_MFA, SOFTWARE_TOKEN_MFA
);
} else if (user?.challengeName === 'NEW_PASSWORD_REQUIRED') {
const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
// You need to get the new password and required attributes from the UI inputs
// and then trigger the following function with a button click
// For example, the email and phone_number are required attributes
const { username, email, phone_number }: UserInput =
await getInfoFromUserInput();
const loggedUser = await Auth.completeNewPassword(
user, // the Cognito User Object
newPassword, // the new password
// OPTIONAL, the required attributes
{
email,
phone_number,
}
);
} else if (user?.challengeName === 'MFA_SETUP') {
// This happens when the MFA method is TOTP
// The user needs to setup the TOTP before using it
// More info please check the Enabling MFA part
await Auth.setupTOTP(user);
} else if (user?.challengeName === 'SELECT_MFA_TYPE') {
// You need to get the MFA method (SMS or TOTP) from user
// and trigger the following function
// user object needs to be CognitoUser type
user.sendMFASelectionAnswer(mfaType, {
onFailure: (err: Error) => {
console.error(err);
},
mfaRequired: (challengeName, parameters) => {
// Auth.confirmSignIn with SMS code
},
totpRequired: (challengeName, parameters) => {
// Auth.confirmSignIn with TOTP code
},
});
} else {
// The user directly signs in
console.log(user);
}
} catch (err) {
if (err?.code === 'UserNotConfirmedException') {
// The error happens if the user didn't finish the confirmation step when signing up
// In this case you need to resend the code and confirm the user
// About how to resend the code and confirm the user, please check the signUp part
} else if (err?.code === 'PasswordResetRequiredException') {
// The error happens when the password is reset in the Cognito console
// In this case you need to call forgotPassword to reset the password
// Please check the Forgot Password part.
} else if (err?.code === 'NotAuthorizedException') {
// The error happens when the incorrect password is provided
} else if (err?.code === 'UserNotFoundException') {
// The error happens when the supplied username/email does not exist in the Cognito user pool
} else {
console.log(err);
}
}
}
async function getCodeFromUserInput(): Promise {
// fetch code using a user form input
}
async function getInfoFromUserInput(): Promise {
// fetch user inputs like username, email and phoneNumber
}
```
```js
import { Auth } from 'aws-amplify';
async function signIn() {
try {
const user = await Auth.signIn(username, password);
if (user?.challengeName === 'SMS_MFA' || user?.challengeName === 'SOFTWARE_TOKEN_MFA') {
// You need to get the code from the UI inputs
// and then trigger the following function with a button click
const code = await getCodeFromUserInput();
// If MFA is enabled, sign-in should be confirmed with the confirmation code
const loggedUser = await Auth.confirmSignIn(
user, // Return object from Auth.signIn()
code, // Confirmation code
mfaType // MFA Type e.g. SMS_MFA, SOFTWARE_TOKEN_MFA
);
} else if (user?.challengeName === 'NEW_PASSWORD_REQUIRED') {
const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
// You need to get the new password and required attributes from the UI inputs
// and then trigger the following function with a button click
// For example, the email and phone_number are required attributes
const { username, email, phone_number } = await getInfoFromUserInput();
const loggedUser = await Auth.completeNewPassword(
user, // the Cognito User Object
newPassword, // the new password
// OPTIONAL, the required attributes
{
email, // from user input
phone_number, // from user input
}
);
} else if (user?.challengeName === 'MFA_SETUP') {
// This happens when the MFA method is TOTP
// The user needs to setup the TOTP before using it
// More info please check the Enabling MFA part
await Auth.setupTOTP(user);
} else if (user?.challengeName === 'SELECT_MFA_TYPE') {
// You need to get the MFA method (SMS or TOTP) from user
// and trigger the following function
// user object needs to be CognitoUser type
user.sendMFASelectionAnswer(mfaType, {
onFailure: (err) => {
console.error(err);
},
mfaRequired: (challengeName, parameters) => {
// Auth.confirmSignIn with SMS code
},
totpRequired: (challengeName, parameters) => {
// Auth.confirmSignIn with TOTP code
},
});
} else {
// The user directly signs in
console.log(user);
}
} catch (err) {
if (err?.code === 'UserNotConfirmedException') {
// The error happens if the user didn't finish the confirmation step when signing up
// In this case you need to resend the code and confirm the user
// About how to resend the code and confirm the user, please check the signUp part
} else if (err?.code === 'PasswordResetRequiredException') {
// The error happens when the password is reset in the Cognito console
// In this case you need to call forgotPassword to reset the password
// Please check the Forgot Password part.
} else if (err?.code === 'NotAuthorizedException') {
// The error happens when the incorrect password is provided
} else if (err?.code === 'UserNotFoundException') {
// The error happens when the supplied username/email does not exist in the Cognito user pool
} else {
console.log(err);
}
}
}
async function getCodeFromUserInput() {
// fetch code using a user form input
}
async function getInfoFromUserInput() {
// fetch user inputs like username, email and phoneNumber
}
```
### Sign-in with custom validation data for Lambda Trigger
You can also pass an object which has the username, password and validationData which is sent to a PreAuthentication Lambda trigger
```ts
import { Auth } from 'aws-amplify';
type CustomValidationParameters = {
username: string;
password: string;
validationData: {[ key: string ]: any; };
};
export async function customValidation({
username,
password,
validationData,
}: CustomValidationParameters) {
try {
const user = await Auth.signIn({
username, // Required, the username
password, // Optional, the password
validationData, // Optional, an arbitrary key-value pair map which can contain any key and will be passed to your PreAuthentication Lambda trigger as-is. It can be used to implement additional validations around authentication
});
console.log('user is signed in!', user);
} catch (error) {
console.log('error signing in:', error);
}
}
```
```js
import { Auth } from 'aws-amplify';
async function customValidation({ username, password, validationData }) {
try {
const user = await Auth.signIn({
username, // Required, the username
password, // Optional, the password
validationData, // Optional, an arbitrary key-value pair map which can contain any key and will be passed to your PreAuthentication Lambda trigger as-is. It can be used to implement additional validations around authentication
});
console.log('user is signed in!', user);
} catch (error) {
console.log('error signing in:', error);
}
}
```
### Forcing Email Uniqueness in Cognito User Pools
When your Cognito User Pool sign-in options are set to "_Username_", and "_Also allow sign in with verified email address_", the _signUp()_ method creates a new user account every time it's called, without validating email uniqueness. In this case you will end up having multiple user pool identities and all previously created accounts will have their _email_verified_ attribute changed to _false_.
To enforce Cognito User Pool signups with a unique email, you need to change your User Pool's _Attributes_ setting in [Amazon Cognito console](https://console.aws.amazon.com/cognito) as the following:
