Skip to content

Commit fe06ea5

Browse files
authored
Merge branch 'main' into mattcreaser/remove-version
2 parents 85c4c3c + 30fccd4 commit fe06ea5

File tree

7 files changed

+179
-29
lines changed

7 files changed

+179
-29
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44
- feat(Geo): Add Kotlin Geo Facade (#2155)
55
- Add a network status listener to restart DataStore after the network comes back online. (#2148)
6+
- feat(auth): Overriding sign in when the State machine is already in the signing in state (#2187)
67

78
### Miscellaneous
89
- chore: Remove deprecated maven plugin (#2137)
@@ -17,6 +18,8 @@
1718
- chore: Upgrade Gradle, AGP, and KtLint (#2172)
1819
- Add a buildspec file for nightly tests (#2180)
1920
- Chore(Auth): Implementation of the custom auth with SRP parity testing use case (#2167)
21+
- chore: Add PR checker workflow (#2188)
22+
- fix(auth): Fix for when loading credentials the success/error is fired twice (#2184)
2023

2124
### Bug fixes
2225
- fix(core): remove unused dynamic nav dependency (#2132)

aws-analytics-pinpoint/src/androidTest/java/com/amplifyframework/analytics/pinpoint/PinpointAnalyticsInstrumentationTest.kt

+19-11
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class PinpointAnalyticsInstrumentationTest {
9090

9191
// Act: Record the event
9292
Amplify.Analytics.recordEvent(event)
93-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
93+
Sleep.milliseconds(DEFAULT_TIMEOUT)
9494
Amplify.Analytics.flushEvents()
9595
val hubEvents = hubAccumulator.await(10, TimeUnit.SECONDS)
9696
val submittedEvents = combineAndFilterEvents(hubEvents)
@@ -118,15 +118,15 @@ class PinpointAnalyticsInstrumentationTest {
118118
.addProperty("DemoDoubleProperty2", 2.0)
119119
.build()
120120
Amplify.Analytics.recordEvent(event1)
121-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
121+
Sleep.milliseconds(DEFAULT_TIMEOUT)
122122
val eventName2 = "Amplify-event" + UUID.randomUUID().toString()
123123
val event2 = AnalyticsEvent.builder()
124124
.name(eventName2)
125125
.addProperty("DemoProperty1", "DemoValue1")
126126
.addProperty("DemoProperty2", 2.0)
127127
.build()
128128
Amplify.Analytics.recordEvent(event2)
129-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
129+
Sleep.milliseconds(DEFAULT_TIMEOUT)
130130
Amplify.Analytics.flushEvents()
131131
val hubEvents = analyticsHubEventAccumulator.await(10, TimeUnit.SECONDS)
132132
val hubEvent = analyticsHubEventAccumulator.awaitFirst()
@@ -189,9 +189,9 @@ class PinpointAnalyticsInstrumentationTest {
189189

190190
// Act: Record two events: the one created above and another just with a key
191191
Amplify.Analytics.recordEvent(event)
192-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
192+
Sleep.milliseconds(DEFAULT_TIMEOUT)
193193
Amplify.Analytics.recordEvent("amplify-test-event")
194-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
194+
Sleep.milliseconds(DEFAULT_TIMEOUT)
195195
Amplify.Analytics.flushEvents()
196196
val hubEvents = analyticsHubEventAccumulator.await(10, TimeUnit.SECONDS)
197197
val submittedEvents = combineAndFilterEvents(hubEvents)
@@ -226,10 +226,10 @@ class PinpointAnalyticsInstrumentationTest {
226226

227227
// Act: Record an event, unregister the global property, and record another event
228228
Amplify.Analytics.recordEvent("amplify-test-event-with-property")
229-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
229+
Sleep.milliseconds(DEFAULT_TIMEOUT)
230230
Amplify.Analytics.unregisterGlobalProperties("GlobalProperty")
231231
Amplify.Analytics.recordEvent("amplify-test-event-without-property")
232-
Sleep.milliseconds(RECORD_INSERTION_TIMEOUT)
232+
Sleep.milliseconds(DEFAULT_TIMEOUT)
233233
Amplify.Analytics.flushEvents()
234234
val hubEvents = analyticsHubEventAccumulator.await(10, TimeUnit.SECONDS)
235235
val submittedEvents = combineAndFilterEvents(hubEvents)
@@ -282,9 +282,16 @@ class PinpointAnalyticsInstrumentationTest {
282282
.customProperties(properties)
283283
.userAttributes(userAttributes)
284284
.build()
285-
Amplify.Analytics.identifyUser(UUID.randomUUID().toString(), pinpointUserProfile)
285+
val uuid = UUID.randomUUID().toString()
286+
Amplify.Analytics.identifyUser(uuid, pinpointUserProfile)
286287
Sleep.milliseconds(PINPOINT_ROUNDTRIP_TIMEOUT)
287-
val endpointResponse = fetchEndpointResponse()
288+
var endpointResponse = fetchEndpointResponse()
289+
var retry_count = 0
290+
while (retry_count < MAX_RETRIES && endpointResponse.attributes.isNullOrEmpty()) {
291+
Sleep.milliseconds(DEFAULT_TIMEOUT)
292+
endpointResponse = fetchEndpointResponse()
293+
retry_count++
294+
}
288295
assertCommonEndpointResponseProperties(endpointResponse)
289296
Assert.assertEquals(
290297
"User attribute value",
@@ -310,7 +317,7 @@ class PinpointAnalyticsInstrumentationTest {
310317
}
311318

312319
private fun assertCommonEndpointResponseProperties(endpointResponse: EndpointResponse) {
313-
Log.i("DEBUG", endpointResponse.toString())
320+
Log.d("PinpointAnalyticsInstrumentationTest", endpointResponse.toString())
314321
val attributes = endpointResponse.attributes!!
315322
Assert.assertEquals("[email protected]", attributes["email"]!![0])
316323
Assert.assertEquals("test-user", attributes["name"]!![0])
@@ -369,7 +376,8 @@ class PinpointAnalyticsInstrumentationTest {
369376
private const val CONFIGURATION_NAME = "amplifyconfiguration"
370377
private const val COGNITO_CONFIGURATION_TIMEOUT = 10 * 1000L
371378
private const val PINPOINT_ROUNDTRIP_TIMEOUT = 10 * 1000L
372-
private const val RECORD_INSERTION_TIMEOUT = 5 * 1000L
379+
private const val DEFAULT_TIMEOUT = 5 * 1000L
380+
private const val MAX_RETRIES = 10
373381
private const val UNIQUE_ID_KEY = "UniqueId"
374382
private const val PREFERENCES_AND_FILE_MANAGER_SUFFIX = "515d6767-01b7-49e5-8273-c8d11b0f331d"
375383
private lateinit var synchronousAuth: SynchronousAuth

aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPlugin.kt

+47-5
Original file line numberDiff line numberDiff line change
@@ -464,19 +464,37 @@ internal class RealAWSCognitoAuthPlugin(
464464
onError: Consumer<AuthException>
465465
) {
466466
authStateMachine.getCurrentState { authState ->
467+
val signInOptions = options as? AWSCognitoAuthSignInOptions ?: AWSCognitoAuthSignInOptions.builder()
468+
.authFlowType(configuration.authFlowType)
469+
.build()
467470
when (authState.authNState) {
468471
is AuthenticationState.NotConfigured -> onError.accept(
469472
InvalidUserPoolConfigurationException()
470473
)
471474
// Continue sign in
472-
is AuthenticationState.SignedOut, is AuthenticationState.Configured -> {
473-
val signInOptions = options as? AWSCognitoAuthSignInOptions ?: AWSCognitoAuthSignInOptions.builder()
474-
.authFlowType(configuration.authFlowType)
475-
.build()
476-
475+
is AuthenticationState.SignedOut,
476+
is AuthenticationState.Configured -> {
477477
_signIn(username, password, signInOptions, onSuccess, onError)
478478
}
479479
is AuthenticationState.SignedIn -> onError.accept(SignedInException())
480+
is AuthenticationState.SigningIn -> {
481+
val token = StateChangeListenerToken()
482+
authStateMachine.listen(
483+
token,
484+
{ authState ->
485+
when (authState.authNState) {
486+
is AuthenticationState.SignedOut -> {
487+
authStateMachine.cancel(token)
488+
_signIn(username, password, signInOptions, onSuccess, onError)
489+
}
490+
else -> Unit
491+
}
492+
},
493+
{
494+
authStateMachine.send(AuthenticationEvent(AuthenticationEvent.EventType.CancelSignIn()))
495+
}
496+
)
497+
}
480498
else -> onError.accept(InvalidStateException())
481499
}
482500
}
@@ -706,6 +724,30 @@ internal class RealAWSCognitoAuthPlugin(
706724
)
707725
}
708726
is AuthenticationState.SignedIn -> onError.accept(SignedInException())
727+
is AuthenticationState.SigningIn -> {
728+
val token = StateChangeListenerToken()
729+
authStateMachine.listen(
730+
token,
731+
{ authState ->
732+
when (authState.authNState) {
733+
is AuthenticationState.SignedOut -> {
734+
authStateMachine.cancel(token)
735+
_signInWithHostedUI(
736+
callingActivity = callingActivity,
737+
options = options,
738+
onSuccess = onSuccess,
739+
onError = onError,
740+
provider = provider
741+
)
742+
}
743+
else -> Unit
744+
}
745+
},
746+
{
747+
authStateMachine.send(AuthenticationEvent(AuthenticationEvent.EventType.CancelSignIn()))
748+
}
749+
)
750+
}
709751
else -> onError.accept(InvalidStateException())
710752
}
711753
}

aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPluginTest.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package com.amplifyframework.auth.cognito
1818
import aws.sdk.kotlin.services.cognitoidentityprovider.CognitoIdentityProviderClient
1919
import aws.sdk.kotlin.services.cognitoidentityprovider.model.AnalyticsMetadataType
2020
import aws.sdk.kotlin.services.cognitoidentityprovider.model.AttributeType
21-
import aws.sdk.kotlin.services.cognitoidentityprovider.model.ChangePasswordRequest
2221
import aws.sdk.kotlin.services.cognitoidentityprovider.model.ChangePasswordResponse
2322
import aws.sdk.kotlin.services.cognitoidentityprovider.model.CodeDeliveryDetailsType
2423
import aws.sdk.kotlin.services.cognitoidentityprovider.model.CognitoIdentityProviderException
@@ -44,7 +43,6 @@ import com.amplifyframework.auth.AuthException
4443
import com.amplifyframework.auth.AuthUserAttribute
4544
import com.amplifyframework.auth.AuthUserAttributeKey
4645
import com.amplifyframework.auth.cognito.exceptions.configuration.InvalidUserPoolConfigurationException
47-
import com.amplifyframework.auth.cognito.exceptions.invalidstate.SignedInException
4846
import com.amplifyframework.auth.cognito.helpers.AuthHelper
4947
import com.amplifyframework.auth.cognito.helpers.SRPHelper
5048
import com.amplifyframework.auth.cognito.options.AWSCognitoAuthResendUserAttributeConfirmationCodeOptions
@@ -223,6 +221,7 @@ class RealAWSCognitoAuthPluginTest {
223221
val expectedAuthError = InvalidUserPoolConfigurationException()
224222
currentState = AuthenticationState.NotConfigured()
225223

224+
coEvery { authConfiguration.authFlowType } returns AuthFlowType.USER_SRP_AUTH
226225
// WHEN
227226
plugin.signIn("user", "password", AuthSignInOptions.defaults(), onSuccess, onError)
228227

@@ -236,7 +235,7 @@ class RealAWSCognitoAuthPluginTest {
236235
// GIVEN
237236
val onSuccess = mockk<Consumer<AuthSignInResult>>()
238237
val onError = mockk<Consumer<AuthException>>(relaxed = true)
239-
val expectedAuthError = SignedInException()
238+
coEvery { authConfiguration.authFlowType } returns AuthFlowType.USER_SRP_AUTH
240239
currentState = AuthenticationState.SignedIn(
241240
SignedInData(
242241
"userId",
@@ -253,7 +252,7 @@ class RealAWSCognitoAuthPluginTest {
253252

254253
// THEN
255254
verify(exactly = 0) { onSuccess.accept(any()) }
256-
verify { onError.accept(expectedAuthError) }
255+
verify { onError.accept(any()) }
257256
}
258257

259258
@Test
@@ -290,7 +289,7 @@ class RealAWSCognitoAuthPluginTest {
290289
}
291290

292291
coEvery {
293-
authService.cognitoIdentityProviderClient?.changePassword(any<ChangePasswordRequest>())
292+
authService.cognitoIdentityProviderClient?.changePassword(any())
294293
} returns ChangePasswordResponse.invoke { }
295294

296295
// WHEN

aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/featuretest/generators/testcasegenerators/SignInTestCaseGenerator.kt

+30-1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,30 @@ object SignInTestCaseGenerator : SerializableProvider {
271271
)
272272
)
273273

274+
private val signInWhenAlreadySigningInAuthCase = FeatureTestCase(
275+
description = "Test that overriding signIn when already signing in returns success",
276+
preConditions = PreConditions(
277+
"authconfiguration.json",
278+
"SigningIn_SigningIn.json",
279+
mockedResponses = listOf(
280+
mockedInitiateAuthResponse,
281+
mockedSMSChallengeResponse,
282+
)
283+
),
284+
api = API(
285+
AuthAPI.signIn,
286+
params = mapOf(
287+
"username" to username,
288+
"password" to password
289+
).toJsonElement(),
290+
options = JsonObject(emptyMap())
291+
),
292+
validations = listOf(
293+
mockedSignInSMSChallengeExpectation,
294+
ExpectationShapes.State("SigningIn_SigningIn.json")
295+
)
296+
)
297+
274298
private val customAuthCase = FeatureTestCase(
275299
description = "Test that Custom Auth signIn invokes proper cognito request and returns custom challenge",
276300
preConditions = PreConditions(
@@ -325,6 +349,11 @@ object SignInTestCaseGenerator : SerializableProvider {
325349
)
326350

327351
override val serializables: List<Any> = listOf(
328-
baseCase, challengeCase, deviceSRPTestCase, customAuthCase, customAuthWithSRPCase
352+
baseCase,
353+
challengeCase,
354+
deviceSRPTestCase,
355+
customAuthCase,
356+
customAuthWithSRPCase,
357+
signInWhenAlreadySigningInAuthCase
329358
)
330359
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"description": "Test that overriding signIn when already signing in returns success",
3+
"preConditions": {
4+
"amplify-configuration": "authconfiguration.json",
5+
"state": "SigningIn_SigningIn.json",
6+
"mockedResponses": [
7+
{
8+
"type": "cognitoIdentityProvider",
9+
"apiName": "initiateAuth",
10+
"responseType": "success",
11+
"response": {
12+
"challengeName": "PASSWORD_VERIFIER",
13+
"challengeParameters": {
14+
"SALT": "abc",
15+
"SECRET_BLOCK": "secretBlock",
16+
"SRP_B": "def",
17+
"USERNAME": "username",
18+
"USER_ID_FOR_SRP": "userId"
19+
}
20+
}
21+
},
22+
{
23+
"type": "cognitoIdentityProvider",
24+
"apiName": "respondToAuthChallenge",
25+
"responseType": "success",
26+
"response": {
27+
"session": "someSession",
28+
"challengeName": "SMS_MFA",
29+
"challengeParameters": {
30+
"CODE_DELIVERY_DELIVERY_MEDIUM": "SMS",
31+
"CODE_DELIVERY_DESTINATION": "+12345678900"
32+
}
33+
}
34+
}
35+
]
36+
},
37+
"api": {
38+
"name": "signIn",
39+
"params": {
40+
"username": "username",
41+
"password": "password"
42+
},
43+
"options": {
44+
}
45+
},
46+
"validations": [
47+
{
48+
"type": "amplify",
49+
"apiName": "signIn",
50+
"responseType": "success",
51+
"response": {
52+
"isSignedIn": false,
53+
"nextStep": {
54+
"signInStep": "CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE",
55+
"additionalInfo": {
56+
},
57+
"codeDeliveryDetails": {
58+
"destination": "+12345678900",
59+
"deliveryMedium": "SMS"
60+
}
61+
}
62+
}
63+
},
64+
{
65+
"type": "state",
66+
"expectedState": "SigningIn_SigningIn.json"
67+
}
68+
]
69+
}

testutils/src/main/java/com/amplifyframework/testutils/FileAssert.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ private static void assertStreamEqualStream(
7373
final byte[] expectedDigest;
7474
final byte[] actualDigest;
7575
try {
76-
expectedDigest = calculateMD5Digest(expectedInputStream);
77-
actualDigest = calculateMD5Digest(actualInputStream);
76+
expectedDigest = calculateDigest(expectedInputStream);
77+
actualDigest = calculateDigest(actualInputStream);
7878
Assert.assertArrayEquals(expectedDigest, actualDigest);
7979
} finally {
8080
expectedInputStream.close();
@@ -83,24 +83,24 @@ private static void assertStreamEqualStream(
8383
}
8484

8585
@SuppressWarnings("MagicNumber") // Buffer size
86-
private static byte[] calculateMD5Digest(InputStream stream) {
86+
private static byte[] calculateDigest(InputStream stream) {
8787
final byte[] buffer = new byte[2_048];
88-
final MessageDigest md5;
88+
final MessageDigest digest;
8989
try {
90-
md5 = MessageDigest.getInstance("MD5");
90+
digest = MessageDigest.getInstance("SHA-256");
9191
} catch (NoSuchAlgorithmException noMd5AvailableError) {
9292
throw new RuntimeException("No MD5 algorithm available to use for hashing.", noMd5AvailableError);
9393
}
9494

9595
int bytesRead;
9696
try {
9797
while ((bytesRead = stream.read(buffer)) != -1) {
98-
md5.update(buffer, 0, bytesRead);
98+
digest.update(buffer, 0, bytesRead);
9999
}
100100
} catch (IOException readError) {
101101
throw new RuntimeException("Failed to read input stream.", readError);
102102
}
103103

104-
return md5.digest();
104+
return digest.digest();
105105
}
106106
}

0 commit comments

Comments
 (0)