Skip to content

Commit 5586c16

Browse files
committed
chore: change state machine and auth bloc to automatically move states if only one mfa method is allowed
1 parent 970bf15 commit 5586c16

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

packages/auth/amplify_auth_cognito_dart/lib/src/state/machines/sign_in_state_machine.dart

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,56 @@ final class SignInStateMachine
330330
createEmailMfaRequest(event),
331331
ChallengeNameType.selectMfaType when hasUserResponse =>
332332
createSelectMfaRequest(event),
333-
ChallengeNameType.mfaSetup when hasUserResponse =>
334-
createMfaSetupRequest(event),
333+
ChallengeNameType.mfaSetup =>
334+
(() async {
335+
final allowedMfaTypes = _allowedMfaTypes;
336+
if (allowedMfaTypes == null || allowedMfaTypes.isEmpty) {
337+
throw const InvalidUserPoolConfigurationException(
338+
'No MFA types are allowed for setup.',
339+
recoverySuggestion: 'Check your user pool MFA configuration.',
340+
);
341+
}
342+
// Exclude MfaType.sms from consideration
343+
final mfaTypesForSetup = allowedMfaTypes.difference({MfaType.sms});
344+
if (mfaTypesForSetup.isEmpty) {
345+
throw const InvalidUserPoolConfigurationException(
346+
'No eligible MFA types are available for setup.',
347+
recoverySuggestion: 'Check your user pool MFA configuration.',
348+
);
349+
}
350+
if (mfaTypesForSetup.length == 1) {
351+
final mfaType = mfaTypesForSetup.first;
352+
if (mfaType == MfaType.totp) {
353+
_enableMfaType = MfaType.totp;
354+
_totpSetupResult ??= await associateSoftwareToken();
355+
if (hasUserResponse) {
356+
return createMfaSetupRequest(event);
357+
} else {
358+
// Need to prompt user for the TOTP code
359+
return null;
360+
}
361+
} else if (mfaType == MfaType.email) {
362+
_enableMfaType = MfaType.email;
363+
if (hasUserResponse) {
364+
return createEmailMfaSetupRequest(event);
365+
} else {
366+
// Need to prompt user for the email verification code
367+
return null;
368+
}
369+
} else {
370+
throw InvalidUserPoolConfigurationException(
371+
'Unsupported MFA type: ${mfaType.name}',
372+
recoverySuggestion: 'Check your user pool MFA configuration.',
373+
);
374+
}
375+
} else if (hasUserResponse) {
376+
// Handle user's selection
377+
return createMfaSetupRequest(event);
378+
} else {
379+
// Need to prompt user to select an MFA type
380+
return null;
381+
}
382+
})(),
335383
ChallengeNameType.newPasswordRequired when hasUserResponse =>
336384
createNewPasswordRequest(event),
337385
_ => null,
@@ -674,6 +722,24 @@ final class SignInStateMachine
674722
});
675723
}
676724

