Repository: govuk-one-login/authentication-api
Pull request #6163: AUT-3485: Add scenarios for MfaMethodsCreateHandler
Author: login: alhcomer
Created: 2025-03-17T14:34:40Z | Merged: 2025-03-19T13:30:12Z

## What
Implements the success scenario P2 and error scenarios E2, E5, and E6

- P2: Migrated user with sms mfa only adds an auth app mfa (no OTP presented)
- E2: Migrated user with auth app mfa only attempts to add a second auth app mfa
- E5: Already have 2 MFAs
- E6: Client attempts to add an SMS MFA with the same number as an existing SMS MFA

Error scenario E1 and success scenarios P1 and P3 were implemented in a [previous pull request](https://github.com/govuk-one-login/authentication-api/pull/6121) (albeit for P1 and P3 without OTP handling).

This leaves error scenarios E3 and E4 unimplemented at the time of writing as they are based around the user having provided an invalid OTP when OTP handling has not yet been implemented.

See scenarios [here](https://govukverify.atlassian.net/browse/AUT-3485)

## How to review

1.  Code review
2. Deploy to dev environment with account in required state and make test requests to the mfa-methods-create seeing expected responses.




Diff:
diff --git a/account-management-api/src/main/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandler.java b/account-management-api/src/main/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandler.java
index 084be535b39..4b429410de0 100644
--- a/account-management-api/src/main/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandler.java
+++ b/account-management-api/src/main/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandler.java
@@ -4,17 +4,18 @@
 import com.amazonaws.services.lambda.runtime.RequestHandler;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
+import io.vavr.control.Either;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.ThreadContext;
 import uk.gov.di.authentication.shared.entity.ErrorResponse;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodCreateRequest;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodData;
-import uk.gov.di.authentication.shared.exceptions.InvalidPriorityIdentifierException;
 import uk.gov.di.authentication.shared.serialization.Json;
 import uk.gov.di.authentication.shared.services.ConfigurationService;
 import uk.gov.di.authentication.shared.services.DynamoService;
 import uk.gov.di.authentication.shared.services.SerializationService;
+import uk.gov.di.authentication.shared.services.mfa.MfaCreateFailureReason;
 import uk.gov.di.authentication.shared.services.mfa.MfaMethodsService;
 
 import static uk.gov.di.authentication.shared.helpers.ApiGatewayResponseHelper.generateApiGatewayProxyErrorResponse;
@@ -85,12 +86,29 @@ public APIGatewayProxyResponseEvent mfaMethodsHandler(
 
             LOG.info("Update MFA POST called with: {}", mfaMethodCreateRequest.mfaMethod());
 
-            MfaMethodData mfaMethodData =
+            Either<MfaCreateFailureReason, MfaMethodData> addBackupMfaResult =
                     mfaMethodsService.addBackupMfa(email, mfaMethodCreateRequest.mfaMethod());
 
-            return generateApiGatewayProxyResponse(200, mfaMethodData, true);
-
-        } catch (Json.JsonException | InvalidPriorityIdentifierException e) {
+            if (addBackupMfaResult.isLeft()) {
+                switch (addBackupMfaResult.getLeft()) {
+                    case INVALID_PRIORITY_IDENTIFIER -> {
+                        return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1001);
+                    }
+                    case BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST -> {
+                        return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1068);
+                    }
+                    case PHONE_NUMBER_ALREADY_EXISTS -> {
+                        return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1069);
+                    }
+                    case AUTH_APP_EXISTS -> {
+                        return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1070);
+                    }
+                }
+            }
+
+            return generateApiGatewayProxyResponse(200, addBackupMfaResult.get(), true);
+
+        } catch (Json.JsonException e) {
             return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1001);
         }
     }
diff --git a/account-management-api/src/test/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandlerTest.java b/account-management-api/src/test/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandlerTest.java
index 89a4ef9ffc2..606e8d0bdc5 100644
--- a/account-management-api/src/test/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandlerTest.java
+++ b/account-management-api/src/test/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandlerTest.java
@@ -3,6 +3,7 @@
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
 import com.google.gson.JsonParser;
+import io.vavr.control.Either;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
@@ -11,15 +12,17 @@
 import uk.gov.di.authentication.shared.entity.ErrorResponse;
 import uk.gov.di.authentication.shared.entity.PriorityIdentifier;
 import uk.gov.di.authentication.shared.entity.UserProfile;
+import uk.gov.di.authentication.shared.entity.mfa.AuthAppMfaDetail;
 import uk.gov.di.authentication.shared.entity.mfa.MFAMethodType;
+import uk.gov.di.authentication.shared.entity.mfa.MfaDetail;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodCreateRequest;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodData;
 import uk.gov.di.authentication.shared.entity.mfa.SmsMfaDetail;
-import uk.gov.di.authentication.shared.exceptions.InvalidPriorityIdentifierException;
 import uk.gov.di.authentication.shared.helpers.ClientSessionIdHelper;
 import uk.gov.di.authentication.shared.helpers.PersistentIdHelper;
 import uk.gov.di.authentication.shared.services.ConfigurationService;
 import uk.gov.di.authentication.shared.services.DynamoService;
+import uk.gov.di.authentication.shared.services.mfa.MfaCreateFailureReason;
 import uk.gov.di.authentication.shared.services.mfa.MfaMethodsService;
 import uk.gov.di.authentication.sharedtest.logging.CaptureLoggingExtension;
 
