Skip to content
1 change: 1 addition & 0 deletions aws-auth-cognito/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ dependencies {
androidTestImplementation(libs.test.kotlin.coroutines)
androidTestImplementation(libs.test.kotlin.kotlinTest)
androidTestImplementation(libs.test.totp)
androidTestImplementation(libs.test.kotest.assertions)

androidTestImplementation(project(":aws-api"))
androidTestImplementation(project(":aws-api-appsync"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,24 @@ package com.amplifyframework.auth.cognito
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.amplifyframework.api.aws.AWSApiPlugin
import com.amplifyframework.api.graphql.GraphQLOperation
import com.amplifyframework.api.graphql.SimpleGraphQLRequest
import com.amplifyframework.auth.AuthUserAttribute
import com.amplifyframework.auth.AuthUserAttributeKey
import com.amplifyframework.auth.MFAType
import com.amplifyframework.auth.cognito.exceptions.service.CodeMismatchException
import com.amplifyframework.auth.cognito.test.R
import com.amplifyframework.auth.cognito.testutils.blockForCode
import com.amplifyframework.auth.cognito.testutils.blockUntilEstablished
import com.amplifyframework.auth.cognito.testutils.createMfaSubscription
import com.amplifyframework.auth.options.AuthSignUpOptions
import com.amplifyframework.auth.result.AuthSignUpResult
import com.amplifyframework.auth.result.step.AuthSignInStep
import com.amplifyframework.core.configuration.AmplifyOutputs
import com.amplifyframework.core.configuration.AmplifyOutputsData
import com.amplifyframework.datastore.generated.model.MfaInfo
import com.amplifyframework.testutils.Assets
import com.amplifyframework.testutils.api.SubscriptionHolder
import com.amplifyframework.testutils.sync.SynchronousAuth
import java.util.Random
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import org.junit.After
Expand All @@ -46,15 +45,13 @@ import org.junit.Test
class AWSCognitoAuthPluginEmailMFATests {

private val password = "${UUID.randomUUID()}BleepBloop1234!"
private val userName = "test${Random().nextInt()}"
private val email = "$userName@amplify-swift-gamma.awsapps.com"
private val username = "test${Random().nextInt()}"
private val email = "$username@amplify-swift-gamma.awsapps.com"

private var authPlugin = AWSCognitoAuthPlugin()
private var apiPlugin = AWSApiPlugin()
private lateinit var synchronousAuth: SynchronousAuth
private var subscription: GraphQLOperation<MfaInfo>? = null
private var mfaCode = ""
private var latch: CountDownLatch? = null
private lateinit var subscription: SubscriptionHolder<MfaInfo>

@Before
fun initializePlugin() {
Expand All @@ -66,29 +63,12 @@ class AWSCognitoAuthPluginEmailMFATests {
apiPlugin.configure(config, context)
synchronousAuth = SynchronousAuth.delegatingTo(authPlugin)

subscription = apiPlugin.subscribe(
SimpleGraphQLRequest(
Assets.readAsString("create-mfa-subscription.graphql"),
MfaInfo::class.java,
null
),
{ println("====== Subscription Established ======") },
{
println("====== Received some MFA Info ======")
if (it.data.username == userName) {
mfaCode = it.data.code
latch?.countDown()
}
},
{ println("====== Subscription Failed $it ======") },
{ }
)
subscription = apiPlugin.createMfaSubscription()
}

@After
fun tearDown() {
subscription?.cancel()
mfaCode = ""
subscription.cancel()
synchronousAuth.deleteUser()
}

Expand All @@ -98,7 +78,7 @@ class AWSCognitoAuthPluginEmailMFATests {
signUpNewUser()

// Step 2: Attempt to sign in with the newly created user
var signInResult = synchronousAuth.signIn(userName, password)
var signInResult = synchronousAuth.signIn(username, password)

// Validation 1: Validate that the next step is MFA Setup Selection
assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION, signInResult.nextStep.signInStep)
Expand All @@ -112,15 +92,17 @@ class AWSCognitoAuthPluginEmailMFATests {
// Validation 2: Validate that the next step is to input the user's email address
assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP, signInResult.nextStep.signInStep)

// Ensure subscription is ready to receive MFA code
subscription.blockUntilEstablished()

// Step 4: Input the email address to send the code to then wait for the MFA code
latch = CountDownLatch(1)
signInResult = synchronousAuth.confirmSignIn(email)

// Validation 3: Validate that the next step is to confirm the emailed MFA code
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)

// Wait until the MFA code has been received
latch?.await(20, TimeUnit.SECONDS)
val mfaCode = subscription.blockForCode(username)

// Step 5: Input the emailed MFA code for confirmation
signInResult = synchronousAuth.confirmSignIn(mfaCode)
Expand All @@ -134,15 +116,17 @@ class AWSCognitoAuthPluginEmailMFATests {
// Step 1: Sign up a new user with an existing email address
signUpNewUser(email)

// Ensure subscription is ready to receive MFA code
subscription.blockUntilEstablished()

// Step 2: Attempt to sign in with the newly created user
latch = CountDownLatch(1)
var signInResult = synchronousAuth.signIn(userName, password)
var signInResult = synchronousAuth.signIn(username, password)

// Validation 1: Validate that the next step is to confirm the emailed MFA code
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)

// Wait until the MFA code has been received
latch?.await(20, TimeUnit.SECONDS)
val mfaCode = subscription.blockForCode(username)

// Step 4: Input the emailed MFA code for confirmation
signInResult = synchronousAuth.confirmSignIn(mfaCode)
Expand All @@ -156,20 +140,22 @@ class AWSCognitoAuthPluginEmailMFATests {
// Step 1: Sign up a new user with an existing email address
signUpNewUser(email)

// Ensure subscription is ready to receive MFA code
subscription.blockUntilEstablished()

// Step 2: Attempt to sign in with the newly created user
latch = CountDownLatch(1)
var signInResult = synchronousAuth.signIn(userName, password)
var signInResult = synchronousAuth.signIn(username, password)

// Validation 1: Validate that the next step is to confirm the emailed MFA code
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)

// Wait until the MFA code has been received
latch?.await(20, TimeUnit.SECONDS)
val mfaCode = subscription.blockForCode(username)

// Step 4: Input the an incorrect MFA code
// Validation 2: Validate that an incorrect MFA code throws a CodeMismatchException
assertFailsWith<CodeMismatchException> {
signInResult = synchronousAuth.confirmSignIn(mfaCode.reversed())
synchronousAuth.confirmSignIn(mfaCode.reversed())
}

// Step 5: Input the correct MFA code for validation
Expand All @@ -189,6 +175,6 @@ class AWSCognitoAuthPluginEmailMFATests {
.userAttributes(
attributes
).build()
return synchronousAuth.signUp(userName, password, options)
return synchronousAuth.signUp(username, password, options)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import android.util.Log
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.amplifyframework.auth.AuthChannelEventName
import com.amplifyframework.auth.AuthSession
import com.amplifyframework.auth.cognito.testutils.Credentials
import com.amplifyframework.core.Amplify
import com.amplifyframework.core.InitializationStatus
import com.amplifyframework.hub.HubChannel
import com.amplifyframework.testutils.HubAccumulator
import com.amplifyframework.testutils.assertAwait
import com.amplifyframework.testutils.await
import com.amplifyframework.testutils.sync.SynchronousAuth
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.seconds
Expand All @@ -43,7 +44,8 @@ import org.junit.runner.RunWith
class AWSCognitoAuthPluginInstrumentationTests {

companion object {
var auth = AWSCognitoAuthPlugin()
val auth = AWSCognitoAuthPlugin()
val syncAuth = SynchronousAuth.delegatingTo(auth)

@BeforeClass
@JvmStatic
Expand All @@ -60,7 +62,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
latch.countDown()
}
}
latch.await(20, TimeUnit.SECONDS)
latch.assertAwait(20, TimeUnit.SECONDS)
} catch (ex: Exception) {
Log.i("AWSCognitoAuthPluginInstrumentationTests", "Error initializing", ex)
}
Expand All @@ -70,7 +72,6 @@ class AWSCognitoAuthPluginInstrumentationTests {
@Before
fun setup() {
signOut()
Thread.sleep(1000) // ensure signout has time to complete
}

@Test
Expand Down Expand Up @@ -118,19 +119,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
fun fetchAuthSession_can_pull_session_when_signed_in() {
signInWithCognito()

lateinit var session: AuthSession
val latch = CountDownLatch(1)

auth.fetchAuthSession(
{
session = it
latch.countDown()
},
{
latch.countDown()
}
)
latch.await(10.seconds)
val session = syncAuth.fetchAuthSession()

assertTrue(session.isSignedIn)
with(session as AWSCognitoAuthSession) {
Expand All @@ -145,20 +134,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
fun fetchAuthSession_does_not_throw_error_even_when_signed_out() {
signOut()

lateinit var session: AuthSession

val latch = CountDownLatch(1)

auth.fetchAuthSession(
{
session = it
latch.countDown()
},
{
latch.countDown()
}
)
latch.await(10.seconds)
val session = syncAuth.fetchAuthSession()

assertFalse(session.isSignedIn)
with(session as AWSCognitoAuthSession) {
Expand All @@ -173,80 +149,24 @@ class AWSCognitoAuthPluginInstrumentationTests {
fun rememberDevice_succeeds_after_signIn_and_signOut() {
signInWithCognito()

val rememberLatch = CountDownLatch(1)

auth.rememberDevice(
{
rememberLatch.countDown()
},
{
rememberLatch.countDown()
assertTrue(false)
}
)

rememberLatch.await(10.seconds)

val forgetLatch = CountDownLatch(1)

auth.forgetDevice(
{
forgetLatch.countDown()
},
{
forgetLatch.countDown()
assertTrue(false)
}
)

forgetLatch.await(10.seconds)
syncAuth.rememberDevice()
syncAuth.forgetDevice()

signOut()
signInWithCognito()

val rememberLatch2 = CountDownLatch(1)

auth.rememberDevice(
{
rememberLatch2.countDown()
},
{
assertTrue(false)
rememberLatch2.countDown()
}
)

rememberLatch2.await(10.seconds)

val forgetLatch2 = CountDownLatch(1)

auth.forgetDevice(
{
forgetLatch2.countDown()
},
{
assertTrue(false)
forgetLatch2.countDown()
}
)

forgetLatch2.await(10.seconds)
syncAuth.rememberDevice()
syncAuth.forgetDevice()
}

private fun signInWithCognito(synchronous: Boolean = true) {
private fun signInWithCognito() {
val context = ApplicationProvider.getApplicationContext<Context>()
val (username, password) = Credentials.load(context)

val latch = CountDownLatch(1)
auth.signIn(username, password, { latch.countDown() }, { latch.countDown() })

if (synchronous) latch.await()
syncAuth.signIn(username, password)
}

private fun signOut(synchronous: Boolean = true) {
val latch = CountDownLatch(1)
auth.signOut { latch.countDown() }
if (synchronous) latch.await()
private fun signOut() {
syncAuth.signOut()
}

// Creates and starts a HubAccumulator, runs the supplied block, and then stops the accumulator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.amplifyframework.auth.AuthUserAttributeKey
import com.amplifyframework.auth.MFAType
import com.amplifyframework.auth.cognito.helpers.value
import com.amplifyframework.auth.cognito.test.R
import com.amplifyframework.auth.cognito.testutils.blockForCompletion
import com.amplifyframework.auth.options.AuthSignUpOptions
import com.amplifyframework.auth.result.step.AuthSignInStep
import com.amplifyframework.core.AmplifyConfiguration
Expand All @@ -32,8 +33,7 @@ import dev.robinohs.totpkt.otp.totp.TotpGenerator
import dev.robinohs.totpkt.otp.totp.timesupport.generateCode
import java.util.Random
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.seconds
import org.junit.After
import org.junit.Assert
import org.junit.Before
Expand Down Expand Up @@ -143,7 +143,7 @@ class AWSCognitoAuthPluginTOTPTests {
)
synchronousAuth.confirmSignIn(otp)
synchronousAuth.updateUserAttribute(AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+19876543210"))
updateMFAPreference(MFAPreference.ENABLED, MFAPreference.ENABLED, MFAPreference.ENABLED)
updateMFAPreference(MFAPreference.ENABLED, MFAPreference.ENABLED, MFAPreference.DISABLED)
synchronousAuth.signOut()
val signInResult = synchronousAuth.signIn(userName, password)
Assert.assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION, signInResult.nextStep.signInStep)
Expand All @@ -169,8 +169,8 @@ class AWSCognitoAuthPluginTOTPTests {
}

private fun updateMFAPreference(sms: MFAPreference, totp: MFAPreference, email: MFAPreference) {
val latch = CountDownLatch(1)
authPlugin.updateMFAPreference(sms, totp, email, { latch.countDown() }, { latch.countDown() })
latch.await(5, TimeUnit.SECONDS)
blockForCompletion(5.seconds) { onSuccess, onError ->
authPlugin.updateMFAPreference(sms, totp, email, onSuccess, onError)
}
}
}
Loading