---
title: "Multi-factor authentication"
section: "build-a-backend/auth/concepts"
platforms: ["android", "angular", "flutter", "javascript", "nextjs", "react", "react-native", "swift", "vue"]
gen: 2
last-updated: "2026-04-24T07:46:51.000Z"
url: "https://docs.amplify.aws/react/build-a-backend/auth/concepts/multi-factor-authentication/"
---

Amplify Auth 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. Amplify Auth supports MFA with time-based one-time passwords (TOTP), text messages (SMS), and email. 

In this guide we will review how you can set up MFA with each of these methods and the discuss tradeoffs between them to help you choose the right setup for your application. We will also review how to set up MFA to remember a device and reduce sign-in friction for your users.

> **Warning:** **MFA and passwordless cannot be used together.** Amazon Cognito does not support enabling both MFA and passwordless sign-in (including passkeys, SMS OTP, and email OTP) for the same user. If a user has MFA configured, passwordless sign-in options will not be available. For more details, see the [Amazon Cognito MFA prerequisites](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa.html#user-pool-settings-mfa-prerequisites).

## Configure multi-factor authentication

Use `defineAuth` to enable MFA for your app. The example below is setting up MFA with TOTP but not SMS as you can see that the phone number is not a required attribute. 
- If you plan to use SMS for MFA, then the `phoneNumber` attribute must be marked as required in your `userAttributes`. Note that if you have `loginWith.phone` as `true` this attribute will automatically be marked as required.
- If you plan to use email for MFA, then the `email` attribute must also be `true` must be marked as required in your `userAttributes`. Note that if you have `loginWith.email` as `true` this attribute will automatically be marked as required.

```ts title="amplify/auth/resource.ts"
import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
    email: true
  },
// highlight-start
  multifactor: {
    mode: 'OPTIONAL',
    totp: true,
    email: true,
  },
  senders: {
    email: {
      fromEmail: 'noreply@example.com',
      fromName: 'My App',
    },
  },
// highlight-end
  userAttributes: {
    phoneNumber: { 
      required: true
    }
  }
});
```

When MFA is `REQUIRED` with SMS in your backend auth resource, you will need to pass the phone number during sign-up API call. If you are using the `email` or `username` as the primary sign-in mechanism, you will need to pass the `phone_number` attribute as a user attribute. 

Similarly, when MFA is `REQUIRED` with email as your delivery mechanism, you will need to pass an email address during the sign-up API call. If you are using `phoneNumber` or `username` as the primary sign-in mechanism, you will need to pass the `email` attribute as a user attribute. 

This configuration may change depending on the combination of MFA methods enabled in your user pool.

### Understand your MFA options

When enabling MFA you will have two key decisions to make:

- **MFA enforcement:** As part of this setup you will determine how MFA is enforced. If you require MFA by setting MFA mode to `REQUIRED`, all your users will need to complete MFA to sign in. If you keep it `OPTIONAL`, your users will have the choice whether to enable MFA or not for their account.
- **MFA methods:** You will also specify which MFA method you are using: TOTP (Time-based One-time Password), SMS (text message), email, or any combination thereof. We recommend that you use TOTP-based MFA as it is more secure and you can reserve SMS or email for account recovery.

<Accordion title='Compare TOTP, SMS, and EMAIL MFA methods' headingLevel='4' eyebrow='Learn more'>

|  | Time-based One-time Password (TOTP) | Short Message Service (SMS) | Email |
| --- | --- | --- | --- |
| **Description** | Generates a short-lived numeric code for user authentication that includes a shared secret key and current time using an authenticator app. | Generates a one-time code shared via text message that is entered with other credentials for user authentication. | Generates a one-time code sent to the user's registered email address. The user must access the email and enter the code to complete the authentication process. |
| **Benefits** | More secure than SMS since the code is generated locally and not transmitted over a network. TOTP also works without cell service as long as the TOTP app is installed. | Easy to set up with a user-provided phone number and is familiar to users as a common authentication method. | Email is a widely used and familiar communication channel that requires no additional hardware or software requirements on the user's end. |
| **Constraints** | Requires an app to generate codes and adds to the initial setup of an account. Codes also expire quickly and must be used promptly after it is generated. | SMS requires cell service and can include an additional cost for the user. Although rare, SMS messages can also be intercepted. | Depends on the availability and reliability of email services. Although rare, emails can be intercepted or accounts can become compromised. |

</details>

<!-- Platform: angular, javascript, nextjs, react, react-native, vue, android -->
If multiple MFA methods are enabled for the user, and none are set as preferred, 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`.
<!-- /Platform -->

<!-- Platform: angular, javascript, nextjs, react-native, react, vue -->
```ts
import { confirmSignIn, type 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':
			// prompt user to enter otp code delivered via SMS
			break;
		case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE':
			// prompt user to enter otp code from their authenticator app
			break;
		case 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE':
			// prompt user to enter otp code delivered via EMAIL
			break;
		// ...
	}
}

