Page updated Nov 15, 2023

Manage MFA settings

If MFA is ON or enabled for the user, you must call confirmSignIn with the OTP sent to their phone.

import { confirmSignIn } from 'aws-amplify/auth'; async function handleSignInConfirmation(otpCode: string) { try { await confirmSignIn({ challengeResponse: otpCode }); } catch (error) { console.log(error); } }
1import { confirmSignIn } from 'aws-amplify/auth';
2
3async function handleSignInConfirmation(otpCode: string) {
4 try {
5 await confirmSignIn({ challengeResponse: otpCode });
6 } catch (error) {
7 console.log(error);
8 }
9}

After a user has been signed in, call updateMFAPreference to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

import { updateMFAPreference } from 'aws-amplify/auth'; async function handleUpdateMFAPreference() { try { await updateMFAPreference({ sms: 'PREFERRED' }); } catch (error) { console.log(error); } }
1import { updateMFAPreference } from 'aws-amplify/auth';
2
3async function handleUpdateMFAPreference() {
4 try {
5 await updateMFAPreference({ sms: 'PREFERRED' });
6 } catch (error) {
7 console.log(error);
8 }
9}

Multi-factor authentication with TOTP

You can use Time-based One-Time Password (TOTP) for multi-factor authentication (MFA) in your web or mobile applications. The Amplify Auth category includes support for TOTP setup and verification using authenticator apps, offering an integrated solution and enhanced security for your users. These apps, such as Google Authenticator, Microsoft Authenticator, have the TOTP algorithm built-in and work by using a shared secret key and the current time to generate short-lived, six digit passwords.

Set up TOTP for a user

After you initiate a user sign in with the signIn API where a user is required to set up TOTP as an MFA method, the API call will return CONTINUE_SIGN_IN_WITH_TOTP_SETUP as a challenge and next step to handle in your app. You will get that challenge if the following conditions are met:

  • MFA is marked as Required in your user pool.
  • TOTP is enabled in your user pool.
  • User does not have TOTP MFA set up already.

The CONTINUE_SIGN_IN_WITH_TOTP_SETUP step signifies that the user must set up TOTP before they can sign in. The step returns an associated value of type TOTPSetupDetails which must be used to configure an authenticator app like Microsoft Authenticator or Google Authenticator. TOTPSetupDetails provides a helper method called getSetupURI which generates a URI that can be used, for example, in a button to open the user's installed authenticator app. For more advanced use cases, TOTPSetupDetails also contains a sharedSecret which can be used to either generate a QR code or be manually entered into an authenticator app.

Once the authenticator app is set up, the user can generate a TOTP code and provide it to the library to complete the sign in process.

