diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorViewModel.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorViewModel.kt index e8ce2fcc..a0e6a579 100644 --- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorViewModel.kt +++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorViewModel.kt @@ -76,7 +76,6 @@ import com.amplifyframework.ui.authenticator.util.PasswordResetMessage import com.amplifyframework.ui.authenticator.util.RealAuthProvider import com.amplifyframework.ui.authenticator.util.UnableToResetPasswordMessage import com.amplifyframework.ui.authenticator.util.UnknownErrorMessage -import com.amplifyframework.ui.authenticator.util.challengeResponse import com.amplifyframework.ui.authenticator.util.toFieldError import java.net.UnknownHostException import kotlinx.coroutines.channels.BufferOverflow diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSelectionStateImpl.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSelectionStateImpl.kt index 35a6ff88..36b481e6 100644 --- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSelectionStateImpl.kt +++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInContinueWithMfaSelectionStateImpl.kt @@ -1,11 +1,11 @@ package com.amplifyframework.ui.authenticator.states import com.amplifyframework.auth.MFAType +import com.amplifyframework.auth.cognito.challengeResponse import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSelectionState import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep import com.amplifyframework.ui.authenticator.forms.FieldKey -import com.amplifyframework.ui.authenticator.util.challengeResponse internal class SignInContinueWithMfaSelectionStateImpl( override val allowedMfaTypes: Set, diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelection.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelection.kt index f9cc5b4a..f0341180 100644 --- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelection.kt +++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelection.kt @@ -4,21 +4,19 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.amplifyframework.auth.MFAType +import com.amplifyframework.auth.cognito.challengeResponse import com.amplifyframework.ui.authenticator.R import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSelectionState import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep import com.amplifyframework.ui.authenticator.forms.FieldKey -import com.amplifyframework.ui.authenticator.util.challengeResponse import kotlinx.coroutines.launch @Composable diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthProvider.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthProvider.kt index 96764651..ffb7d723 100644 --- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthProvider.kt +++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthProvider.kt @@ -24,6 +24,9 @@ import com.amplifyframework.auth.AuthUser import com.amplifyframework.auth.AuthUserAttribute import com.amplifyframework.auth.AuthUserAttributeKey import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin +import com.amplifyframework.auth.cognito.PasswordProtectionSettings +import com.amplifyframework.auth.cognito.UsernameAttribute +import com.amplifyframework.auth.cognito.VerificationMechanism as AmplifyVerificationMechanism import com.amplifyframework.auth.options.AuthSignUpOptions import com.amplifyframework.auth.result.AuthResetPasswordResult import com.amplifyframework.auth.result.AuthSignInResult @@ -44,7 +47,6 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -import org.json.JSONException /** * An abstraction of the Amplify.Auth API that allows us to use coroutines with no exceptions @@ -253,62 +255,31 @@ internal class RealAuthProvider : AuthProvider { } override suspend fun getConfiguration(): AuthConfigurationResult { - val authConfigJSON = getCognitoPlugin()?.getPluginConfiguration() ?: return AuthConfigurationResult.Missing - try { - val innerJSON = authConfigJSON - .getJSONObject("Auth") - .getJSONObject("Default") - val signUpAttributes = innerJSON.getJSONArray("signupAttributes") - val usernameAttributes = innerJSON.getJSONArray("usernameAttributes") - val passwordAttributes = innerJSON.getJSONObject("passwordProtectionSettings") - - val signInAttributeList = List(usernameAttributes.length()) { - usernameAttributes.getString(it) - } - val containsEmail = signInAttributeList.contains("EMAIL") - val containsPhoneNumber = signInAttributeList.contains("PHONE_NUMBER") - val signInMethod = when { - containsEmail -> SignInMethod.Email - containsPhoneNumber -> SignInMethod.PhoneNumber - else -> SignInMethod.Username - } + val authConfiguration = getCognitoPlugin()?.getAuthConfiguration() ?: return AuthConfigurationResult.Missing + + val passwordCriteria = authConfiguration.passwordProtectionSettings?.toPasswordCriteria() + ?: return AuthConfigurationResult.Invalid( + """ + Your auth configuration does not define passwordProtectionSettings. + Authenticator needs these settings to perform client-side validation of passwords. + """.trimIndent() + ) - val signUpAttributeList = List(signUpAttributes.length()) { - AuthUserAttributeKey.custom(signUpAttributes.getString(it).lowercase()) + val verificationMechanisms = authConfiguration.verificationMechanisms.map { + when (it) { + AmplifyVerificationMechanism.Email -> VerificationMechanism.Email + AmplifyVerificationMechanism.PhoneNumber -> VerificationMechanism.PhoneNumber } + }.toSet() - val passwordRequirementsJSON = passwordAttributes - .getJSONArray("passwordPolicyCharacters") - val passwordRequirements = List(passwordRequirementsJSON.length()) { - passwordRequirementsJSON.getString(it) - } - val passwordCriteria = PasswordCriteria( - length = passwordAttributes.getInt("passwordPolicyMinLength"), - requiresNumber = passwordRequirements.contains("REQUIRES_NUMBERS"), - requiresSpecial = passwordRequirements.contains("REQUIRES_SYMBOLS"), - requiresLower = passwordRequirements.contains("REQUIRES_LOWER"), - requiresUpper = passwordRequirements.contains("REQUIRES_UPPER") - ) + val amplifyAuthConfiguration = AmplifyAuthConfiguration( + signInMethod = getSignInMethod(authConfiguration.usernameAttributes), + signUpAttributes = authConfiguration.signUpAttributes, + passwordCriteria = passwordCriteria, + verificationMechanisms = verificationMechanisms + ) - val verificationMechanismsJson = innerJSON.getJSONArray("verificationMechanisms") - val verificationMechanisms = List(verificationMechanismsJson.length()) { - when (verificationMechanismsJson.getString(it)) { - "EMAIL" -> VerificationMechanism.Email - else -> VerificationMechanism.PhoneNumber - } - }.toSet() - - return AuthConfigurationResult.Valid( - AmplifyAuthConfiguration( - signInMethod, - signUpAttributeList, - passwordCriteria, - verificationMechanisms - ) - ) - } catch (e: JSONException) { - return AuthConfigurationResult.Invalid(e.message ?: "Auth configuration is not valid", e) - } + return AuthConfigurationResult.Valid(amplifyAuthConfiguration) } private fun getCognitoPlugin(): AWSCognitoAuthPlugin? { @@ -319,6 +290,20 @@ internal class RealAuthProvider : AuthProvider { null } } + + private fun getSignInMethod(attributes: List) = when { + attributes.contains(UsernameAttribute.Email) -> SignInMethod.Email + attributes.contains(UsernameAttribute.PhoneNumber) -> SignInMethod.PhoneNumber + else -> SignInMethod.Username + } + + private fun PasswordProtectionSettings.toPasswordCriteria() = PasswordCriteria( + length = length, + requiresNumber = requiresNumber, + requiresSpecial = requiresSpecial, + requiresUpper = requiresUpper, + requiresLower = requiresLower + ) } internal sealed interface AmplifyResult { diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/MfaTypeExtensions.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/MfaTypeExtensions.kt deleted file mode 100644 index 2106deab..00000000 --- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/MfaTypeExtensions.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.amplifyframework.ui.authenticator.util - -import com.amplifyframework.auth.MFAType - -// Amplify currently doesn't expose the strings we need to pass to confirmSignIn. This should be fixed in a future -// Amplify version -internal val MFAType.challengeResponse: String - get() = when (this) { - MFAType.SMS -> "SMS_MFA" - MFAType.TOTP -> "SOFTWARE_TOKEN_MFA" - } diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt index 1dd31a37..ca89c5c0 100644 --- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt +++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt @@ -1,11 +1,11 @@ package com.amplifyframework.ui.authenticator.ui import com.amplifyframework.auth.MFAType +import com.amplifyframework.auth.cognito.challengeResponse import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep import com.amplifyframework.ui.authenticator.states.SignInContinueWithMfaSelectionStateImpl import com.amplifyframework.ui.authenticator.ui.robots.signInContinueWithMfaSelection -import com.amplifyframework.ui.authenticator.util.challengeResponse import com.amplifyframework.ui.testing.ComposeTest import io.mockk.mockk import io.mockk.verify diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithMfaSelectionRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithMfaSelectionRobot.kt index a9496139..15d54347 100644 --- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithMfaSelectionRobot.kt +++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInContinueWithMfaSelectionRobot.kt @@ -20,8 +20,8 @@ import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.isSelected import androidx.compose.ui.test.junit4.ComposeTestRule import com.amplifyframework.auth.MFAType +import com.amplifyframework.auth.cognito.challengeResponse import com.amplifyframework.ui.authenticator.ui.TestTags -import com.amplifyframework.ui.authenticator.util.challengeResponse import com.amplifyframework.ui.testing.ComposeTest fun ComposeTest.signInContinueWithMfaSelection(func: SignInContinueWithMfaSelectionRobot.() -> Unit) = diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81a081b9..cc7fe411 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] agp = "8.1.4" -amplify = "2.15.0" +amplify = "2.16.0" binary-compatibility = "0.14.0" cameraX = "1.2.0" compose = "1.5.4" diff --git a/samples/authenticator/app/build.gradle b/samples/authenticator/app/build.gradle index 266c7dc6..58a1afa7 100644 --- a/samples/authenticator/app/build.gradle +++ b/samples/authenticator/app/build.gradle @@ -24,6 +24,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -53,6 +54,7 @@ dependencies { // Use this to use published version of Amplify UI implementation "com.amplifyframework.ui:authenticator:$authenticatorVersion" + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation "com.google.accompanist:accompanist-permissions:0.28.0" implementation 'androidx.core:core-ktx:1.9.0'