725+
/// Compeletes set up of an email MFA.
726+
@protected
727+
Future<RespondToAuthChallengeRequest> createEmailMfaSetupRequest(
728+
SignInRespondToChallenge event,
729+
) async {
730+
_enableMfaType = MfaType.email;
731+
return RespondToAuthChallengeRequest.build((b) {
732+
b
733+
..challengeName = ChallengeNameType.emailOtp
734+
..challengeResponses.addAll({
735+
CognitoConstants.challengeParamUsername: cognitoUsername,
736+
CognitoConstants.challengeParamEmailMfaCode: event.answer,
737+
})
738+
..clientId = _authOutputs.userPoolClientId
739+
..clientMetadata.addAll(event.clientMetadata);
740+
});
741+
}
742+
677743
/// Selects an MFA type to use for sign-in.
678744
@protected
679745
Future<RespondToAuthChallengeRequest> createSelectMfaRequest(

packages/authenticator/amplify_authenticator/lib/src/blocs/auth/auth_bloc.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,16 @@ class StateMachineBloc
227227
yield UnauthenticatedState.confirmSignInNewPassword;
228228
case AuthSignInStep.confirmSignInWithTotpMfaCode:
229229
yield UnauthenticatedState.confirmSignInWithTotpMfaCode;
230+
case AuthSignInStep.confirmSignInWithEmailMfaCode:
231+
yield UnauthenticatedState.confirmSignInWithEmailMfaCode;
230232
case AuthSignInStep.continueSignInWithMfaSelection:
231233
yield ContinueSignInWithMfaSelection(
232234
allowedMfaTypes: result.nextStep.allowedMfaTypes,
233235
);
236+
case AuthSignInStep.continueSignInWithMfaSetupSelection:
237+
yield ContinueSignInWithMfaSetupSelection(
238+
allowedMfaTypes: result.nextStep.allowedMfaTypes,
239+
);
234240
case AuthSignInStep.continueSignInWithTotpSetup:
235241
assert(
236242
result.nextStep.totpSetupDetails != null,
@@ -333,6 +339,43 @@ class StateMachineBloc
333339
allowedMfaTypes: result.nextStep.allowedMfaTypes,
334340
),
335341
);
342+
case AuthSignInStep.continueSignInWithMfaSetupSelection:
343+
final allowedMfaTypes = result.nextStep.allowedMfaTypes;
344+
if (allowedMfaTypes != null) {
345+
final mfaTypesForSetup = allowedMfaTypes.toSet()..remove(MfaType.sms);
346+
if (mfaTypesForSetup.length == 1) {
347+
final mfaType = mfaTypesForSetup.first;
348+
if (mfaType == MfaType.totp) {
349+
assert(
350+
result.nextStep.totpSetupDetails != null,
351+
'Sign In Result should have totpSetupDetails',
352+
);
353+
_emit(await ContinueSignInTotpSetup.setupURI(
354+
result.nextStep.totpSetupDetails!,
355+
totpOptions,
356+
),);
357+
} else if (mfaType == MfaType.email) {
358+
_emit(UnauthenticatedState.continueSignInWithEmailMfaSetup);
359+
} else {
360+
throw InvalidUserPoolConfigurationException(
361+
'Unsupported MFA type: ${mfaType.name}',
362+
recoverySuggestion: 'Check your user pool MFA configuration.',
363+
);
364+
}
365+
} else {
366+
_emit(
367+
ContinueSignInWithMfaSetupSelection(
368+
allowedMfaTypes: result.nextStep.allowedMfaTypes,
369+
),
370+
);
371+
}
372+
} else {
373+
_emit(
374+
ContinueSignInWithMfaSetupSelection(
375+
allowedMfaTypes: result.nextStep.allowedMfaTypes,
376+
),
377+
);
378+
}
336379
case AuthSignInStep.continueSignInWithTotpSetup:
337380
assert(
338381
result.nextStep.totpSetupDetails != null,
@@ -344,8 +387,13 @@ class StateMachineBloc
344387
totpOptions,
345388
),
346389
);
390+
case AuthSignInStep.continueSignInWithEmailMfaSetup:
391+
_emit(UnauthenticatedState.continueSignInWithEmailMfaSetup);
347392
case AuthSignInStep.confirmSignInWithTotpMfaCode:
348393
_emit(UnauthenticatedState.confirmSignInWithTotpMfaCode);
394+
case AuthSignInStep.confirmSignInWithEmailMfaCode:
395+
_notifyCodeSent(result.nextStep.codeDeliveryDetails?.destination);
396+
_emit(UnauthenticatedState.confirmSignInWithEmailMfaCode);
349397
case AuthSignInStep.resetPassword:
350398
_emit(UnauthenticatedState.confirmResetPassword);
351399
case AuthSignInStep.confirmSignUp:

0 commit comments

Comments
 (0)