import { signIn, SignInOutput } from 'aws-amplify/auth'; async function handleSignIn(username: string, password: string) { try { const output = await signIn({ username, password }); handleSignInNextSteps(output); } catch (error) { console.log(error); } } function handleSignInNextSteps(output: SignInOutput) { const { nextStep } = output; switch (nextStep.signInStep) { // ... case 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP': const totpSetupDetails = nextStep.totpSetupDetails; const appName = 'my_app_name'; const setupUri = totpSetupDetails.getSetupUri(appName); // Open setupUri with an authenticator APP to retrieve an OTP code break; // ... } }
1import { signIn, SignInOutput } from 'aws-amplify/auth';
2
3async function handleSignIn(username: string, password: string) {
4 try {
5 const output = await signIn({
6 username,
7 password
8 });
9
10 handleSignInNextSteps(output);
11 } catch (error) {
12 console.log(error);
13 }
14}
15
16function handleSignInNextSteps(output: SignInOutput) {
17 const { nextStep } = output;
18 switch (nextStep.signInStep) {
19 // ...
20 case 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP':
21 const totpSetupDetails = nextStep.totpSetupDetails;
22 const appName = 'my_app_name';
23 const setupUri = totpSetupDetails.getSetupUri(appName);
24 // Open setupUri with an authenticator APP to retrieve an OTP code
25 break;
26 // ...
27 }
28}

The TOTP code can be obtained from the user via a text field or any other means. Once the user provides the TOTP code, call confirmSignIn with the TOTP code as the challengeResponse parameter.

import { confirmSignIn } from 'aws-amplify/auth'; async function handleSignInConfirmation(totpCode: string) { try { await confirmSignIn({ challengeResponse: totpCode }); } catch (error) { console.log(error); } }
1import { confirmSignIn } from 'aws-amplify/auth';
2
3async function handleSignInConfirmation(totpCode: string) {
4 try {
5 await confirmSignIn({ challengeResponse: totpCode });
6 } catch (error) {
7 console.log(error);
8 }
9}

After a user has been signed in, call updateMFAPreference to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

import { updateMFAPreference } from 'aws-amplify/auth'; async function handleUpdateMFAPreference() { try { await updateMFAPreference({ totp: 'PREFERRED' }); } catch (error) { console.log(error); } }
1import { updateMFAPreference } from 'aws-amplify/auth';
2
3async function handleUpdateMFAPreference() {
4 try {
5 await updateMFAPreference({ totp: 'PREFERRED' });
6 } catch (error) {
7 console.log(error);
8 }
9}

Enable TOTP after a user is signed in

TOTP MFA can be set up after a user has signed in. This can be done when the following conditions are met:

  • MFA is marked as Optional or Required in your user pool.
  • TOTP is marked as an enabled MFA method in your user pool.

TOTP can be set up by calling the setUpTOTP and verifyTOTPSetup APIs in the Auth category.

Invoke the setUpTOTP API to generate a TOTPSetupDetails object which should be used to configure an Authenticator app like Microsoft Authenticator or Google Authenticator. TOTPSetupDetails provides a helper method called getSetupURI which generates a URI that can be used, for example, in a button to open the user's installed Authenticator app. For more advanced use cases, TOTPSetupDetails also contains a sharedSecret which can be used to either generate a QR code or be manually entered into an Authenticator app.

that contains the sharedSecret which will be used to either to generate a QR code or can be manually entered into an Authenticator app.

import { setUpTOTP } from 'aws-amplify/auth'; async function handleTOTPSetup() { try { const totpSetupDetails = await setUpTOTP(); const appName = 'my_app_name'; const setupUri = totpSetupDetails.getSetupUri(appName); // Open setupUri with an authenticator APP to retrieve an OTP code } catch (error) { console.log(error); } }
1import { setUpTOTP } from 'aws-amplify/auth';
2
3async function handleTOTPSetup() {
4 try {
5 const totpSetupDetails = await setUpTOTP();
6 const appName = 'my_app_name';
7 const setupUri = totpSetupDetails.getSetupUri(appName);
8 // Open setupUri with an authenticator APP to retrieve an OTP code
9 } catch (error) {
10 console.log(error);
11 }
12}

Once the Authenticator app is set up, the user must generate a TOTP code and provide it to the library. Pass the code to verifyTOTPSetup to complete the TOTP setup process.

import { verifyTOTPSetup } from 'aws-amplify/auth'; async function handleTOTPVerification(totpCode: string) { try { await verifyTOTPSetup({ code: totpCode }); } catch (error) { console.log(error); } }
1import { verifyTOTPSetup } from 'aws-amplify/auth';
2
3async function handleTOTPVerification(totpCode: string) {
4 try {
5 await verifyTOTPSetup({ code: totpCode });
6 } catch (error) {
7 console.log(error);
8 }
9}

After TOTP setup is complete, call updateMFAPreference to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

import { updateMFAPreference } from 'aws-amplify/auth'; async function handleUpdateMFAPreference() { try { await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' }); } catch (error) { console.log(error); } }
1import { updateMFAPreference } from 'aws-amplify/auth';
2
3async function handleUpdateMFAPreference() {
4 try {
5 await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' });
6 } catch (error) {
7 console.log(error);
8 }
9}

Recover from a lost TOTP device

If a user loses access to their TOTP device, they will need to contact an administrator to get help accessing their account. Based on the Cognito User Pool configuration, the administrator can use the AdminSetUserMFAPreference to either change the MFA preference to a different MFA method or to disable MFA for the user.

In a scenario where MFA is marked as "Required" in the Cognito User Pool and another MFA method is not set up, the administrator would need to first initiate an AdminUpdateUserAttributes call and update the user’s phone number attribute. Once this is complete, the administrator can continue changing the MFA preference to SMS as suggested above.

Set up a user's preferred MFA method

Fetch the current user's MFA preferences

Invoke the following API to get the current MFA preference and enabled MFA types, if any, for the current user.

import { fetchMFAPreference } from 'aws-amplify/auth'; async function handleFetchMFAPreference() { try { const output = await fetchMFAPreference(); console.log(`Enabled MFA types for the user: ${output.enabled}`); console.log(`Preferred MFA type for the user: ${output.preferred}`); } catch (error) { console.log(error); } }
1import { fetchMFAPreference } from 'aws-amplify/auth';
2
3async function handleFetchMFAPreference() {
4 try {
5 const output = await fetchMFAPreference();
6 console.log(`Enabled MFA types for the user: ${output.enabled}`);
7 console.log(`Preferred MFA type for the user: ${output.preferred}`);
8 } catch (error) {
9 console.log(error);
10 }
11}

Update the current user's MFA preferences

Invoke the following API to update the MFA preference for the current user.

Only one MFA method can be marked as preferred at a time. If the user has multiple MFA methods enabled and tries to mark more than one MFA method as preferred, the API will throw an error.

import { updateMFAPreference } from 'aws-amplify/auth'; async function handleUpdateMFAPreference() { try { await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' }); } catch (error) { console.log(error); } }
1import { updateMFAPreference } from 'aws-amplify/auth';
2
3async function handleUpdateMFAPreference() {
4 try {
5 await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' });
6 } catch (error) {
7 console.log(error);
8 }
9}

If multiple MFA methods are enabled for the user, the signIn API will return CONTINUE_SIGN_IN_WITH_MFA_SELECTION as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to confirmSignIn.

import { confirmSignIn, SignInOutput } from 'aws-amplify/auth'; function handleSignInNextSteps(output: SignInOutput) { const { nextStep } = output; switch (nextStep.signInStep) { // ... case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION': const allowedMFATypes = nextStep.allowedMFATypes; const mfaType = promptUserForMFAType(allowedMFATypes); case 'CONFIRM_SIGN_IN_WITH_SMS_CODE': // display user to enter otp code; break; case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE': // display user to enter otp code; break; // ... } } function promptUserForMFAType( allowedMFATypes?: ('SMS' | 'TOTP')[] ): 'SMS' | 'TOTP' { // Prompt user to select MFA type } async function handleMFASelection(mfaType: 'SMS' | 'TOTP') { try { const output = await confirmSignIn({ challengeResponse: mfaType }); handleSignInNextSteps(output); } catch (error) { console.log(error); } }
1import { confirmSignIn, SignInOutput } from 'aws-amplify/auth';
2
3function handleSignInNextSteps(output: SignInOutput) {
4 const { nextStep } = output;
5 switch (nextStep.signInStep) {
6 // ...
7 case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION':
8 const allowedMFATypes = nextStep.allowedMFATypes;
9 const mfaType = promptUserForMFAType(allowedMFATypes);
10 case 'CONFIRM_SIGN_IN_WITH_SMS_CODE':
11 // display user to enter otp code;
12 break;
13 case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE':
14 // display user to enter otp code;
15 break;
16 // ...
17 }
18}
19
20function promptUserForMFAType(
21 allowedMFATypes?: ('SMS' | 'TOTP')[]
22): 'SMS' | 'TOTP' {
23 // Prompt user to select MFA type
24}
25
26async function handleMFASelection(mfaType: 'SMS' | 'TOTP') {
27 try {
28 const output = await confirmSignIn({
29 challengeResponse: mfaType
30 });
31 handleSignInNextSteps(output);
32 } catch (error) {
33 console.log(error);
34 }
35}

Remember a device

Remembering a device is useful in conjunction with MFA because it allows the second factor requirement to be automatically met when your user signs in on that device and reduces friction in their sign-in experience.

Note: The device tracking and remembering features are not available if any of the following conditions are met:

  • the federated OAuth flow with Cognito User Pools or Hosted UI is used, or
  • the User Pool uses email/phone_number or alias sign-in method, or
  • when the signIn API uses the USER_PASSWORD_AUTH as the authFlowType.

Configure device tracking

You can enable the remembered device functionality in the Cognito User Pool console. To begin, go to your project directory and issue the command:

amplify auth console
1amplify auth console

Select the following option to open the Cognito User Pool console:

? Which Console User Pool
1? Which Console
2 User Pool

When the console opens, scroll down to the "Device Tracking" section and select the "Edit" button. This will render the following page allowing you to configure your preference for remembering a user's device.

Edit device tracking options list is shown, with the don't remember highlighted.

Choose either "Always remember" to remember a user's device by default or "User Opt-in" to give the user the ability to choose.

When MFA is enabled you will have the option to suppress the second factor during multi-factor authentication. Choose "Yes" if you want a remembered device to be used as a second factor mechanism.

Options for allow users to bypass MFA for trusted devices.

When you have made your selection(s), click "Save changes". You are now ready to start updating your code to manage remembered devices.

Learn more
Understand key terms used for tracking devices

There are differences to keep in mind when working with remembered, forgotten, and tracked devices.

  • Tracked: Every time the user signs in with a new device, the client is given the device key at the end of a successful authentication event. We use this device key to generate a salt and password verifier which is used to call the ConfirmDevice API. At this point, the device is considered to be "tracked". Once the device is in a tracked state, you can use the Amazon Cognito console to see the time it started to be tracked, last authentication time, and other information about that device.
  • Remembered: Remembered devices are also tracked. During user authentication, the device key and secret pair assigned to a remembered device is used to authenticate the device to verify that it is the same device that the user previously used to sign in.
  • Not Remembered: A not-remembered device is a tracked device where Cognito has been configured to require users to "Opt-in" to remember a device but the user has chosen not to remember the device. This use case is for users signing into their application from a device that they don't own.
  • Forgotten: In the event that you no longer want to remember or track devices, you can use the forgetDevice() API to remove devices from being both remembered and tracked.

Remember devices

You can remember devices using the following:

import { rememberDevice } from 'aws-amplify/auth'; export async function handleRememberDevice() { try { await rememberDevice(); } catch (error) { console.log(error); } }
1import { rememberDevice } from 'aws-amplify/auth';
2
3export async function handleRememberDevice() {
4 try {
5 await rememberDevice();
6 } catch (error) {
7 console.log(error);
8 }
9}

Forget devices

You can also forget devices but note that forgotten devices are still tracked.

import { forgetDevice } from 'aws-amplify/auth'; export async function handleForgetDevice() { try { await forgetDevice(); } catch (error) { console.log(error); } }
1import { forgetDevice } from 'aws-amplify/auth';
2
3export async function handleForgetDevice() {
4 try {
5 await forgetDevice();
6 } catch (error) {
7 console.log(error);
8 }
9}

Fetch devices

You can fetch a list of remembered devices by using the following:

import { fetchDevices } from 'aws-amplify/auth'; export async function handleFetchDevices() { try { const output = await fetchDevices(); console.log(output); } catch (error) { console.log(error); } }
1import { fetchDevices } from 'aws-amplify/auth';
2
3export async function handleFetchDevices() {
4 try {
5 const output = await fetchDevices();
6 console.log(output);
7 } catch (error) {
8 console.log(error);
9 }
10}

You can now set up devices to be remembered, forgotten, and fetched.

Conclusion

Congratulations! You finished the Manage MFA settings guide. In this guide, you set up and configured MFA for your users and provided them options on remembering their MFA preferences and devices.

Next steps

Now that you completed setting up multi-factor authentication you may also want to add additional customization. We recommend you learn more about:

The Auth category supports Multi-factor Authentication (MFA) for user sign-in flows. MFA is an extra layer of security used to make sure that users trying to gain access to an account are who they say they are. It requires users to provide additional information to verify their identity. The category supports the following MFA methods:

  • SMS
  • TOTP (Time-based One-time Password)

Set Up Backend Resources

Below are the steps you can use to set up MFA using SMS or TOTP with the Amplify CLI. The Amplify libraries are designed to work with MFA even if you have set up your Amazon Cognito resources separately.

When using Amplify CLI to set up backend resources, the following options are only available when starting a new project (via amplify add auth). You will not have access to these settings after creation (via amplify update).

  • Required MFA (i.e. Setting MFA to "ON")

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.

Refer to SMS sandbox.

For information about Amazon SNS pricing, see Worldwide SMS Pricing.

Run amplify add auth to create a new Cognito Auth resource, and follow the prompts below depending on how you want to integrate MFA into your flow.

Turning MFA "ON" will make it required for all users, while "Optional" will make it available to enable on a per-user basis.

SMS MFA

$ amplify add auth ? Do you want to use the default authentication and security configuration? # Manual configuration ... Answer as appropriate ? Multifactor authentication (MFA) user login options: # ON (Required for all logins, can not be enabled later) ? For user login, select the MFA types: # SMS Text Message ? Please specify an SMS authentication message: # Your authentication code is {####} ... Answer as appropriate Some next steps: "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
1$ amplify add auth
2
3? Do you want to use the default authentication and security configuration?
4# Manual configuration
5
6... Answer as appropriate
7
8? Multifactor authentication (MFA) user login options:
9# ON (Required for all logins, can not be enabled later)
10? For user login, select the MFA types:
11# SMS Text Message
12? Please specify an SMS authentication message:
13# Your authentication code is {####}
14
15... Answer as appropriate
16
17Some next steps:
18"amplify push" will build all your local backend resources and provision it in the cloud
19"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

TOTP MFA

$ amplify add auth ? Do you want to use the default authentication and security configuration? # Manual configuration ... Answer as appropriate ? Multifactor authentication (MFA) user login options: # ON (Required for all logins, can not be enabled later) ? For user login, select the MFA types: # Time-Based One-Time Password (TOTP) ... Answer as appropriate Some next steps: "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
1$ amplify add auth
2
3? Do you want to use the default authentication and security configuration?
4# Manual configuration
5
6... Answer as appropriate
7
8? Multifactor authentication (MFA) user login options:
9# ON (Required for all logins, can not be enabled later)
10? For user login, select the MFA types:
11# Time-Based One-Time Password (TOTP)
12
13... Answer as appropriate
14
15Some next steps:
16"amplify push" will build all your local backend resources and provision it in the cloud
17"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

Multi-factor authentication with SMS

Enabling SMS for MFA during Sign Up

You will need to pass phone_number as a user attribute to enable SMS MFA for your users during sign up. However, if the primary login mechanism for your Cognito resource is phone_number (without enabling username), then you do not need to pass it as an attribute.

AuthSignUpOptions options = AuthSignUpOptions.builder() .userAttribute(AuthUserAttributeKey.email(), "my@email.com") .userAttribute(AuthUserAttributeKey.phoneNumber(), "+18885551234") .build(); Amplify.Auth.signUp("username", "Password123", options, result -> Log.i("AuthQuickStart", "Result: " + result.toString()), error -> Log.e("AuthQuickStart", "Sign up failed", error) );
1AuthSignUpOptions options = AuthSignUpOptions.builder()
2 .userAttribute(AuthUserAttributeKey.email(), "my@email.com")
3 .userAttribute(AuthUserAttributeKey.phoneNumber(), "+18885551234")
4 .build();
5Amplify.Auth.signUp("username", "Password123", options,
6 result -> Log.i("AuthQuickStart", "Result: " + result.toString()),
7 error -> Log.e("AuthQuickStart", "Sign up failed", error)
8);

By default, you have to verify a user account after they sign up using the confirmSignUp API, which will send a one-time password to the user's phone number or email, depending on your Amazon Cognito configuration.

Amplify.Auth.confirmSignUp( "username", "the code you received", result -> Log.i("AuthQuickstart", result.isSignUpComplete() ? "Confirm signUp succeeded" : "Confirm sign up not complete"), error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.confirmSignUp(
2 "username",
3 "the code you received",
4 result -> Log.i("AuthQuickstart", result.isSignUpComplete() ? "Confirm signUp succeeded" : "Confirm sign up not complete"),
5 error -> Log.e("AuthQuickstart", error.toString())
6);

You will know the sign up flow is complete if you see the following in your console window:

Confirm signUp succeeded
1Confirm signUp succeeded

Handling SMS MFA challenge during Sign In

After a user signs in, if they have MFA enabled for their account, a challenge will be returned that you would need to call the confirmSignIn API where the user provides their confirmation code sent to their phone number.

Amplify.Auth.signIn( "username", "password", result -> { if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE && result.getNextStep().getCodeDeliveryDetails() != null) { String destination = result.getNextStep().getCodeDeliveryDetails().getDestination(); Log.d("SignIn", "SMS code sent to "+ destination); Log.d("SignIn", "Additional Info" + result.getNextStep().getAdditionalInfo()); // Prompt the user to enter the SMSMFA code they received // Then invoke `confirmSignIn` api with the code } }, error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.signIn(
2 "username",
3 "password",
4 result -> {
5 if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE &&
6 result.getNextStep().getCodeDeliveryDetails() != null) {
7 String destination = result.getNextStep().getCodeDeliveryDetails().getDestination();
8 Log.d("SignIn", "SMS code sent to "+ destination);
9 Log.d("SignIn", "Additional Info" + result.getNextStep().getAdditionalInfo());
10
11 // Prompt the user to enter the SMSMFA code they received
12 // Then invoke `confirmSignIn` api with the code
13 }
14 },
15 error -> Log.e("AuthQuickstart", error.toString())
16);

If MFA is ON or enabled for the user, you must call confirmSignIn with the OTP sent to their phone.

Amplify.Auth.confirmSignIn( "Confirmation code received via SMS", result -> Log.i("AuthQuickstart", result.toString()), error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.confirmSignIn(
2 "Confirmation code received via SMS",
3 result -> Log.i("AuthQuickstart", result.toString()),
4 error -> Log.e("AuthQuickstart", error.toString())
5);

After a user has been signed in, call updateMFAPreference to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); plugin.updateMFAPreference( MFAPreference.ENABLED, null, () -> Log.i("AuthQuickstart", "MFA preference updated successfully"), e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e) ); }
1if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
2 AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
3 plugin.updateMFAPreference(
4 MFAPreference.ENABLED,
5 null,
6 () -> Log.i("AuthQuickstart", "MFA preference updated successfully"),
7 e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e)
8 );
9 }

Multi-factor authentication with TOTP

You can use Time-based One-Time Password (TOTP) for multi-factor authentication (MFA) in your web or mobile applications. The Amplify Auth category includes support for TOTP setup and verification using authenticator apps, offering an integrated solution and enhanced security for your users. These apps, such as Google Authenticator, Microsoft Authenticator, have the TOTP algorithm built-in and work by using a shared secret key and the current time to generate short-lived, six digit passwords.

Setting up TOTP for a user

After you initiate a user sign in with the signIn API where a user is required to set up TOTP as an MFA method, the API call will return continueSignInWithTOTPSetup as a challenge and next step to handle in your app. You will get that challenge if the following conditions are met:

  • MFA is marked as Required in Cognito User Pool.
  • TOTP is enabled in the Cognito User Pool
  • User does not have TOTP MFA set up already.

The continueSignInWithTOTPSetup step signifies that the user must set up TOTP before they can sign in. The step returns an associated value of type TOTPSetupDetails which must be used to configure an authenticator app like Microsoft Authenticator or Google Authenticator. TOTPSetupDetails provides a helper method called getSetupURI which generates a URI that can be used, for example, in a button to open the user's installed authenticator app. For more advanced use cases, TOTPSetupDetails also contains a sharedSecret which can be used to either generate a QR code or be manually entered into an authenticator app.

Once the authenticator app is set up, the user can generate a TOTP code and provide it to the library to complete the sign in process.

Amplify.Auth.signIn( "username", "password", result -> { if (result.getNextStep().getSignInStep() == AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP && result.getNextStep().getTotpSetupDetails() != null) { Log.d("SignIn", "Received next step as continue sign in by setting up TOTP"); Log.d("SignIn", "Shared Secret is" + result.getNextStep().getTotpSetupDetails().getSharedSecret()); // appName parameter will help distinguish the account in the Authenticator app Uri setupURI = result.getNextStep().getTotpSetupDetails().getSetupURI("<Your_App_Name>"); Log.d("SignIn", "TOTP Setup URI: " + setupURI); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code } }, error -> Log.e("SignIn", error.toString()) );
1Amplify.Auth.signIn(
2 "username",
3 "password",
4 result -> {
5 if (result.getNextStep().getSignInStep() == AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP &&
6 result.getNextStep().getTotpSetupDetails() != null) {
7 Log.d("SignIn", "Received next step as continue sign in by setting up TOTP");
8 Log.d("SignIn", "Shared Secret is" + result.getNextStep().getTotpSetupDetails().getSharedSecret());
9 // appName parameter will help distinguish the account in the Authenticator app
10 Uri setupURI = result.getNextStep().getTotpSetupDetails().getSetupURI("<Your_App_Name>");
11
12 Log.d("SignIn", "TOTP Setup URI: " + setupURI);
13
14 // Prompt the user to enter the TOTP code generated in their authenticator app
15 // Then invoke `confirmSignIn` api with the code
16 }
17 },
18 error -> Log.e("SignIn", error.toString())
19);

The TOTP code can be obtained from the user via a text field or any other means. Once the user provides the TOTP code, call confirmSignIn with the TOTP code as the challengeResponse parameter.

Amplify.Auth.confirmSignIn( "OTP code from Authenticator App", result -> Log.i("AuthQuickstart", result.toString()), error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.confirmSignIn(
2 "OTP code from Authenticator App",
3 result -> Log.i("AuthQuickstart", result.toString()),
4 error -> Log.e("AuthQuickstart", error.toString())
5);

Enabling TOTP after a user is signed in

TOTP MFA can be set up after a user has signed in. This can be done when the following conditions are met:

  • MFA is marked as Optional or Required in the Cognito User Pool
  • TOTP is marked as an enabled MFA method in Cognito user pool

TOTP can be set up by calling the setUpTOTP and verifyTOTPSetup APIs in the Auth category.

Invoke the setUpTOTP API to generate a TOTPSetupDetails object which should be used to configure an Authenticator app like Microsoft Authenticator or Google Authenticator. TOTPSetupDetails provides a helper method called getSetupURI which generates a URI that can be used, for example, in a button to open the user's installed Authenticator app. For more advanced use cases, TOTPSetupDetails also contains a sharedSecret which can be used to either generate a QR code or be manually entered into an Authenticator app.

that contains the sharedSecret which will be used to either to generate a QR code or can be manually entered into an Authenticator app.

Amplify.Auth.setUpTOTP( result -> { Log.d("AuthQuickstart","Shared secret that will be used to set up TOTP in the authenticator app" + result.getSharedSecret()); // appName parameter will help distinguish the account in the Authenticator app Uri setupURI = result.getSetupURI("<Your_App_Name>>"); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `verifyTOTP` api with the code }, error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.setUpTOTP(
2 result -> {
3 Log.d("AuthQuickstart","Shared secret that will be used to set up TOTP in the authenticator app" + result.getSharedSecret());
4 // appName parameter will help distinguish the account in the Authenticator app
5 Uri setupURI = result.getSetupURI("<Your_App_Name>>");
6 // Prompt the user to enter the TOTP code generated in their authenticator app
7 // Then invoke `verifyTOTP` api with the code
8 },
9 error -> Log.e("AuthQuickstart", error.toString())
10);

Once the Authenticator app is set up, the user must generate a TOTP code and provide it to the library. Pass the code to verifyTOTPSetup to complete the TOTP setup process.

Amplify.Auth.verifyTOTPSetup( totpCodeFromAuthenticatorApp, () ->Log.d("AuthQuickstart","TOTP Verification Succeeded"), error -> Log.e("AuthQuickstart", error.toString()) );
1Amplify.Auth.verifyTOTPSetup(
2 totpCodeFromAuthenticatorApp,
3 () ->Log.d("AuthQuickstart","TOTP Verification Succeeded"),
4 error -> Log.e("AuthQuickstart", error.toString())
5);
if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); plugin.updateMFAPreference( null, MFAPreference.ENABLED, () -> Log.i("AuthQuickstart", "MFA preference updated successfully"), e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e) ); }
1if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
2 AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
3 plugin.updateMFAPreference(
4 null,
5 MFAPreference.ENABLED,
6 () -> Log.i("AuthQuickstart", "MFA preference updated successfully"),
7 e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e)
8 );
9 }

Recovering from a lost TOTP device

If a user loses access to their TOTP device, they would need to contact an administrator to help get access to their account. Based on the Cognito User Pool configuration, the administrator can use the AdminSetUserMFAPreference to either change the MFA preference to a different MFA method or to disable MFA for the user.

In a scenario where MFA is marked as Required in Cognito User Pool and another MFA method is not set up, the administrator would need to first initiate an AdminUpdateUserAttributes call and update the user’s phone number attribute. Once this is complete, the administrator can continue changing the MFA preference to SMS as suggested above.

Setting a user's preferred MFA option

Fetch the current user's MFA preferences

Invoke the following API to get the current MFA preference and enabled MFA types, if any, for the current user.

if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); plugin.fetchMFAPreference( preference -> Log.i( "AuthQuickStart", "Fetched MFA preference, enabled: " + preference.getEnabled() + ", preferred: " + preference.getPreferred() ), e -> Log.e("AuthQuickStart", "Failed to fetch MFA preference.", e) ); }
1if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
2 AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
3 plugin.fetchMFAPreference(
4 preference -> Log.i(
5 "AuthQuickStart",
6 "Fetched MFA preference, enabled: " + preference.getEnabled() + ", preferred: " + preference.getPreferred()
7 ),
8 e -> Log.e("AuthQuickStart", "Failed to fetch MFA preference.", e)
9 );
10}

Update the current user's MFA preferences

Invoke the following API to update the MFA preference for the current user.

Only one MFA method can be marked as preferred at a time. If the user has multiple MFA methods enabled and tries to mark more than one MFA method as preferred, the API will throw an error.

if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); plugin.updateMFAPreference( MFAPreference.DISABLED, // SMS Preference MFAPreference.PREFERRED, // TOTP Preference () -> Log.i( "AuthQuickStart", "Preference updated successfully"), e -> Log.e("AuthQuickStart", "Failed to update MFA preference.", e) ); }
1if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
2 AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
3 plugin.updateMFAPreference(
4 MFAPreference.DISABLED, // SMS Preference
5 MFAPreference.PREFERRED, // TOTP Preference
6 () -> Log.i( "AuthQuickStart", "Preference updated successfully"),
7 e -> Log.e("AuthQuickStart", "Failed to update MFA preference.", e)
8 );
9}

If multiple MFA methods are enabled for the user, the signIn API will return continueSignInWithMFASelection as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to confirmSignIn.

If the next step is CONFIRM_SIGN_IN_WITH_TOTP_CODE, Amplify Auth is waiting for the user to enter their TOTP code. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, your implementation must pass the value to Amplify Auth confirmSignIn API.

Note: the signIn result also includes an AuthCodeDeliveryDetails member. It includes additional information about the code delivery.

Amplify.Auth.confirmSignIn( "TOTP confirmation code", result -> { if (result.isSignedIn()) { Log.i("AuthQuickstart", "Confirm signIn succeeded"); } else { Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); // Switch on the next step to take appropriate actions. // If `signInResult.isSignedIn` is true, the next step // is 'done', and the user is now signed in. } }, error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) );
1Amplify.Auth.confirmSignIn(
2 "TOTP confirmation code",
3 result -> {
4 if (result.isSignedIn()) {
5 Log.i("AuthQuickstart", "Confirm signIn succeeded");
6 } else {
7 Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep());
8 // Switch on the next step to take appropriate actions.
9 // If `signInResult.isSignedIn` is true, the next step
10 // is 'done', and the user is now signed in.
11 }
12 },
13 error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error)
14);