@@ -37,6 +37,8 @@ import com.amplifyframework.auth.cognito.exceptions.service.PasswordResetRequire
3737import com.amplifyframework.auth.cognito.exceptions.service.UserNotConfirmedException
3838import com.amplifyframework.auth.cognito.exceptions.service.UserNotFoundException
3939import com.amplifyframework.auth.cognito.exceptions.service.UsernameExistsException
40+ import com.amplifyframework.auth.cognito.options.AWSCognitoAuthConfirmSignInOptions
41+ import com.amplifyframework.auth.cognito.options.AWSCognitoAuthSignInOptions
4042import com.amplifyframework.auth.exceptions.NotAuthorizedException
4143import com.amplifyframework.auth.exceptions.SessionExpiredException
4244import com.amplifyframework.auth.exceptions.UnknownException
@@ -52,7 +54,12 @@ import com.amplifyframework.ui.authenticator.auth.AmplifyAuthConfiguration
5254import com.amplifyframework.ui.authenticator.auth.toAttributeKey
5355import com.amplifyframework.ui.authenticator.auth.toFieldKey
5456import com.amplifyframework.ui.authenticator.auth.toVerifiedAttributeKey
57+ import com.amplifyframework.ui.authenticator.data.AuthFactor
58+ import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
5559import com.amplifyframework.ui.authenticator.data.UserInfo
60+ import com.amplifyframework.ui.authenticator.data.challengeResponse
61+ import com.amplifyframework.ui.authenticator.data.toAuthFactors
62+ import com.amplifyframework.ui.authenticator.data.toAuthFlowType
5663import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
5764import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
5865import com.amplifyframework.ui.authenticator.enums.SignInSource
@@ -69,6 +76,7 @@ import com.amplifyframework.ui.authenticator.states.BaseStateImpl
6976import com.amplifyframework.ui.authenticator.states.StepStateFactory
7077import com.amplifyframework.ui.authenticator.util.AmplifyResult
7178import com.amplifyframework.ui.authenticator.util.AuthConfigurationResult
79+ import com.amplifyframework.ui.authenticator.util.AuthFlowSessionExpiredMessage
7280import com.amplifyframework.ui.authenticator.util.AuthProvider
7381import com.amplifyframework.ui.authenticator.util.AuthenticatorMessage
7482import com.amplifyframework.ui.authenticator.util.CannotSendCodeMessage
@@ -83,7 +91,11 @@ import com.amplifyframework.ui.authenticator.util.PasswordResetMessage
8391import com.amplifyframework.ui.authenticator.util.RealAuthProvider
8492import com.amplifyframework.ui.authenticator.util.UnableToResetPasswordMessage
8593import com.amplifyframework.ui.authenticator.util.UnknownErrorMessage
94+ import com.amplifyframework.ui.authenticator.util.authFlow
95+ import com.amplifyframework.ui.authenticator.util.callingActivity
96+ import com.amplifyframework.ui.authenticator.util.isAuthFlowSessionExpiredError
8697import com.amplifyframework.ui.authenticator.util.isConnectivityIssue
98+ import com.amplifyframework.ui.authenticator.util.preferredFirstFactor
8799import com.amplifyframework.ui.authenticator.util.toFieldError
88100import java.lang.ref.WeakReference
89101import kotlinx.coroutines.channels.BufferOverflow
@@ -272,7 +284,8 @@ internal class AuthenticatorViewModel(application: Application, private val auth
272284 }
273285
274286 private suspend fun handleSignedUp (info : UserInfo ) = startSignInJob {
275- when (val result = authProvider.signIn(info.username, info.password)) {
287+ val options = getSignInOptions()
288+ when (val result = authProvider.signIn(info.username, info.password, options)) {
276289 is AmplifyResult .Error -> {
277290 moveTo(AuthenticatorStep .SignIn )
278291 handleSignInFailure(info, result.error)
@@ -295,21 +308,35 @@ internal class AuthenticatorViewModel(application: Application, private val auth
295308 }
296309
297310 private suspend fun startSignIn (info : UserInfo ) = startSignInJob {
298- when (val result = authProvider.signIn(info.username, info.password)) {
311+ val options = getSignInOptions()
312+ when (val result = authProvider.signIn(info.username, info.password, options)) {
299313 is AmplifyResult .Error -> handleSignInFailure(info, result.error)
300314 is AmplifyResult .Success -> handleSignInSuccess(info, result.data)
301315 }
302316 }
303317
318+ private fun getSignInOptions (preferredFirstFactorOverride : AuthFactor ? = null) =
319+ AWSCognitoAuthSignInOptions .builder()
320+ .authFlow(configuration.authenticationFlow.toAuthFlowType())
321+ .callingActivity(activity)
322+ .preferredFirstFactor(configuration.authenticationFlow, preferredFirstFactorOverride)
323+ .build()
324+
304325 private suspend fun confirmSignIn (info : UserInfo , challengeResponse : String ) = startSignInJob {
305- when (val result = authProvider.confirmSignIn(challengeResponse)) {
306- is AmplifyResult .Error -> handleSignInFailure(info, result.error)
326+ val options = AWSCognitoAuthConfirmSignInOptions .builder()
327+ .callingActivity(activity)
328+ .build()
329+ when (val result = authProvider.confirmSignIn(challengeResponse, options)) {
330+ is AmplifyResult .Error -> handleConfirmSignInFailure(info, result.error)
307331 is AmplifyResult .Success -> handleSignInSuccess(info, result.data)
308332 }
309333 }
310334
311335 private suspend fun setNewSignInPassword (info : UserInfo , newPassword : String ) = startSignInJob {
312- when (val result = authProvider.confirmSignIn(newPassword)) {
336+ val options = AWSCognitoAuthConfirmSignInOptions .builder()
337+ .callingActivity(activity)
338+ .build()
339+ when (val result = authProvider.confirmSignIn(newPassword, options)) {
313340 // an error here is more similar to a sign up error
314341 is AmplifyResult .Error -> handleSignUpFailure(result.error)
315342 is AmplifyResult .Success -> {
@@ -330,6 +357,17 @@ internal class AuthenticatorViewModel(application: Application, private val auth
330357 }
331358 }
332359
360+ private suspend fun handleConfirmSignInFailure (info : UserInfo , error : AuthException ) {
361+ if (configuration.authenticationFlow is AuthenticationFlow .UserChoice &&
362+ error.isAuthFlowSessionExpiredError()
363+ ) {
364+ moveTo(AuthenticatorStep .SignIn )
365+ sendMessage(AuthFlowSessionExpiredMessage (error))
366+ } else {
367+ handleSignInFailure(info, error)
368+ }
369+ }
370+
333371 private suspend fun handleUnconfirmedSignIn (info : UserInfo ) {
334372 when (val result = authProvider.resendSignUpCode(info.username)) {
335373 is AmplifyResult .Error -> handleAuthException(result.error)
@@ -352,7 +390,33 @@ internal class AuthenticatorViewModel(application: Application, private val auth
352390 }
353391 }
354392
355- private suspend fun handleTotpSetupRequired (info : UserInfo , totpSetupDetails : TOTPSetupDetails ? ) {
393+ private suspend fun handleFactorSelectionRequired (info : UserInfo , availableFactors : Set <AuthFactor >? ) {
394+ if (availableFactors == null ) {
395+ val exception = AuthException (" Missing available AuthFactorTypes" , " Please open a bug with Amplify" )
396+ handleGeneralFailure(exception)
397+ return
398+ }
399+
400+ // Auto-select a single auth factor
401+ if (availableFactors.size == 1 ) {
402+ confirmSignIn(info, availableFactors.first().challengeResponse)
403+ return
404+ }
405+
406+ val newState = stateFactory.newSignInSelectFactorState(
407+ username = info.username,
408+ availableFactors = availableFactors,
409+ onSelect = { authFactor ->
410+ val passwordField = (currentState as ? BaseStateImpl )?.form?.fields?.get(Password )
411+ val password = passwordField?.state?.content
412+ val newInfo = info.copy(password = password)
413+ confirmSignIn(newInfo, authFactor.challengeResponse)
414+ }
415+ )
416+ moveTo(newState)
417+ }
418+
419+ private fun handleTotpSetupRequired (info : UserInfo , totpSetupDetails : TOTPSetupDetails ? ) {
356420 if (totpSetupDetails == null ) {
357421 val exception = AuthException (" Missing TOTPSetupDetails" , " Please open a bug with Amplify" )
358422 handleGeneralFailure(exception)
@@ -444,6 +508,23 @@ internal class AuthenticatorViewModel(application: Application, private val auth
444508 confirmSignIn(info, confirmationCode)
445509 }
446510 )
511+ AuthSignInStep .CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION ->
512+ handleFactorSelectionRequired(
513+ info,
514+ result.nextStep.availableFactors?.toAuthFactors()
515+ )
516+ AuthSignInStep .CONFIRM_SIGN_IN_WITH_PASSWORD -> {
517+ if (info.password != null ) {
518+ confirmSignIn(info, info.password)
519+ } else {
520+ moveTo(
521+ stateFactory.newSignInConfirmPasswordState(username = info.username) { password ->
522+ val newInfo = info.copy(password = password)
523+ confirmSignIn(newInfo, password)
524+ }
525+ )
526+ }
527+ }
447528 else -> {
448529 // Generic error for any other next steps that may be added in the future
449530 val exception = AuthException (
@@ -532,7 +613,7 @@ internal class AuthenticatorViewModel(application: Application, private val auth
532613 }
533614 }
534615
535- private suspend fun handlePasswordResetComplete (username : String? = null, password : String? = null ) {
616+ private suspend fun handlePasswordResetComplete () {
536617 logger.debug(" Password reset complete" )
537618 sendMessage(PasswordResetMessage )
538619 moveTo(stateFactory.newSignInState(this ::signIn))
0 commit comments