@@ -33,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static uk.gov.di.authentication.sharedtest.helper.RequestEventHelper.identityWithSourceIp;
@@ -53,6 +57,8 @@ class MFAMethodsCreateHandlerTest {
     private static final String TEST_PUBLIC_SUBJECT = "test-public-subject";
     private static final String TEST_EMAIL = "test@test.com";
     private static final String TEST_SMS_MFA_ID = "35c7940d-be5f-4b31-95b7-0eedc42929b9";
+    private static final String TEST_AUTH_APP_ID = "f2ec40f3-9e63-496c-a0a5-a3bdafee868b";
+    private static final String TEST_CREDENTIAL = "ZZ11BB22CC33DD44EE55FF66GG77HH88II99JJ00";
     private static final ConfigurationService configurationService =
             mock(ConfigurationService.class);
     private static final MfaMethodsService mfaMethodsService = mock(MfaMethodsService.class);
@@ -67,26 +73,27 @@ void setUp() {
         handler =
                 new MFAMethodsCreateHandler(configurationService, mfaMethodsService, dynamoService);
         when(configurationService.getAwsRegion()).thenReturn("eu-west-2");
+        reset(mfaMethodsService);
     }
 
     @Test
-    void shouldReturn200AndCreateMfaSmsMfaMethod() throws InvalidPriorityIdentifierException {
+    void shouldReturn200AndCreateMfaSmsMfaMethod() {
         when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
                 .thenReturn(Optional.of(userProfile));
         when(userProfile.getEmail()).thenReturn(TEST_EMAIL);
         when(mfaMethodsService.addBackupMfa(any(), any()))
                 .thenReturn(
-                        new MfaMethodData(
-                                TEST_SMS_MFA_ID,
-                                PriorityIdentifier.BACKUP,
-                                true,
-                                new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER)));
+                        Either.right(
+                                new MfaMethodData(
+                                        TEST_SMS_MFA_ID,
+                                        PriorityIdentifier.BACKUP,
+                                        true,
+                                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER))));
 
         var event =
                 generateApiGatewayEvent(
                         PriorityIdentifier.BACKUP,
-                        MFAMethodType.SMS,
-                        TEST_PHONE_NUMBER,
+                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER),
                         TEST_PUBLIC_SUBJECT);
 
         var result = handler.handleRequest(event, context);
@@ -121,6 +128,60 @@ void shouldReturn200AndCreateMfaSmsMfaMethod() throws InvalidPriorityIdentifierE
         assertEquals(expectedResponseParsedToString, result.getBody());
     }
 
+    @Test
+    void shouldReturn200AndCreateAuthAppMfa() {
+        when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
+                .thenReturn(Optional.of(userProfile));
+        when(userProfile.getEmail()).thenReturn(TEST_EMAIL);
+        when(mfaMethodsService.addBackupMfa(any(), any()))
+                .thenReturn(
+                        Either.right(
+                                new MfaMethodData(
+                                        TEST_AUTH_APP_ID,
+                                        PriorityIdentifier.BACKUP,
+                                        true,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP, TEST_CREDENTIAL))));
+
+        var event =
+                generateApiGatewayEvent(
+                        PriorityIdentifier.BACKUP,
+                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, TEST_CREDENTIAL),
+                        TEST_PUBLIC_SUBJECT);
+
+        var result = handler.handleRequest(event, context);
+
+        ArgumentCaptor<MfaMethodCreateRequest.MfaMethod> mfaMethodCaptor =
+                ArgumentCaptor.forClass(MfaMethodCreateRequest.MfaMethod.class);
+
+        verify(mfaMethodsService).addBackupMfa(eq(TEST_EMAIL), mfaMethodCaptor.capture());
+        var capturedRequest = mfaMethodCaptor.getValue();
+
+        assertEquals(
+                new AuthAppMfaDetail(MFAMethodType.AUTH_APP, TEST_CREDENTIAL),
+                capturedRequest.method());
+        assertEquals(PriorityIdentifier.BACKUP, capturedRequest.priorityIdentifier());
+
+        assertThat(result, hasStatus(200));
+        var expectedResponse =
+                format(
+                        """
+                {
+                  "mfaIdentifier": "%s",
+                  "priorityIdentifier": "BACKUP",
+                  "methodVerified": true,
+                  "method": {
+                    "mfaMethodType": "AUTH_APP",
+                    "credential": "%s"
+                  }
+                }
+                """,
+                        TEST_AUTH_APP_ID, TEST_CREDENTIAL);
+        var expectedResponseParsedToString =
+                JsonParser.parseString(expectedResponse).getAsJsonObject().toString();
+        assertEquals(expectedResponseParsedToString, result.getBody());
+    }
+
     @Test
     void shouldReturn400IfRequestIsMadeInEnvWhereApiNotEnabled() {
         when(configurationService.isMfaMethodManagementApiEnabled()).thenReturn(false);
@@ -128,8 +189,7 @@ void shouldReturn400IfRequestIsMadeInEnvWhereApiNotEnabled() {
         var event =
                 generateApiGatewayEvent(
                         PriorityIdentifier.BACKUP,
-                        MFAMethodType.AUTH_APP,
-                        TEST_PHONE_NUMBER,
+                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, TEST_CREDENTIAL),
                         TEST_PUBLIC_SUBJECT);
 
         var result = handler.handleRequest(event, context);
@@ -161,9 +221,8 @@ void shouldReturn404WhenUserProfileNotFoundForPublicSubject() {
         var event =
                 generateApiGatewayEvent(
                         PriorityIdentifier.BACKUP,
-                        MFAMethodType.AUTH_APP,
-                        TEST_PHONE_NUMBER,
-                        "incorrect-subject");
+                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, TEST_CREDENTIAL),
+                        TEST_PUBLIC_SUBJECT);
 
         var result = handler.handleRequest(event, context);
 