type MfaType = 'SMS' | 'TOTP' | 'EMAIL';

function promptUserForMFAType(allowedMFATypes?: MfaType[]): MfaType {
	// Prompt user to select MFA type
}

async function handleMFASelection(mfaType: MfaType) {
	try {
		const output = await confirmSignIn({
			challengeResponse: mfaType,
		});
		handleSignInNextSteps(output);
	} catch (error) {
		console.log(error);
	}
}
```
<!-- /Platform -->

<!-- Platform: android -->
```kotlin
fun signIn(username: String, password: String) {
    val result: AuthSignInResult
    try {
        result = Amplify.Auth.signIn(username, password)
    } catch (e: AuthException) {
        Log.e("MFASelection", "Failed to sign in", e)
    }
    handleNextSignInStep(username, result.nextStep)
}

fun handleNextSignInStep(
    username: String,
    nextStep: AuthNextSignInStep
) {
    when (nextStep.signInStep) {
        AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION -> {
            // User has multiple MFA methods and none are preferred
            promptUserForMfaType(nextStep.allowedMFATypes)
        }
        else -> {
            // Handle other SignInSteps
        }
    }
}

fun promptUserForMfaType(mfaTypes: Set<MFAType>?) {
    // Prompt user to select one of the passed-in MFA Types
    // Then invoke Amplify.Auth.confirmSignIn(selectedMfaType.challengeResponse)
}
```
<!-- /Platform -->

<!-- Platform: swift, flutter -->
If multiple MFA methods are enabled for the user, and none are set as preferred, 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`.
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> _handleSignInResult(SignInResult result) async {
  switch (result.nextStep.signInStep) {
    // ···
    case AuthSignInStep.continueSignInWithMfaSelection:
      final allowedMfaTypes = result.nextStep.allowedMfaTypes!;
      final selection = await _promptUserPreference(allowedMfaTypes);
      return _handleMfaSelection(selection);
    // ···
  }
}

Future<MfaType> _promptUserPreference(Set<MfaType> allowedTypes) async {
  // ···
}

