Skip to content

Commit 3f2ea63

Browse files
authored
fix(auth): Add userAttributes to confirmSignIn call (#2640)
1 parent 644be2d commit 3f2ea63

File tree

8 files changed

+183
-9
lines changed

8 files changed

+183
-9
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,12 +785,15 @@ internal class RealAWSCognitoAuthPlugin(
785785
},
786786
{
787787
val awsCognitoConfirmSignInOptions = options as? AWSCognitoAuthConfirmSignInOptions
788+
val metadata = awsCognitoConfirmSignInOptions?.metadata ?: emptyMap()
789+
val userAttributes = awsCognitoConfirmSignInOptions?.userAttributes ?: emptyList()
788790
when (signInState) {
789791
is SignInState.ResolvingChallenge -> {
790792
val event = SignInChallengeEvent(
791793
SignInChallengeEvent.EventType.VerifyChallengeAnswer(
792794
challengeResponse,
793-
awsCognitoConfirmSignInOptions?.metadata ?: mapOf()
795+
metadata,
796+
userAttributes
794797
)
795798
)
796799
authStateMachine.send(event)

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.amplifyframework.auth.cognito.actions
1818
import aws.sdk.kotlin.services.cognitoidentityprovider.model.ChallengeNameType
1919
import aws.sdk.kotlin.services.cognitoidentityprovider.model.ResourceNotFoundException
2020
import aws.sdk.kotlin.services.cognitoidentityprovider.respondToAuthChallenge
21+
import com.amplifyframework.auth.AuthUserAttribute
2122
import com.amplifyframework.auth.cognito.AuthEnvironment
2223
import com.amplifyframework.auth.cognito.helpers.AuthHelper
2324
import com.amplifyframework.auth.cognito.helpers.SignInChallengeHelper
@@ -32,9 +33,11 @@ import com.amplifyframework.statemachine.codegen.events.SignInChallengeEvent
3233
internal object SignInChallengeCognitoActions : SignInChallengeActions {
3334
private const val KEY_SECRET_HASH = "SECRET_HASH"
3435
private const val KEY_USERNAME = "USERNAME"
36+
private const val KEY_PREFIX_USER_ATTRIBUTE = "userAttributes."
3537
override fun verifyChallengeAuthAction(
3638
answer: String,
3739
metadata: Map<String, String>,
40+
attributes: List<AuthUserAttribute>,
3841
challenge: AuthChallenge
3942
): Action = Action<AuthEnvironment>("VerifySignInChallenge") { id, dispatcher ->
4043
logger.verbose("$id Starting execution")
@@ -50,6 +53,12 @@ internal object SignInChallengeCognitoActions : SignInChallengeActions {
5053
challengeResponses[responseKey] = answer
5154
}
5255

56+
challengeResponses.putAll(
57+
attributes.map {
58+
Pair("${KEY_PREFIX_USER_ATTRIBUTE}${it.key.keyString}", it.value)
59+
}
60+
)
61+
5362
val secretHash = AuthHelper.getSecretHash(
5463
username,
5564
configuration.userPool?.appClient,
@@ -90,6 +99,7 @@ internal object SignInChallengeCognitoActions : SignInChallengeActions {
9099
SignInChallengeEvent.EventType.RetryVerifyChallengeAnswer(
91100
answer,
92101
metadata,
102+
attributes,
93103
challenge
94104
)
95105
)

aws-auth-cognito/src/main/java/com/amplifyframework/statemachine/codegen/actions/SignInChallengeActions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515

1616
package com.amplifyframework.statemachine.codegen.actions
1717

18+
import com.amplifyframework.auth.AuthUserAttribute
1819
import com.amplifyframework.statemachine.Action
1920
import com.amplifyframework.statemachine.codegen.data.AuthChallenge
2021

2122
internal interface SignInChallengeActions {
2223
fun verifyChallengeAuthAction(
2324
answer: String,
2425
metadata: Map<String, String>,
26+
userAttributes: List<AuthUserAttribute>,
2527
challenge: AuthChallenge
2628
): Action
2729
}

aws-auth-cognito/src/main/java/com/amplifyframework/statemachine/codegen/events/SignInChallengeEvent.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@
1515

1616
package com.amplifyframework.statemachine.codegen.events
1717

18+
import com.amplifyframework.auth.AuthUserAttribute
1819
import com.amplifyframework.statemachine.StateMachineEvent
1920
import com.amplifyframework.statemachine.codegen.data.AuthChallenge
2021
import java.util.Date
2122

2223
internal class SignInChallengeEvent(val eventType: EventType, override val time: Date? = null) : StateMachineEvent {
2324
sealed class EventType {
2425
data class WaitForAnswer(val challenge: AuthChallenge, val hasNewResponse: Boolean = false) : EventType()
25-
data class VerifyChallengeAnswer(val answer: String, val metadata: Map<String, String>) : EventType()
26+
data class VerifyChallengeAnswer(
27+
val answer: String,
28+
val metadata: Map<String, String>,
29+
val userAttributes: List<AuthUserAttribute>
30+
) : EventType()
2631

2732
data class RetryVerifyChallengeAnswer(
2833
val answer: String,
2934
val metadata: Map<String, String>,
35+
val userAttributes: List<AuthUserAttribute>,
3036
val authChallenge: AuthChallenge
3137
) : EventType()
3238
data class FinalizeSignIn(val accessToken: String) : EventType()

aws-auth-cognito/src/main/java/com/amplifyframework/statemachine/codegen/states/SignInChallengeState.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ internal sealed class SignInChallengeState : State {
6060
is WaitingForAnswer -> when (challengeEvent) {
6161
is SignInChallengeEvent.EventType.VerifyChallengeAnswer -> {
6262
val action = challengeActions.verifyChallengeAuthAction(
63-
challengeEvent.answer, challengeEvent.metadata, oldState.challenge
63+
challengeEvent.answer,
64+
challengeEvent.metadata,
65+
challengeEvent.userAttributes,
66+
oldState.challenge
6467
)
6568
StateResolution(Verifying(oldState.challenge.challengeName), listOf(action))
6669
}
@@ -78,7 +81,10 @@ internal sealed class SignInChallengeState : State {
7881
}
7982
is SignInChallengeEvent.EventType.RetryVerifyChallengeAnswer -> {
8083
val action = challengeActions.verifyChallengeAuthAction(
81-
challengeEvent.answer, challengeEvent.metadata, challengeEvent.authChallenge
84+
challengeEvent.answer,
85+
challengeEvent.metadata,
86+
challengeEvent.userAttributes,
87+
challengeEvent.authChallenge,
8288
)
8389
StateResolution(Verifying(challengeEvent.authChallenge.challengeName), listOf(action))
8490
}
@@ -92,7 +98,10 @@ internal sealed class SignInChallengeState : State {
9298
when (challengeEvent) {
9399
is SignInChallengeEvent.EventType.VerifyChallengeAnswer -> {
94100
val action = challengeActions.verifyChallengeAuthAction(
95-
challengeEvent.answer, challengeEvent.metadata, oldState.challenge
101+
challengeEvent.answer,
102+
challengeEvent.metadata,
103+
challengeEvent.userAttributes,
104+
oldState.challenge,
96105
)
97106
StateResolution(Verifying(oldState.challenge.challengeName), listOf(action))
98107
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ open class StateTransitionTestBase {
327327

328328
Mockito.`when`(
329329
mockSignInChallengeActions.verifyChallengeAuthAction(
330+
MockitoHelper.anyObject(),
330331
MockitoHelper.anyObject(),
331332
MockitoHelper.anyObject(),
332333
MockitoHelper.anyObject()

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,12 +387,13 @@ class StateTransitionTests : StateTransitionTestBase() {
387387
SignInChallengeEvent(
388388
SignInChallengeEvent.EventType.RetryVerifyChallengeAnswer(
389389
"test",
390-
mapOf(),
390+
emptyMap(),
391+
emptyList(),
391392
AuthChallenge(
392393
ChallengeNameType.CustomChallenge.toString(),
393394
"Test",
394395
"session_mock_value",
395-
mapOf()
396+
emptyMap(),
396397
)
397398
)
398399
)
@@ -401,7 +402,8 @@ class StateTransitionTests : StateTransitionTestBase() {
401402
SignInChallengeEvent(
402403
SignInChallengeEvent.EventType.VerifyChallengeAnswer(
403404
"test",
404-
mapOf()
405+
emptyMap(),
406+
emptyList()
405407
)
406408
)
407409
)
@@ -481,7 +483,7 @@ class StateTransitionTests : StateTransitionTestBase() {
481483
challengeState?.apply {
482484
stateMachine.send(
483485
SignInChallengeEvent(
484-
SignInChallengeEvent.EventType.VerifyChallengeAnswer("test", mapOf())
486+
SignInChallengeEvent.EventType.VerifyChallengeAnswer("test", emptyMap(), emptyList())
485487
)
486488
)
487489
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package com.amplifyframework.auth.cognito.actions
16+
17+
import androidx.test.core.app.ApplicationProvider
18+
import aws.sdk.kotlin.services.cognitoidentityprovider.CognitoIdentityProviderClient
19+
import aws.sdk.kotlin.services.cognitoidentityprovider.model.RespondToAuthChallengeRequest
20+
import com.amplifyframework.auth.AuthUserAttribute
21+
import com.amplifyframework.auth.AuthUserAttributeKey
22+
import com.amplifyframework.auth.cognito.AWSCognitoAuthService
23+
import com.amplifyframework.auth.cognito.AuthEnvironment
24+
import com.amplifyframework.auth.cognito.StoreClientBehavior
25+
import com.amplifyframework.logging.Logger
26+
import com.amplifyframework.statemachine.EventDispatcher
27+
import com.amplifyframework.statemachine.StateMachineEvent
28+
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
29+
import com.amplifyframework.statemachine.codegen.data.AuthChallenge
30+
import com.amplifyframework.statemachine.codegen.data.AuthConfiguration
31+
import com.amplifyframework.statemachine.codegen.data.CredentialType
32+
import com.amplifyframework.statemachine.codegen.data.UserPoolConfiguration
33+
import io.mockk.coEvery
34+
import io.mockk.every
35+
import io.mockk.mockk
36+
import io.mockk.slot
37+
import junit.framework.TestCase.assertTrue
38+
import kotlin.test.assertEquals
39+
import kotlinx.coroutines.test.runTest
40+
import org.junit.Before
41+
import org.junit.Test
42+
import org.junit.runner.RunWith
43+
import org.robolectric.RobolectricTestRunner
44+
45+
@RunWith(RobolectricTestRunner::class)
46+
class SignInChallengeCognitoActionsTest {
47+
48+
private val pool = mockk<UserPoolConfiguration> {
49+
every { appClient } returns "client"
50+
every { appClientSecret } returns null
51+
every { pinpointAppId } returns null
52+
}
53+
private val configuration = mockk<AuthConfiguration> {
54+
every { userPool } returns pool
55+
}
56+
private val cognitoAuthService = mockk<AWSCognitoAuthService>()
57+
private val credentialStoreClient = mockk<StoreClientBehavior> {
58+
coEvery { loadCredentials(CredentialType.ASF) } returns AmplifyCredential.ASFDevice("asf_id")
59+
}
60+
private val logger = mockk<Logger>(relaxed = true)
61+
private val cognitoIdentityProviderClientMock = mockk<CognitoIdentityProviderClient>()
62+
63+
private val capturedEvent = slot<StateMachineEvent>()
64+
private val dispatcher = mockk<EventDispatcher> {
65+
every { send(capture(capturedEvent)) }.answers { }
66+
}
67+
68+
private lateinit var authEnvironment: AuthEnvironment
69+
70+
@Before
71+
fun setup() {
72+
every { cognitoAuthService.cognitoIdentityProviderClient }.answers { cognitoIdentityProviderClientMock }
73+
authEnvironment = AuthEnvironment(
74+
ApplicationProvider.getApplicationContext(),
75+
configuration,
76+
cognitoAuthService,
77+
credentialStoreClient,
78+
null,
79+
null,
80+
logger
81+
)
82+
}
83+
84+
@Test
85+
fun `very auth challenge without user attributes`() = runTest {
86+
val expectedChallengeResponses = mapOf(
87+
"USERNAME" to "testUser"
88+
)
89+
val capturedRequest = slot<RespondToAuthChallengeRequest>()
90+
coEvery {
91+
cognitoIdentityProviderClientMock.respondToAuthChallenge(capture(capturedRequest))
92+
}.answers {
93+
mockk()
94+
}
95+
96+
SignInChallengeCognitoActions.verifyChallengeAuthAction(
97+
"myAnswer",
98+
emptyMap(),
99+
emptyList(),
100+
AuthChallenge(
101+
"CONFIRM_SIGN_IN_WITH_NEW_PASSWORD",
102+
username = "testUser",
103+
session = null,
104+
parameters = null
105+
)
106+
).execute(dispatcher, authEnvironment)
107+
108+
assertTrue(capturedRequest.isCaptured)
109+
assertEquals(expectedChallengeResponses, capturedRequest.captured.challengeResponses)
110+
}
111+
112+
@Test
113+
fun `user attributes are added to auth challenge`() = runTest {
114+
val providedUserAttributes = listOf(AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+15555555555"))
115+
val expectedChallengeResponses = mapOf(
116+
"USERNAME" to "testUser",
117+
"userAttributes.phone_number" to "+15555555555"
118+
)
119+
val capturedRequest = slot<RespondToAuthChallengeRequest>()
120+
coEvery {
121+
cognitoIdentityProviderClientMock.respondToAuthChallenge(capture(capturedRequest))
122+
}.answers {
123+
mockk()
124+
}
125+
126+
SignInChallengeCognitoActions.verifyChallengeAuthAction(
127+
"myAnswer",
128+
emptyMap(),
129+
providedUserAttributes,
130+
AuthChallenge(
131+
"CONFIRM_SIGN_IN_WITH_NEW_PASSWORD",
132+
username = "testUser",
133+
session = null,
134+
parameters = null
135+
)
136+
).execute(dispatcher, authEnvironment)
137+
138+
assertTrue(capturedRequest.isCaptured)
139+
assertEquals(expectedChallengeResponses, capturedRequest.captured.challengeResponses)
140+
}
141+
}

0 commit comments

Comments
 (0)