@@ -176,8 +235,7 @@ void shouldReturn400WhenJsonIsInvalid() {
         var event =
                 generateApiGatewayEvent(
                         PriorityIdentifier.BACKUP,
-                        MFAMethodType.AUTH_APP,
-                        TEST_PHONE_NUMBER,
+                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER),
                         TEST_PUBLIC_SUBJECT);
         event.setBody("Invalid JSON");
         when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
@@ -189,23 +247,89 @@ void shouldReturn400WhenJsonIsInvalid() {
         assertThat(result, hasJsonBody(ErrorResponse.ERROR_1001));
     }
 
+    @Test
+    void shouldReturn400WhenMfaMethodServiceReturnsBackupAndDefaultExistError() {
+        var event =
+                generateApiGatewayEvent(
+                        PriorityIdentifier.BACKUP,
+                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER),
+                        TEST_PUBLIC_SUBJECT);
+        when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
+                .thenReturn(Optional.of(userProfile));
+        when(mfaMethodsService.addBackupMfa(any(), any()))
+                .thenReturn(
+                        Either.left(
+                                MfaCreateFailureReason.BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST));
+
+        var result = handler.handleRequest(event, context);
+
+        assertThat(result, hasStatus(400));
+        assertThat(result, hasJsonBody(ErrorResponse.ERROR_1068));
+    }
+
+    @Test
+    void shouldReturn400WhenMfaMethodServiceReturnsSmsMfaAlreadyExistsError() {
+        var event =
+                generateApiGatewayEvent(
+                        PriorityIdentifier.BACKUP,
+                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER),
+                        TEST_PUBLIC_SUBJECT);
+        when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
+                .thenReturn(Optional.of(userProfile));
+        when(mfaMethodsService.addBackupMfa(any(), any()))
+                .thenReturn(Either.left(MfaCreateFailureReason.PHONE_NUMBER_ALREADY_EXISTS));
+
+        var result = handler.handleRequest(event, context);
+
+        assertThat(result, hasStatus(400));
+        assertThat(result, hasJsonBody(ErrorResponse.ERROR_1069));
+    }
+
+    @Test
+    void shouldReturn400WhenMfaMethodServiceReturnsAuthAppAlreadyExistsError() {
+        var event =
+                generateApiGatewayEvent(
+                        PriorityIdentifier.BACKUP,
+                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, TEST_CREDENTIAL),
+                        TEST_PUBLIC_SUBJECT);
+        when(dynamoService.getOptionalUserProfileFromPublicSubject(TEST_PUBLIC_SUBJECT))
+                .thenReturn(Optional.of(userProfile));
+        when(mfaMethodsService.addBackupMfa(any(), any()))
+                .thenReturn(Either.left(MfaCreateFailureReason.AUTH_APP_EXISTS));
+
+        var result = handler.handleRequest(event, context);
+
+        assertThat(result, hasStatus(400));
+        assertThat(result, hasJsonBody(ErrorResponse.ERROR_1070));
+    }
+
     private APIGatewayProxyRequestEvent generateApiGatewayEvent(
-            PriorityIdentifier priorityIdentifier,
-            MFAMethodType mfaMethodType,
-            String phoneNumber,
-            String publicSubject) {
-        var body =
-                format(
-                        """
+            PriorityIdentifier priorityIdentifier, MfaDetail mfaDetail, String publicSubject) {
+
+        String body =
+                mfaDetail instanceof SmsMfaDetail
+                        ? format(
+                                """
                                 { "mfaMethod": {
                                     "priorityIdentifier": "%s",
                                     "method": {
-                                        "mfaMethodType": "%s",
+                                        "mfaMethodType": "SMS",
                                         "phoneNumber": "%s" }
                                     }
                                 }
                                """,
-                        priorityIdentifier, mfaMethodType, phoneNumber);
+                                priorityIdentifier, ((SmsMfaDetail) mfaDetail).phoneNumber())
+                        : format(
+                                """
+                                { "mfaMethod": {
+                                    "priorityIdentifier": "%s",
+                                    "method": {
+                                        "mfaMethodType": "AUTH_APP",
+                                        "credential": "%s" }
+                                    }
+                                }
+                               """,
+                                priorityIdentifier, ((AuthAppMfaDetail) mfaDetail).credential());
 
         var event = new APIGatewayProxyRequestEvent();
 
diff --git a/account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java b/account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java
index f4b1c6c2dca..d827c476135 100644
--- a/account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java
+++ b/account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java
@@ -3,6 +3,8 @@
 import com.google.gson.JsonParser;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
 import uk.gov.di.accountmanagement.lambda.MFAMethodsCreateHandler;
 import uk.gov.di.authentication.shared.entity.ErrorResponse;
 import uk.gov.di.authentication.shared.entity.PriorityIdentifier;
@@ -18,6 +20,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Stream;
 
 import static java.lang.String.format;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -30,8 +34,30 @@ class MFAMethodsCreateHandlerIntegrationTest extends ApiGatewayHandlerIntegratio
     private static final String TEST_EMAIL = "test@email.com";
     private static final String TEST_PASSWORD = "test-password";
     private static final String TEST_PHONE_NUMBER = "07123123123";
+    private static final String TEST_PHONE_NUMBER_TWO = "07987987987";
     private static final String TEST_CREDENTIAL = "ZZ11BB22CC33DD44EE55FF66GG77HH88II99JJ00";
     private static String TEST_PUBLIC_SUBJECT;
+    private static final MFAMethod defaultPrioritySms =
+            MFAMethod.smsMfaMethod(
+                    true,
+                    true,
+                    TEST_PHONE_NUMBER,
+                    PriorityIdentifier.DEFAULT,
+                    UUID.randomUUID().toString());
+    private static final MFAMethod backupPrioritySms =
+            MFAMethod.smsMfaMethod(
+                    true,
+                    true,
+                    TEST_PHONE_NUMBER_TWO,
+                    PriorityIdentifier.BACKUP,
+                    UUID.randomUUID().toString());
+    private static final MFAMethod defaultPriorityAuthApp =
+            MFAMethod.authAppMfaMethod(
+                    TEST_CREDENTIAL,
+                    true,
+                    true,
+                    PriorityIdentifier.DEFAULT,
+                    UUID.randomUUID().toString());
 
     @BeforeEach
     void setUp() {
@@ -51,7 +77,7 @@ public boolean isMfaMethodManagementApiEnabled() {
 
     @Test
     void shouldReturn200AndMfaMethodDataWhenAuthAppUserAddsSmsMfa() {
-        userStore.addAuthAppMethod(TEST_EMAIL, true, true, TEST_CREDENTIAL);
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPriorityAuthApp);
         var response =
                 makeRequest(
                         Optional.of(
@@ -71,24 +97,61 @@ void shouldReturn200AndMfaMethodDataWhenAuthAppUserAddsSmsMfa() {
 
         List<MFAMethod> mfaMethods = userStore.getMfaMethod(TEST_EMAIL);
 
-        assertTrue(
+        var retrievedSmsMethod =
                 mfaMethods.stream()
                         .filter(
                                 mfaMethod ->
                                         mfaMethod
                                                 .getMfaMethodType()
                                                 .equals(MFAMethodType.SMS.getValue()))
-                        .filter(
-                                mfaMethod ->
-                                        mfaMethod.getMfaIdentifier().equals(extractedMfaIdentifier))
-                        .filter(mfaMethod -> mfaMethod.getDestination().equals(TEST_PHONE_NUMBER))
+                        .findFirst()
+                        .get();
+
+        assertEquals(extractedMfaIdentifier, retrievedSmsMethod.getMfaIdentifier());
+        assertEquals(PriorityIdentifier.BACKUP.toString(), retrievedSmsMethod.getPriority());
+        assertEquals(TEST_PHONE_NUMBER, retrievedSmsMethod.getDestination());
+        assertTrue(retrievedSmsMethod.isEnabled());
+        assertTrue(retrievedSmsMethod.isMethodVerified());
+    }
+
+    @Test
+    void shouldReturn200AndMfaMethodDataWhenSmsUserAddsAuthAppMfa() {
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPrioritySms);
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP, TEST_CREDENTIAL))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT));
+        assertEquals(200, response.getStatusCode());
+
+        String extractedMfaIdentifier =
+                JsonParser.parseString(response.getBody())
+                        .getAsJsonObject()
+                        .get("mfaIdentifier")
+                        .getAsString();
+
+        List<MFAMethod> mfaMethods = userStore.getMfaMethod(TEST_EMAIL);
+
+        var retrievedAuthAppMethod =
+                mfaMethods.stream()
                         .filter(
                                 mfaMethod ->
                                         mfaMethod
-                                                .getPriority()
-                                                .equals(PriorityIdentifier.BACKUP.toString()))
-                        .filter(MFAMethod::isMethodVerified)
-                        .anyMatch(m -> true));
+                                                .getMfaMethodType()
+                                                .equals(MFAMethodType.AUTH_APP.getValue()))
+                        .findFirst()
+                        .get();
+
+        assertEquals(TEST_CREDENTIAL, retrievedAuthAppMethod.getCredentialValue());
+        assertEquals(extractedMfaIdentifier, retrievedAuthAppMethod.getMfaIdentifier());
+        assertEquals(PriorityIdentifier.BACKUP.toString(), retrievedAuthAppMethod.getPriority());
+        assertTrue(retrievedAuthAppMethod.isEnabled());
+        assertTrue(retrievedAuthAppMethod.isMethodVerified());
     }
 
     @Test
@@ -121,6 +184,87 @@ void shouldReturn400AndBadRequestWhenPublicSubjectIsNotInUserStore() {
         assertThat(response, hasJsonBody(ErrorResponse.ERROR_1056));
     }
 
+    private static Stream<MFAMethodType> invalidMfaMethodTypes() {
+        return Stream.of(MFAMethodType.EMAIL, MFAMethodType.NONE);
+    }
+
+    @ParameterizedTest
+    @MethodSource("invalidMfaMethodTypes")
+    void shouldReturn400AndBadRequestWhenMfaMethodTypeIsInvalid(MFAMethodType mfaMethodType) {
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new SmsMfaDetail(mfaMethodType, TEST_PHONE_NUMBER))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT),
+                        Collections.emptyMap());
+        assertEquals(400, response.getStatusCode());
+        assertThat(response, hasJsonBody(ErrorResponse.ERROR_1001));
+    }
+
+    @Test
+    void shouldReturn400ErrorResponseWhenAddingMfaAfterMfaCountLimitReached() {
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPrioritySms);
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, backupPrioritySms);
+
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT),
+                        Collections.emptyMap());
+
+        assertEquals(400, response.getStatusCode());
+        assertThat(response, hasJsonBody(ErrorResponse.ERROR_1068));
+    }
+
+    @Test
+    void shouldReturn400ErrorResponseWhenSmsUserAddsSmsMfaWithSamePhoneNumber() {
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPrioritySms);
+
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new SmsMfaDetail(MFAMethodType.SMS, TEST_PHONE_NUMBER))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT),
+                        Collections.emptyMap());
+
+        assertEquals(400, response.getStatusCode());
+        assertThat(response, hasJsonBody(ErrorResponse.ERROR_1069));
+    }
+
+    @Test
+    void shouldReturn400ErrorResponseWhenAuthAppAddsSecondAuthApp() {
+        userStore.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPriorityAuthApp);
+
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP,
+                                                "AA99BB88CC77DD66EE55FF44GG33HH22II11JJ00"))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT),
+                        Collections.emptyMap());
+
+        assertEquals(400, response.getStatusCode());
+        assertThat(response, hasJsonBody(ErrorResponse.ERROR_1070));
+    }
+
     private static String constructRequestBody(
             PriorityIdentifier priorityIdentifier, MfaDetail mfaDetail) {
         return format(
diff --git a/integration-tests/src/test/java/uk/gov/di/authentication/services/MfaMethodsServiceIntegrationTest.java b/integration-tests/src/test/java/uk/gov/di/authentication/services/MfaMethodsServiceIntegrationTest.java
index ba0e455ea7e..3d02dc013ef 100644
--- a/integration-tests/src/test/java/uk/gov/di/authentication/services/MfaMethodsServiceIntegrationTest.java
+++ b/integration-tests/src/test/java/uk/gov/di/authentication/services/MfaMethodsServiceIntegrationTest.java
@@ -17,8 +17,8 @@
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodCreateRequest;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodData;
 import uk.gov.di.authentication.shared.entity.mfa.SmsMfaDetail;
-import uk.gov.di.authentication.shared.exceptions.InvalidPriorityIdentifierException;
 import uk.gov.di.authentication.shared.services.ConfigurationService;
+import uk.gov.di.authentication.shared.services.mfa.MfaCreateFailureReason;
 import uk.gov.di.authentication.shared.services.mfa.MfaDeleteFailureReason;
 import uk.gov.di.authentication.shared.services.mfa.MfaMethodsService;
 import uk.gov.di.authentication.sharedtest.extensions.UserStoreExtension;
@@ -29,8 +29,6 @@
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static uk.gov.di.authentication.shared.services.mfa.MfaMethodsService.HARDCODED_APP_MFA_ID;
 import static uk.gov.di.authentication.shared.services.mfa.MfaMethodsService.HARDCODED_SMS_MFA_ID;
@@ -38,6 +36,7 @@
 class MfaMethodsServiceIntegrationTest {
 
     private static final String TEST_EMAIL = "joe.bloggs@example.com";
+    private static final String TEST_PHONE_NUMBER = "07123123123";
     private static final String PHONE_NUMBER = "+44123456789";
     private static final String AUTH_APP_CREDENTIAL = "some-credential";
     MfaMethodsService mfaMethodsService = new MfaMethodsService(ConfigurationService.getInstance());
@@ -218,16 +217,16 @@ void shouldReturnMultipleMethodsWhenTheyExist(List<MFAMethod> mfaMethods) {
         @Nested
         class AddBackupMfaTests {
             @Test
-            void authAppUserShouldSuccessfullyAddSmsMfaInPost()
-                    throws InvalidPriorityIdentifierException {
-                userStoreExtension.addAuthAppMethod(TEST_EMAIL, true, true, AUTH_APP_CREDENTIAL);
+            void authAppUserShouldSuccessfullyAddSmsMfaInPost() {
+                userStoreExtension.addMfaMethodSupportingMultiple(
+                        TEST_EMAIL, defaultPriorityAuthApp);
                 SmsMfaDetail smsMfaDetail = new SmsMfaDetail(MFAMethodType.SMS, PHONE_NUMBER);
 
                 MfaMethodCreateRequest.MfaMethod mfaMethod =
                         new MfaMethodCreateRequest.MfaMethod(
                                 PriorityIdentifier.BACKUP, smsMfaDetail);
 
-                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, mfaMethod);
+                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, mfaMethod).get();
 
                 List<MFAMethod> mfaMethods = userStoreExtension.getMfaMethod(TEST_EMAIL);
                 boolean smsMethodExists =
@@ -244,6 +243,34 @@ void authAppUserShouldSuccessfullyAddSmsMfaInPost()
                 assertEquals(smsMfaDetail, result.method());
             }
 
+            @Test
+            void smsUserShouldSuccessfullyAddAuthAppMfa() {
+                userStoreExtension.addMfaMethodSupportingMultiple(TEST_EMAIL, defaultPrioritySms);
+
+                AuthAppMfaDetail authAppMfaDetail =
+                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, AUTH_APP_CREDENTIAL);
+
+                MfaMethodCreateRequest.MfaMethod mfaMethod =
+                        new MfaMethodCreateRequest.MfaMethod(
+                                PriorityIdentifier.BACKUP, authAppMfaDetail);
+
+                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, mfaMethod).get();
+
+                List<MFAMethod> mfaMethods = userStoreExtension.getMfaMethod(TEST_EMAIL);
+                boolean authAppMethodExists =
+                        mfaMethods.stream()
+                                .anyMatch(
+                                        method ->
+                                                method.getMfaMethodType()
+                                                        .equals(MFAMethodType.AUTH_APP.getValue()));
+
+                assertTrue(authAppMethodExists);
+                assertDoesNotThrow(() -> UUID.fromString(result.mfaIdentifier()));
+                assertEquals(PriorityIdentifier.BACKUP, result.priorityIdentifier());
+                assertTrue(result.methodVerified());
+                assertEquals(authAppMfaDetail, result.method());
+            }
+
             @Test
             void shouldErrorWhenPriorityIdentifierIsDefault() {
                 userStoreExtension.addAuthAppMethod(TEST_EMAIL, true, true, AUTH_APP_CREDENTIAL);
@@ -254,23 +281,58 @@ void shouldErrorWhenPriorityIdentifierIsDefault() {
                                 new MfaMethodCreateRequest.MfaMethod(
                                         PriorityIdentifier.DEFAULT, smsMfaDetail));
 
-                assertThrows(
-                        InvalidPriorityIdentifierException.class,
-                        () -> mfaMethodsService.addBackupMfa(TEST_EMAIL, request.mfaMethod()));
+                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, request.mfaMethod());
+
+                assertEquals(MfaCreateFailureReason.INVALID_PRIORITY_IDENTIFIER, result.getLeft());
             }
 
             @Test
-            void shouldReturnNullWhenAuthAppMfaAdded() throws InvalidPriorityIdentifierException {
-                AuthAppMfaDetail authAppMfaDetail =
-                        new AuthAppMfaDetail(MFAMethodType.AUTH_APP, AUTH_APP_CREDENTIAL);
+            void shouldReturnAtMaximumMfaErrorWhenAddingBackupWithTwoExistingMfaMethods() {
+                userStoreExtension.addMfaMethodSupportingMultiple(EMAIL, defaultPriorityAuthApp);
+                userStoreExtension.addMfaMethodSupportingMultiple(EMAIL, backupPrioritySms);
+
+                MfaMethodCreateRequest request =
+                        new MfaMethodCreateRequest(
+                                new MfaMethodCreateRequest.MfaMethod(
+                                        PriorityIdentifier.BACKUP,
+                                        new SmsMfaDetail(MFAMethodType.SMS, PHONE_NUMBER)));
+
+                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, request.mfaMethod());
+
+                assertEquals(
+                        MfaCreateFailureReason.BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST,
+                        result.getLeft());
+            }
+
+            @Test
+            void shouldReturnPhoneNumberAlreadyExistsErrorWhenSmsMfaUserAddsBackupWithSameNumber() {
+                userStoreExtension.addMfaMethodSupportingMultiple(EMAIL, defaultPrioritySms);
 
                 MfaMethodCreateRequest request =
                         new MfaMethodCreateRequest(
                                 new MfaMethodCreateRequest.MfaMethod(
-                                        PriorityIdentifier.BACKUP, authAppMfaDetail));
+                                        PriorityIdentifier.BACKUP,
+                                        new SmsMfaDetail(MFAMethodType.SMS, PHONE_NUMBER)));
 
                 var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, request.mfaMethod());
-                assertNull(result);
+
+                assertEquals(MfaCreateFailureReason.PHONE_NUMBER_ALREADY_EXISTS, result.getLeft());
+            }
+
+            @Test
+            void shouldReturnAuthAppAlreadyExistsErrorWhenAuthAppMfaUserAddsSecondAuthAppMfa() {
+                userStoreExtension.addMfaMethodSupportingMultiple(EMAIL, defaultPriorityAuthApp);
+
+                MfaMethodCreateRequest request =
+                        new MfaMethodCreateRequest(
+                                new MfaMethodCreateRequest.MfaMethod(
+                                        PriorityIdentifier.BACKUP,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP, AUTH_APP_CREDENTIAL)));
+
+                var result = mfaMethodsService.addBackupMfa(TEST_EMAIL, request.mfaMethod());
+
+                assertEquals(MfaCreateFailureReason.AUTH_APP_EXISTS, result.getLeft());
             }
         }
     }
diff --git a/shared/src/main/java/uk/gov/di/authentication/shared/entity/ErrorResponse.java b/shared/src/main/java/uk/gov/di/authentication/shared/entity/ErrorResponse.java
index 1b1445e4894..fad35b9cf55 100644
--- a/shared/src/main/java/uk/gov/di/authentication/shared/entity/ErrorResponse.java
+++ b/shared/src/main/java/uk/gov/di/authentication/shared/entity/ErrorResponse.java
@@ -78,7 +78,10 @@ public enum ErrorResponse {
     ERROR_1064(1064, "Unknown mfa method type"),
     ERROR_1065(1065, "Mfa method not found"),
     ERROR_1066(1066, "Cannot delete default priority mfa method"),
-    ERROR_1067(1067, "Cannot delete mfa method for non-migrated user");
+    ERROR_1067(1067, "Cannot delete mfa method for non-migrated user"),
+    ERROR_1068(1068, "MFA method count limit reached"),
+    ERROR_1069(1069, "SMS MFA with same number already exists"),
+    ERROR_1070(1070, "AUTH APP MFA already exists");
 
     private int code;
 
diff --git a/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaCreateFailureReason.java b/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaCreateFailureReason.java
new file mode 100644
index 00000000000..149e1eba526
--- /dev/null
+++ b/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaCreateFailureReason.java
@@ -0,0 +1,8 @@
+package uk.gov.di.authentication.shared.services.mfa;
+
+public enum MfaCreateFailureReason {
+    INVALID_PRIORITY_IDENTIFIER,
+    BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST,
+    PHONE_NUMBER_ALREADY_EXISTS,
+    AUTH_APP_EXISTS
+}
diff --git a/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaMethodsService.java b/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaMethodsService.java
index 01357699697..e673826950b 100644
--- a/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaMethodsService.java
+++ b/shared/src/main/java/uk/gov/di/authentication/shared/services/mfa/MfaMethodsService.java
@@ -4,12 +4,12 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import uk.gov.di.authentication.shared.entity.*;
+import uk.gov.di.authentication.shared.entity.mfa.AuthAppMfaDetail;
 import uk.gov.di.authentication.shared.entity.mfa.MFAMethod;
 import uk.gov.di.authentication.shared.entity.mfa.MFAMethodType;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodCreateRequest;
 import uk.gov.di.authentication.shared.entity.mfa.MfaMethodData;
 import uk.gov.di.authentication.shared.entity.mfa.SmsMfaDetail;
-import uk.gov.di.authentication.shared.exceptions.InvalidPriorityIdentifierException;
 import uk.gov.di.authentication.shared.exceptions.UnknownMfaTypeException;
 import uk.gov.di.authentication.shared.services.AuthenticationService;
 import uk.gov.di.authentication.shared.services.ConfigurationService;
@@ -134,14 +134,32 @@ private List<MfaMethodData> getMfaMethodsForNonMigratedUser(
         }
     }
 
-    public MfaMethodData addBackupMfa(String email, MfaMethodCreateRequest.MfaMethod mfaMethod)
-            throws InvalidPriorityIdentifierException {
+    public Either<MfaCreateFailureReason, MfaMethodData> addBackupMfa(
+            String email, MfaMethodCreateRequest.MfaMethod mfaMethod) {
         if (mfaMethod.priorityIdentifier() == PriorityIdentifier.DEFAULT) {
-            throw new InvalidPriorityIdentifierException(
-                    "Priority identifier for newly added MFA method should be BACKUP");
+            return Either.left(MfaCreateFailureReason.INVALID_PRIORITY_IDENTIFIER);
+        }
+
+        UserCredentials userCredentials = persistentService.getUserCredentialsFromEmail(email);
+        List<MfaMethodData> mfaMethods = getMfaMethodsForMigratedUser(userCredentials);
+
+        if (mfaMethods.size() >= 2) {
+            return Either.left(MfaCreateFailureReason.BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST);
         }
 
         if (mfaMethod.method() instanceof SmsMfaDetail smsMfaDetail) {
+
+            boolean phoneNumberExists =
+                    mfaMethods.stream()
+                            .map(MfaMethodData::method)
+                            .filter(SmsMfaDetail.class::isInstance)
+                            .map(SmsMfaDetail.class::cast)
+                            .anyMatch(mfa -> mfa.phoneNumber().equals(smsMfaDetail.phoneNumber()));
+
+            if (phoneNumberExists) {
+                return Either.left(MfaCreateFailureReason.PHONE_NUMBER_ALREADY_EXISTS);
+            }
+
             String uuid = UUID.randomUUID().toString();
             persistentService.addMFAMethodSupportingMultiple(
                     email,
@@ -151,11 +169,38 @@ public MfaMethodData addBackupMfa(String email, MfaMethodCreateRequest.MfaMethod
                             smsMfaDetail.phoneNumber(),
                             mfaMethod.priorityIdentifier(),
                             uuid));
-            return MfaMethodData.smsMethodData(
-                    uuid, mfaMethod.priorityIdentifier(), true, smsMfaDetail.phoneNumber());
-        }
+            return Either.right(
+                    MfaMethodData.smsMethodData(
+                            uuid,
+                            mfaMethod.priorityIdentifier(),
+                            true,
+                            smsMfaDetail.phoneNumber()));
+        } else {
+            boolean authAppExists =
+                    mfaMethods.stream()
+                            .map(MfaMethodData::method)
+                            .filter(AuthAppMfaDetail.class::isInstance)
+                            .anyMatch(mfa -> true);
+
+            if (authAppExists) {
+                return Either.left(MfaCreateFailureReason.AUTH_APP_EXISTS);
+            }
 
-        //      Further implementation to come in subsequent commits
-        return null;
+            String uuid = UUID.randomUUID().toString();
+            persistentService.addMFAMethodSupportingMultiple(
+                    email,
+                    MFAMethod.authAppMfaMethod(
+                            ((AuthAppMfaDetail) mfaMethod.method()).credential(),
+                            true,
+                            true,
+                            mfaMethod.priorityIdentifier(),
+                            uuid));
+            return Either.right(
+                    MfaMethodData.authAppMfaData(
+                            uuid,
+                            mfaMethod.priorityIdentifier(),
+                            true,
+                            ((AuthAppMfaDetail) mfaMethod.method()).credential()));
+        }
     }
 }


Reviews:
user: login: BeckaL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T09:32:47Z
user: login: BeckaL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T09:37:52Z
user: login: BeckaL
body: Comment on a test assertion. Happy for you to fix in a subsequent mini PR just so that we can get this in :-) 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T13:05:23Z

Review comments:
diff_hunk: @@ -94,6 +94,9 @@ public APIGatewayProxyResponseEvent mfaMethodsHandler(
                     case INVALID_PRIORITY_IDENTIFIER -> {
                         return generateApiGatewayProxyErrorResponse(400, ErrorResponse.ERROR_1001);
                     }
+                    case AT_MAXIMUM_MFA_COUNT -> {
path: account-management-api/src/main/java/uk/gov/di/accountmanagement/lambda/MFAMethodsCreateHandler.java
user: login: BeckaL
body: I'd like to avoid using 'count' terminology here, as I read it as being about mfa attempt counts. Maybe "BACKUP_AND_DEFAULT_METHOD_ALREADY_EXIST"? (and same for elsewhere where we call this count)
created_at: 2025-03-19T09:32:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -91,6 +92,56 @@ void shouldReturn200AndMfaMethodDataWhenAuthAppUserAddsSmsMfa() {
                         .anyMatch(m -> true));
     }
 
+    @Test
+    void shouldReturn200AndMfaMethodDataWhenSmsUserAddsAuthAppMfa() {
+        userStore.addMfaMethodSupportingMultiple(
+                TEST_EMAIL,
+                MFAMethod.smsMfaMethod(
+                        true,
+                        true,
+                        TEST_PHONE_NUMBER,
+                        PriorityIdentifier.DEFAULT,
+                        UUID.randomUUID().toString()));
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP, TEST_CREDENTIAL))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT));
+        assertEquals(200, response.getStatusCode());
+
+        String extractedMfaIdentifier =
+                JsonParser.parseString(response.getBody())
+                        .getAsJsonObject()
+                        .get("mfaIdentifier")
+                        .getAsString();
+
+        List<MFAMethod> mfaMethods = userStore.getMfaMethod(TEST_EMAIL);
+
+        assertTrue(
path: account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java
user: login: BeckaL
body: I find this quite hard to read as an assertion.

I think I'd rather grab the sms method and then assert on fields, e.g:

```
        var retrievedSmsMethod = mfaMethods.stream().filter(mfaMethod -> mfaMethod.getMfaMethodType().equals(MFAMethodType.SMS.getValue())).findFirst().get();

assertEquals(expectedFoo, retrievedSmsMethod.foo());
etc...

```
created_at: 2025-03-19T09:37:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,24 +72,68 @@ void shouldReturn200AndMfaMethodDataWhenAuthAppUserAddsSmsMfa() {
 
         List<MFAMethod> mfaMethods = userStore.getMfaMethod(TEST_EMAIL);
 
-        assertTrue(
+        var retrievedSmsMethod =
                 mfaMethods.stream()
                         .filter(
                                 mfaMethod ->
                                         mfaMethod
                                                 .getMfaMethodType()
                                                 .equals(MFAMethodType.SMS.getValue()))
-                        .filter(
-                                mfaMethod ->
-                                        mfaMethod.getMfaIdentifier().equals(extractedMfaIdentifier))
-                        .filter(mfaMethod -> mfaMethod.getDestination().equals(TEST_PHONE_NUMBER))
+                        .findFirst()
+                        .get();
+
+        assertEquals(extractedMfaIdentifier, retrievedSmsMethod.getMfaIdentifier());
+        assertEquals(PriorityIdentifier.BACKUP.toString(), retrievedSmsMethod.getPriority());
+        assertEquals(TEST_PHONE_NUMBER, retrievedSmsMethod.getDestination());
+        assertTrue(retrievedSmsMethod.isEnabled());
+        assertTrue(retrievedSmsMethod.isMethodVerified());
+    }
+
+    @Test
+    void shouldReturn200AndMfaMethodDataWhenSmsUserAddsAuthAppMfa() {
+        userStore.addMfaMethodSupportingMultiple(
+                TEST_EMAIL,
+                MFAMethod.smsMfaMethod(
+                        true,
+                        true,
+                        TEST_PHONE_NUMBER,
+                        PriorityIdentifier.DEFAULT,
+                        UUID.randomUUID().toString()));
+        var response =
+                makeRequest(
+                        Optional.of(
+                                constructRequestBody(
+                                        PriorityIdentifier.BACKUP,
+                                        new AuthAppMfaDetail(
+                                                MFAMethodType.AUTH_APP, TEST_CREDENTIAL))),
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Map.of("publicSubjectId", TEST_PUBLIC_SUBJECT));
+        assertEquals(200, response.getStatusCode());
+
+        String extractedMfaIdentifier =
path: account-management-integration-tests/src/test/java/uk/gov/di/accountmanagement/api/MFAMethodsCreateHandlerIntegrationTest.java
user: login: BeckaL
body: It'd be good to assert against a full json response here (e.g. `assertEquals(response.getBody(), someJsonString`. We could potentially do this by grabbing the mfa identifier from the database first, and then chucking that in the json we're expecting. Just helps to have a readable json response when having a look at what the lambda does.
created_at: 2025-03-19T13:01:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-03-19T11:53:04Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=govuk-one-login_authentication-api&pullRequest=6163) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&pullRequest=6163&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&pullRequest=6163&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=govuk-one-login_authentication-api&pullRequest=6163&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [95.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=govuk-one-login_authentication-api&pullRequest=6163&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=govuk-one-login_authentication-api&pullRequest=6163&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=govuk-one-login_authentication-api&pullRequest=6163)


author_association: NONE