Future<void> _handleMfaSelection(MfaType selection) async {
  try {
    final result = await Amplify.Auth.confirmSignIn(
      confirmationValue: selection.confirmationValue,
    );
    return _handleSignInResult(result);
  } on AuthException catch (e) {
    safePrint('Error sending MFA selection: ${e.message}');
  }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func signIn(username: String, password: String) async {
    do {
        let signInResult = try await Amplify.Auth.signIn(username: username, password: password)
        switch signInResult.nextStep {

        case .continueSignInWithMFASelection(let allowedMFATypes):
            print("Received next step as continue sign in by selecting MFA type")
            print("Allowed MFA types \(allowedMFATypes)")
            
            // Prompt the user to select the MFA type they want to use
            // Then invoke `confirmSignIn` api with the MFA type
        
        default:
            
            // Use has successfully signed in to the app
            print("Step: \(signInResult.nextStep)")
        }
    } catch let error as AuthError{
        print ("Sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}

func confirmSignInWithTOTPAsMFASelection() async {
    do {
        let signInResult = try await Amplify.Auth.confirmSignIn(
            challengeResponse: MFAType.totp.challengeResponse)

        if case .confirmSignInWithTOTPCode = signInResult.nextStep {
            print("Received next step as confirm sign in with TOTP")
        }

    } catch {
        print("Confirm sign in failed \(error)")
    }
}
```
<!-- /Platform -->

## Multi-factor authentication with SMS
<!-- Platform: react -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: swift -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/swift/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: flutter -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/flutter/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: android -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/android/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

Once you have setup SMS as your second layer of authentication with MFA as shown above, your users will get an authentication code via a text message to complete sign-in after they sign in with their username and password.

> **Warning:** **Warning:** In order to send SMS authentication codes, you must [request an origination number](https://docs.aws.amazon.com/pinpoint/latest/userguide/settings-request-number.html). [Learn more about configuring your auth resource for production workloads](/[platform]/build-a-backend/auth/moving-to-production/).

### Enable SMS 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 sign-in mechanism for your Cognito resource is `phone_number` (without enabling `username`), then you do not need to pass it as an attribute.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { signUp } from 'aws-amplify/auth';

await signUp({
  username: "hello@mycompany.com",
  password: "hunter2",
  options: {
    userAttributes: {
      phone_number: "+15555555555",
      email: "hello@mycompany.com",
    },
  },
});
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> signUpWithPhoneVerification(
  String username,
  String password,
) async {
  await Amplify.Auth.signUp(
    username: username,
    password: password,
    options: SignUpOptions(
      userAttributes: <AuthUserAttributeKey, String>{
        // ... if required
        AuthUserAttributeKey.email: 'test@example.com',
        AuthUserAttributeKey.phoneNumber: '+18885551234',
      },
    ),
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func signUp(username: String, password: String, email: String, phonenumber: String) async {
    do {
        let signUpResult = try await Amplify.Auth.signUp(
            username: username,
            password: password,
            options: .init(userAttributes: [
                AuthUserAttribute(.email, value: email), 
                AuthUserAttribute(.phoneNumber, value: phonenumber)
            ])
        )
        if case let .confirmUser(deliveryDetails, _, userId) = signUpResult.nextStep {
            print("Delivery details \(String(describing: deliveryDetails)) for userId: \(String(describing: userId)))")
        } else {
            print("SignUp Complete")
        }
    } catch let error as AuthError {
        print("An error occurred while registering a user \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { confirmSignUp } from 'aws-amplify/auth';

await confirmSignUp({
  username: "hello@mycompany.com",
  confirmationCode: "123456",
})
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> confirmSignUpPhoneVerification(
  String username,
  String otpCode,
) async {
  await Amplify.Auth.confirmSignUp(
    username: username,
    confirmationCode: otpCode,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func confirmSignUp(for username: String, with confirmationCode: String) async {
    do {
        let confirmSignUpResult = try await Amplify.Auth.confirmSignUp(
            for: username,
            confirmationCode: confirmationCode
        )
        print("Confirm sign up result completed: \(confirmSignUpResult.isSignUpComplete)")
    } catch let error as AuthError {
        print("An error occurred while confirming sign up \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

### Manage SMS MFA 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.

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

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { confirmSignIn } from 'aws-amplify/auth';

await confirmSignIn({
  challengeResponse: "123456"
});
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> confirmSignInPhoneVerification(String otpCode) async {
  await Amplify.Auth.confirmSignIn(
    confirmationValue: otpCode,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func confirmSignIn() async {
    do {
        let signInResult = try await Amplify.Auth.confirmSignIn(
            challengeResponse: "<confirmation code received via SMS>")
        print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
    } catch let error as AuthError {
        print("Confirm sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { updateMFAPreference } from 'aws-amplify/auth';

await updateMFAPreference({ sms: 'PREFERRED' });
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> updateMfaPreferences() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  await cognitoPlugin.updateMfaPreference(
    sms: MfaPreference.enabled, // or .preferred
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func updateMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let smsMfaPreference: MFAPreference = .preferred

    try await authCognitoPlugin?.updateMFAPreference(
        sms: smsMfaPreference)
}
```
<!-- /Platform -->

## Multi-factor authentication with TOTP

<!-- Platform: react -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: swift -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/swift/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: flutter -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/flutter/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: android -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/android/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

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

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
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.

```ts
import { signIn, SignInOutput } from 'aws-amplify/auth';

const output = await signIn({
  username: "hello@mycompany.com",
  password: "hunter2"
});

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;
  // ...
}
```
<!-- /Platform -->

<!-- Platform: swift, flutter -->
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 your user pool.
- TOTP is enabled in your 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.
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> signInUser(String username, String password) async {
  try {
    final result = await Amplify.Auth.signIn(
      username: username,
      password: password,
    );
    return _handleSignInResult(result);
  } on AuthException catch (e) {
    safePrint('Error signing in: ${e.message}');
  }
}

Future<void> _handleSignInResult(SignInResult result) async {
  switch (result.nextStep.signInStep) {
    // ···
    case AuthSignInStep.continueSignInWithTotpSetup:
      final totpSetupDetails = result.nextStep.totpSetupDetails!;
      final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp');
      safePrint('Open URI to complete setup: $setupUri');
    // ···
  }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func signIn(username: String, password: String) async {
    do {
        let signInResult = try await Amplify.Auth.signIn(
            username: username,
            password: password
        )

        if case .continueSignInWithTOTPSetup(let setUpDetails) = signInResult.nextStep {

            print("Received next step as continue sign in by setting up TOTP")
            print("Shared secret that will be used to set up TOTP in the authenticator app \(setUpDetails.sharedSecret)")

            // appName parameter will help distinguish the account in the Authenticator app
            let setupURI = try setUpDetails.getSetupURI(appName: "<Your_App_Name>>")

            print("TOTP Setup URI: \(setupURI)")

            // Prompt the user to enter the TOTP code generated in their authenticator app
            // Then invoke `confirmSignIn` api with the code

        }
    } catch let error as AuthError {
        print("Sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { confirmSignIn } from 'aws-amplify/auth';

await confirmSignIn({
  challengeResponse: "123456"
});
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> confirmTotpUser(String totpCode) async {
  try {
    final result = await Amplify.Auth.confirmSignIn(
      confirmationValue: totpCode,
    );
    return _handleSignInResult(result);
  } on AuthException catch (e) {
    safePrint('Error confirming TOTP code: ${e.message}');
  }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func confirmSignIn() async {
    do {
        let signInResult = try await Amplify.Auth.confirmSignIn(
            challengeResponse: "<confirmation code received from Authenticator app>")
        print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
    } catch let error as AuthError {
        print("Confirm sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { updateMFAPreference } from 'aws-amplify/auth';

await updateMFAPreference({ totp: 'PREFERRED' });
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> updateMfaPreferences() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  await cognitoPlugin.updateMfaPreference(
    totp: MfaPreference.preferred,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func updateMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let totpMfaPreference: MFAPreference = .preferred

    try await authCognitoPlugin?.updateMFAPreference(
        totp: totpMfaPreference)
}
```
<!-- /Platform -->

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

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { setUpTOTP } from 'aws-amplify/auth';

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
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> setUpTotp() async {
  try {
    final totpSetupDetails = await Amplify.Auth.setUpTotp();
    final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp');
    safePrint('Open URI to complete setup: $setupUri');
  } on AuthException catch (e) {
    safePrint('An error occurred setting up TOTP: $e');
  }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func setUpTOTP() async {
    do {
        let setUpDetails = try await Amplify.Auth.setUpTOTP()

        print("Received next step as continue sign in by setting up TOTP")
        print("Shared secret that will be used to set up TOTP in the authenticator app \(setUpDetails.sharedSecret)")

        // appName parameter will help distinguish the account in the Authenticator app
        let setupURI = try setUpDetails.getSetupURI(appName: "<Your_App_Name>>")

        print("TOTP Setup URI: \(setupURI)")

        // Prompt the user to enter the TOTP code generated in their authenticator app
        // Then invoke `confirmSignIn` api with the code
    } catch {
        print("TOTP Setup Initiation failed \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { verifyTOTPSetup } from 'aws-amplify/auth';

await verifyTOTPSetup({ code: "123456" });
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> verifyTotpSetup(String totpCode) async {
  try {
    await Amplify.Auth.verifyTotpSetup(totpCode);
  } on AuthException catch (e) {
    safePrint('An error occurred verifying TOTP: $e');
  }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func verifyTOTPSetup(totpCodeFromAuthenticatorApp: String) async {
    do {
        try await Amplify.Auth.verifyTOTPSetup(
            code: totpCodeFromAuthenticatorApp)
    } catch {
        print("TOTP Setup Verification failed \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue, android -->
```ts
import { updateMFAPreference } from 'aws-amplify/auth';

await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' });
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> updateMfaPreferences() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  await cognitoPlugin.updateMfaPreference(
    sms: MfaPreference.enabled,
    totp: MfaPreference.preferred,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func updateMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let smsMfaPreference: MFAPreference = .enabled
    let totpMfaPreference: MFAPreference = .preferred

    try await authCognitoPlugin?.updateMFAPreference(
        sms: smsMfaPreference,
        totp: totpMfaPreference)
}
```
<!-- /Platform -->

### Recover from a lost TOTP device

> **Warning:** 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](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminSetUserMFAPreference.html) 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`](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUpdateUserAttributes.html) 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.

## Multi-factor authentication with EMAIL
To enable email MFA, set `email: true` in your multifactor configuration and configure an email sender.

> **Warning:** To permit users to sign in with email MFA, your user pool must have the following configuration options:
> 
> - You have the Plus or Essentials feature plan in your user pool.
> - Your user pool sends email messages with your own Amazon SES resources.
> 
> For more details, see [Amazon Cognito email MFA configuration](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa-sms-email-message.html).

```ts title="amplify/auth/resource.ts"
import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
    email: true
  },
  multifactor: {
    mode: 'OPTIONAL',
    email: true,
  },
  // BE SURE TO PICK A RECOVERY OPTION APPROPRIATE FOR YOUR APPLICATION.
  accountRecovery: "EMAIL_AND_PHONE_WITHOUT_MFA",
  senders: {
    email: {
      fromEmail: 'noreply@example.com',
      fromName: 'My App',
    },
  },
});
```

<!-- Platform: swift -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/swift/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: flutter -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/flutter/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

<!-- Platform: android -->
> **Info:** If you are using the [Authenticator component](https://ui.docs.amplify.aws/android/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation.
<!-- /Platform -->

Once you have setup email as your second layer of authentication with MFA as shown above, your users will get an authentication code via email to complete sign-in after they sign in with their username and password.

> **Warning:** In order to send email authentication codes, the following prerequisites must be met:
> - Cognito must be configured to send emails using [Amazon Simple Email Service (Amazon SES)](/[platform]/build-a-backend/auth/moving-to-production/#email).
> - If account recovery is enabled in Cognito, the delivery method for recovery messages cannot be set to `Email only`

### Enable EMAIL MFA during sign-up

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

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { signUp } from 'aws-amplify/auth';

await signUp({
  username: "+15555555555",
  password: "hunter2",
  options: {
    userAttributes: {
      email: "hello@mycompany.com",
    },
  },
});
```
<!-- /Platform -->

<!-- Platform: android -->

#### [Java]

```java
ArrayList<AuthUserAttribute> attributes = new ArrayList<>();
attributes.add(new AuthUserAttribute(AuthUserAttributeKey.email(), "my@email.com"));
attributes.add(new AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+15551234567"));

Amplify.Auth.signUp(
    "username",
    "Password123",
    AuthSignUpOptions.builder().userAttributes(attributes).build(),
    result -> Log.i("AuthQuickstart", result.toString()),
    error -> Log.e("AuthQuickstart", error.toString())
);
```

#### [Kotlin - Callbacks]

```kotlin
val attrs = mapOf(
    AuthUserAttributeKey.email() to "my@email.com",
    AuthUserAttributeKey.phoneNumber() to "+15551234567"
)
val options = AuthSignUpOptions.builder()
    .userAttributes(attrs.map { AuthUserAttribute(it.key, it.value) })
    .build()
Amplify.Auth.signUp("username", "Password123", options,
    { Log.i("AuthQuickstart", "Sign up result = $it") },
    { Log.e("AuthQuickstart", "Sign up failed", it) }
)
```

#### [Kotlin - Coroutines]

```kotlin
val attrs = mapOf(
    AuthUserAttributeKey.email() to "my@email.com",
    AuthUserAttributeKey.phoneNumber() to "+15551234567"
)
val options = AuthSignUpOptions.builder()
    .userAttributes(attrs.map { AuthUserAttribute(it.key, it.value) })
    .build()
try {
    val result = Amplify.Auth.signUp("username", "Password123", options)
    Log.i("AuthQuickstart", "Sign up OK: $result")
} catch (error: AuthException) {
    Log.e("AuthQuickstart", "Sign up failed", error)
}
```

#### [RxJava]

```java
ArrayList<AuthUserAttribute> attributes = new ArrayList<>();
attributes.add(new AuthUserAttribute(AuthUserAttributeKey.email(), "my@email.com"));
attributes.add(new AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+15551234567"));

RxAmplify.Auth.signUp(
    "username",
    "Password123",
    AuthSignUpOptions.builder().userAttributes(attributes).build())
    .subscribe(
        result -> Log.i("AuthQuickstart", result.toString()),
        error -> Log.e("AuthQuickstart", error.toString())
    );
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> signUpWithEmailVerification(
  String username,
  String password,
) async {
  await Amplify.Auth.signUp(
    username: username,
    password: password,
    options: SignUpOptions(
      userAttributes: <AuthUserAttributeKey, String>{
        AuthUserAttributeKey.email: 'test@example.com',
        // ... if required
        AuthUserAttributeKey.phoneNumber: '+18885551234',
      },
    ),
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func signUp(username: String, password: String, email: String, phonenumber: String) async {
    do {
        let signUpResult = try await Amplify.Auth.signUp(
            username: username,
            password: password,
            options: .init(userAttributes: [
                AuthUserAttribute(.email, value: email), 
                AuthUserAttribute(.phoneNumber, value: phonenumber)
            ])
        )
        if case let .confirmUser(deliveryDetails, _, userId) = signUpResult.nextStep {
            print("Delivery details \(String(describing: deliveryDetails)) for userId: \(String(describing: userId)))")
        } else {
            print("SignUp Complete")
        }
    } catch let error as AuthError {
        print("An error occurred while registering a user \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

By default, you have to verify a user account after they sign up using the `confirmSignUp` API. Following the initial `signUp` request, a one-time passcode will be sent to the user's phone number or email, depending on your Amazon Cognito configuration.

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { confirmSignUp } from 'aws-amplify/auth';

await confirmSignUp({
  username: "+15555555555",
  confirmationCode: "123456",
})
```
<!-- /Platform -->

<!-- Platform: android -->

#### [Java]

```java
 try {
      Amplify.Auth.confirmSignUp(
             "username",
             "confirmation code",
             result -> Log.i("AuthQuickstart", "Confirm signUp result completed: " + result.isSignUpComplete()),
             error -> Log.e("AuthQuickstart", "An error occurred while confirming sign up: " + error)
      );
} catch (Exception error) {
   Log.e("AuthQuickstart", "unexpected error: " + error);
}
```

#### [Kotlin - Callbacks]

```kotlin
 try {
      Amplify.Auth.confirmSignUp(
          "username",
          "confirmation code",
          { result ->
              Log.i("AuthQuickstart", "Confirm signUp result completed: ${result.isSignUpComplete}")
          }
      ) { error ->
          Log.e("AuthQuickstart", "An error occurred while confirming sign up: $error")
      }
} catch (error: Exception) {
    Log.e("AuthQuickstart", "unexpected error: $error")
}
```

#### [Kotlin - Coroutines]

```kotlin
try {
     val result = Amplify.Auth.confirmSignUp(
         "username",
         "confirmation code"
     )
     Log.i("AuthQuickstart", "Confirm signUp result completed: ${result.isSignUpComplete}")
} catch (error: Exception) {
   Log.e("AuthQuickstart", "unexpected error: $error")
}
```

#### [RxJava]

```java
RxAmplify.Auth.confirmSignUp(
        "username",
        "confirmation code").subscribe(
        result -> Log.i("AuthQuickstart", "Confirm signUp result completed: " + result.isSignUpComplete()),
        error -> Log.e("AuthQuickstart", "An error occurred while confirming sign up: " + error)
);
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> confirmSignUpEmailVerification(
  String username,
  String otpCode,
) async {
  await Amplify.Auth.confirmSignUp(
    username: username,
    confirmationCode: otpCode,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func confirmSignUp(for username: String, with confirmationCode: String) async {
    do {
        let confirmSignUpResult = try await Amplify.Auth.confirmSignUp(
            for: username,
            confirmationCode: confirmationCode
        )
        print("Confirm sign up result completed: \(confirmSignUpResult.isSignUpComplete)")
    } catch let error as AuthError {
        print("An error occurred while confirming sign up \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

### Manage EMAIL MFA during sign-in

After a user signs in, if they have MFA enabled for their account, a challenge will be issued that requires calling the `confirmSignIn` API with the user provided confirmation code sent to their email address.

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

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { confirmSignIn } from 'aws-amplify/auth';

await confirmSignIn({
  challengeResponse: "123456"
});
```
<!-- /Platform -->
<!-- Platform: android -->

#### [Java]

```java
try {
      Amplify.Auth.confirmSignIn(
            "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 `result.isSignedIn` is true, the next step
                    // is 'done', and the user is now signed in.
                }
            },
            error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error)
    );
} catch (Exception error) {
    Log.e("AuthQuickstart", "Unexpected error: " + error);
}
```

#### [Kotlin - Callbacks]

```kotlin
try {
    Amplify.Auth.confirmSignIn(
          "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.nextStep}")
                  // Switch on the next step to take appropriate actions.
                  // If `result.isSignedIn` is true, the next step
                  // is 'done', and the user is now signed in.
              }
          }
    ) { error -> Log.e("AuthQuickstart", "Confirm sign in failed: $error")}
} catch (error: Exception) {
    Log.e("AuthQuickstart", "Unexpected error: $error")
}
```

#### [Kotlin - Coroutines]

```kotlin
try {
    val result = Amplify.Auth.confirmSignIn(
        "confirmation code"
    )
    if (result.isSignedIn) {
        Log.i("AuthQuickstart", "Confirm signIn succeeded")
    } else {
        Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: ${result.nextStep}"
        )
        // Switch on the next step to take appropriate actions.
        // If `result.isSignedIn` is true, the next step
        // is 'done', and the user is now signed in.
    }
} catch (error: Exception) {
    Log.e("AuthQuickstart", "Unexpected error: $error")
}
```

#### [RxJava]

```java

RxAmplify.Auth.confirmSignIn(
                "confirmation code").subscribe(
                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 `result.isSignedIn` is true, the next step
                        // is 'done', and the user is now signed in.
                    }
                },
                error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error)
        );
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> confirmSignInEmailVerification(String otpCode) async {
  await Amplify.Auth.confirmSignIn(
    confirmationValue: otpCode,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func confirmSignIn() async {
    do {
        let signInResult = try await Amplify.Auth.confirmSignIn(
            challengeResponse: "<confirmation code received via email>")
        print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
    } catch let error as AuthError {
        print("Confirm sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}
```
<!-- /Platform -->

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.

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { updateMFAPreference } from 'aws-amplify/auth';

await updateMFAPreference({ email: 'PREFERRED' });
```
<!-- /Platform -->

<!-- Platform: android -->

#### [Java]

```java
if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
            AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
            plugin.updateMFAPreference(
                    MFAPreference.DISABLED, // SMS Preference
                    MFAPreference.DISABLED, // TOTP Preference
                    MFAPreference.PREFERRED, // Email Preference
                    () -> Log.i("AuthQuickstart", "MFA preference updated successfully"),
                    e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e)
            );
        }
```

#### [Kotlin]

```kotlin
if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") is AWSCognitoAuthPlugin) {
    val plugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin
    plugin?.updateMFAPreference(
            MFAPreference.DISABLED, // SMS Preference
            MFAPreference.DISABLED, // TOTP Preference
            MFAPreference.PREFERRED, // Email Preference
        { Log.i("AuthQuickstart", "MFA preference updated successfully" ) },
        { e: AuthException? -> Log.e("AuthQuickstart", "Failed to update MFA preference", e) }
    )
}
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> updateMfaPreferences() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  await cognitoPlugin.updateMfaPreference(
    email: MfaPreference.enabled, // or .preferred
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func updateMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let emailMfaPreference: MFAPreference = .preferred

    try await authCognitoPlugin?.updateMFAPreference(
        email: emailMfaPreference)
}
```
<!-- /Platform -->

## Set up a user's preferred MFA method

Depending on your user pool configuration, it's possible that multiple MFA options may be available to a given user. In order to avoid requiring your users to select an MFA method each time they sign-in to your application, Amplify provides two utility APIs to manage an individual user's MFA preferences.

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

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { fetchMFAPreference } from 'aws-amplify/auth';

const { enabled, preferred } = await fetchMFAPreference();
```
<!-- /Platform -->

<!-- Platform: android -->

#### [Java]

```java
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)
    );
}
```

#### [Kotlin]

```kotlin
val cognitoAuthPlugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin
cognitoAuthPlugin?.fetchMFAPreference(
    { Log.d("AuthQuickStart", "Fetched MFA preference, enabled: ${it.enabled}, preferred: ${it.preferred}") },
    { Log.e("AuthQuickStart", "Failed to fetch MFA preference.", it) }
)
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> getCurrentMfaPreference() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  final currentPreference = await cognitoPlugin.fetchMfaPreference();
  safePrint('Enabled MFA types for user: ${currentPreference.enabled}');
  safePrint('Preferred MFA type for user: ${currentPreference.preferred}');
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func getMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let result = try await authCognitoPlugin?.fetchMFAPreference()

    print("Enabled MFA types: \(result?.enabled)")
    print("Preferred MFA type: \(result?.preferred)")
}
```
<!-- /Platform -->

### Update the current user's MFA preferences

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

> **Warning:** 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.

<!-- Platform: angular, javascript, nextjs, react, vue -->
```ts
import { updateMFAPreference } from 'aws-amplify/auth';

await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' });
```
<!-- /Platform -->

<!-- Platform: android -->

#### [Java]

```java
if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) {
    AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin");
    plugin.updateMFAPreference(
        MFAPreference.DISABLED, // SMS Preference
        MFAPreference.PREFERRED, // TOTP Preference
        null // Email Preference
        () -> Log.i( "AuthQuickStart", "Preference updated successfully"),
        e -> Log.e("AuthQuickStart", "Failed to update MFA preference.", e)
    );
}
```

#### [Kotlin - Callbacks]

```kotlin
val cognitoAuthPlugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin
cognitoAuthPlugin?.updateMFAPreference(
    MFAPreference.DISABLED, // SMS Preference
    MFAPreference.PREFERRED, // TOTP Preference
    null, // Email Preference
    { Log.d("AuthQuickStart", "Preference updated successfully") },
    { Log.e("AuthQuickStart", "Failed to update MFA preference.", it) }
)
```

<!-- /Platform -->

<!-- Platform: flutter -->
```dart
Future<void> updateMfaPreferences() async {
  final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

  await cognitoPlugin.updateMfaPreference(
    sms: MfaPreference.enabled,
    totp: MfaPreference.preferred,
  );
}
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
func updateMFAPreferences() async throws {
    let authCognitoPlugin = try Amplify.Auth.getPlugin(
        for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin

    let smsMfaPreference: MFAPreference = .enabled
    let totpMfaPreference: MFAPreference = .preferred

    try await authCognitoPlugin?.updateMFAPreference(
        sms: smsMfaPreference,
        totp: totpMfaPreference)
}
```
<!-- /Platform -->

## 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. By default, this feature is turned off.

> **Info:** **Note:** The [device tracking and remembering](https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/) 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
> - when the `signIn` API uses the `USER_PASSWORD_AUTH` as the `authFlowType`.

### Configure device tracking

You can configure device tracking with `deviceTracking` construct.

```ts title="amplify/backend.ts"
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';

const backend = defineBackend({
  auth,
  data
});

const { cfnUserPool } = backend.auth.resources.cfnResources;

cfnUserPool.addPropertyOverride('DeviceConfiguration', {
  ChallengeRequiredOnNewDevice: true,
  DeviceOnlyRememberedOnUserPrompt: false
});
```

<Accordion title='Understand key terms used for tracking devices' headingLevel='4' eyebrow='Learn more'>

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.

</details>

## Next steps

- [Learn how to sign-up with MFA enabled](/[platform]/frontend/auth/sign-in/#with-multi-factor-auth-enabled)
- [Learn how to manage user devices](/[platform]/build-a-backend/auth/manage-users/manage-devices/)
