Skip to content

Commit fe9c2a2

Browse files
committed
Move auto-sign-in to usecase
1 parent 9f7d6e7 commit fe9c2a2

File tree

7 files changed

+287
-112
lines changed

7 files changed

+287
-112
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthService>() {
459459
) = enqueue(onSuccess, onError) { useCaseFactory.listWebAuthnCredentials().execute(options) }
460460

461461
override fun autoSignIn(onSuccess: Consumer<AuthSignInResult>, onError: Consumer<AuthException>) =
462-
enqueue(onSuccess, onError) { queueFacade.autoSignIn() }
462+
enqueue(onSuccess, onError) { useCaseFactory.autoSignIn().execute() }
463463

464464
override fun deleteWebAuthnCredential(credentialId: String, onSuccess: Action, onError: Consumer<AuthException>) =
465465
deleteWebAuthnCredential(credentialId, AuthDeleteWebAuthnCredentialOptions.defaults(), onSuccess, onError)

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,4 @@ internal class KotlinAuthFacadeInternal(private val delegate: RealAWSCognitoAuth
168168
{ continuation.resumeWithException(it) }
169169
)
170170
}
171-
172-
suspend fun autoSignIn(): AuthSignInResult = suspendCoroutine { continuation ->
173-
delegate.autoSignIn(
174-
{ continuation.resume(it) },
175-
{ continuation.resumeWithException(it) }
176-
)
177-
}
178171
}

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

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ import com.amplifyframework.statemachine.codegen.data.HostedUIErrorData
8787
import com.amplifyframework.statemachine.codegen.data.SignInData
8888
import com.amplifyframework.statemachine.codegen.data.SignInMethod
8989
import com.amplifyframework.statemachine.codegen.data.SignOutData
90-
import com.amplifyframework.statemachine.codegen.data.SignUpData
9190
import com.amplifyframework.statemachine.codegen.data.WebAuthnSignInContext
9291
import com.amplifyframework.statemachine.codegen.data.challengeNameType
9392
import com.amplifyframework.statemachine.codegen.errors.SessionError
@@ -110,7 +109,6 @@ import com.amplifyframework.statemachine.codegen.states.SetupTOTPState
110109
import com.amplifyframework.statemachine.codegen.states.SignInChallengeState
111110
import com.amplifyframework.statemachine.codegen.states.SignInState
112111
import com.amplifyframework.statemachine.codegen.states.SignOutState
113-
import com.amplifyframework.statemachine.codegen.states.SignUpState
114112
import com.amplifyframework.statemachine.codegen.states.WebAuthnSignInState
115113
import java.lang.ref.WeakReference
116114
import java.util.concurrent.CountDownLatch
@@ -174,107 +172,6 @@ internal class RealAWSCognitoAuthPlugin(
174172
authStateMachine.state.takeWhile { it !is AuthState.Configured && it !is AuthState.Error }.collect()
175173
}
176174

177-
fun autoSignIn(onSuccess: Consumer<AuthSignInResult>, onError: Consumer<AuthException>) {
178-
authStateMachine.getCurrentState { authState ->
179-
when (authState.authNState) {
180-
is AuthenticationState.NotConfigured -> onError.accept(
181-
InvalidUserPoolConfigurationException()
182-
)
183-
is AuthenticationState.SignedIn -> {
184-
onError.accept(InvalidStateException())
185-
}
186-
is AuthenticationState.SignedOut -> GlobalScope.launch {
187-
when (val signUpState = authState.authSignUpState) {
188-
is SignUpState.SignedUp -> {
189-
_autoSignIn(signUpState.signUpData, onSuccess, onError)
190-
}
191-
else -> onError.accept(InvalidStateException())
192-
}
193-
}
194-
is AuthenticationState.SigningIn -> {
195-
val token = StateChangeListenerToken()
196-
authStateMachine.listen(
197-
token,
198-
{ authState ->
199-
when (authState.authNState) {
200-
is AuthenticationState.SignedOut -> {
201-
authStateMachine.cancel(token)
202-
when (val signUpState = authState.authSignUpState) {
203-
is SignUpState.SignedUp -> GlobalScope.launch {
204-
_autoSignIn(signUpState.signUpData, onSuccess, onError)
205-
}
206-
else -> onError.accept(InvalidStateException())
207-
}
208-
}
209-
else -> Unit
210-
}
211-
},
212-
{
213-
authStateMachine.send(AuthenticationEvent(AuthenticationEvent.EventType.CancelSignIn()))
214-
}
215-
)
216-
}
217-
else -> onError.accept(InvalidStateException())
218-
}
219-
}
220-
}
221-
222-
private suspend fun _autoSignIn(
223-
signUpData: SignUpData,
224-
onSuccess: Consumer<AuthSignInResult>,
225-
onError: Consumer<AuthException>
226-
) {
227-
val token = StateChangeListenerToken()
228-
authStateMachine.listen(
229-
token,
230-
{ authState ->
231-
val authNState = authState.authNState
232-
val authZState = authState.authZState
233-
when {
234-
authNState is AuthenticationState.SigningIn -> {
235-
val signInState = authNState.signInState
236-
when {
237-
signInState is SignInState.Error -> {
238-
authStateMachine.cancel(token)
239-
onError.accept(
240-
CognitoAuthExceptionConverter.lookup(signInState.exception, "Sign in failed.")
241-
)
242-
}
243-
}
244-
}
245-
authNState is AuthenticationState.SignedIn &&
246-
authZState is AuthorizationState.SessionEstablished -> {
247-
authStateMachine.cancel(token)
248-
val authSignInResult = AuthSignInResult(
249-
true,
250-
AuthNextSignInStep(
251-
AuthSignInStep.DONE,
252-
mapOf(),
253-
null,
254-
null,
255-
null,
256-
null
257-
)
258-
)
259-
onSuccess.accept(authSignInResult)
260-
sendHubEvent(AuthChannelEventName.SIGNED_IN.toString())
261-
}
262-
else -> Unit
263-
}
264-
},
265-
{
266-
val signInData = SignInData.AutoSignInData(
267-
signUpData.username,
268-
signUpData.session,
269-
signUpData.clientMetadata ?: mapOf(),
270-
signUpData.userId
271-
)
272-
val event = AuthenticationEvent(AuthenticationEvent.EventType.SignInRequested(signInData))
273-
authStateMachine.send(event)
274-
}
275-
)
276-
}
277-
278175
fun signIn(
279176
username: String?,
280177
password: String?,

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import com.amplifyframework.auth.cognito.AuthStateMachine
2020
import com.amplifyframework.auth.cognito.RealAWSCognitoAuthPlugin
2121
import com.amplifyframework.auth.cognito.helpers.WebAuthnHelper
2222
import com.amplifyframework.auth.cognito.requireIdentityProviderClient
23+
import com.amplifyframework.auth.plugins.core.AuthHubEventEmitter
2324

2425
internal class AuthUseCaseFactory(
2526
private val plugin: RealAWSCognitoAuthPlugin,
2627
private val authEnvironment: AuthEnvironment,
27-
private val stateMachine: AuthStateMachine
28+
private val stateMachine: AuthStateMachine,
29+
private val hubEmitter: AuthHubEventEmitter = AuthHubEventEmitter()
2830
) {
2931

3032
fun fetchAuthSession() = FetchAuthSessionUseCase(plugin)
@@ -140,6 +142,11 @@ internal class AuthUseCaseFactory(
140142
stateMachine = stateMachine
141143
)
142144

145+
fun autoSignIn() = AutoSignInUseCase(
146+
stateMachine = stateMachine,
147+
hubEmitter = hubEmitter
148+
)
149+
143150
fun fetchMfaPreference() = FetchMfaPreferenceUseCase(
144151
client = authEnvironment.requireIdentityProviderClient(),
145152
fetchAuthSession = fetchAuthSession(),
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2025 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+
16+
package com.amplifyframework.auth.cognito.usecases
17+
18+
import com.amplifyframework.auth.AuthChannelEventName
19+
import com.amplifyframework.auth.cognito.AuthStateMachine
20+
import com.amplifyframework.auth.cognito.CognitoAuthExceptionConverter
21+
import com.amplifyframework.auth.cognito.exceptions.configuration.InvalidUserPoolConfigurationException
22+
import com.amplifyframework.auth.exceptions.InvalidStateException
23+
import com.amplifyframework.auth.plugins.core.AuthHubEventEmitter
24+
import com.amplifyframework.auth.result.AuthSignInResult
25+
import com.amplifyframework.auth.result.step.AuthNextSignInStep
26+
import com.amplifyframework.auth.result.step.AuthSignInStep
27+
import com.amplifyframework.statemachine.codegen.data.SignInData
28+
import com.amplifyframework.statemachine.codegen.data.SignUpData
29+
import com.amplifyframework.statemachine.codegen.events.AuthenticationEvent
30+
import com.amplifyframework.statemachine.codegen.states.AuthState
31+
import com.amplifyframework.statemachine.codegen.states.AuthenticationState
32+
import com.amplifyframework.statemachine.codegen.states.AuthorizationState
33+
import com.amplifyframework.statemachine.codegen.states.SignInState
34+
import com.amplifyframework.statemachine.codegen.states.SignUpState
35+
import kotlinx.coroutines.flow.first
36+
import kotlinx.coroutines.flow.onStart
37+
import kotlinx.coroutines.flow.transformWhile
38+
39+
internal class AutoSignInUseCase(
40+
private val stateMachine: AuthStateMachine,
41+
private val hubEmitter: AuthHubEventEmitter
42+
) {
43+
suspend fun execute(): AuthSignInResult {
44+
val authState = waitForSignedOutState()
45+
val signUpData = getSignUpData(authState)
46+
val result = completeAutoSignIn(signUpData)
47+
return result
48+
}
49+
50+
private suspend fun waitForSignedOutState(): AuthState {
51+
val authState = stateMachine.state.transformWhile { authState ->
52+
when (val authNState = authState.authNState) {
53+
is AuthenticationState.NotConfigured -> throw InvalidUserPoolConfigurationException()
54+
is AuthenticationState.SignedOut -> {
55+
emit(authState)
56+
false
57+
}
58+
is AuthenticationState.SigningOut -> true
59+
is AuthenticationState.SigningIn -> {
60+
// Cancel the sign in
61+
stateMachine.send(AuthenticationEvent(AuthenticationEvent.EventType.CancelSignIn()))
62+
true
63+
}
64+
is AuthenticationState.Error -> {
65+
throw CognitoAuthExceptionConverter.lookup(authNState.exception, "Sign in failed.")
66+
}
67+
else -> throw InvalidStateException()
68+
}
69+
}.first()
70+
return authState
71+
}
72+
73+
private fun getSignUpData(authState: AuthState): SignUpData = when (val signUpState = authState.authSignUpState) {
74+
is SignUpState.SignedUp -> signUpState.signUpData
75+
else -> throw InvalidStateException()
76+
}
77+
78+
private suspend fun completeAutoSignIn(signUpData: SignUpData): AuthSignInResult {
79+
val signInData = SignInData.AutoSignInData(
80+
signUpData.username,
81+
signUpData.session,
82+
signUpData.clientMetadata ?: mapOf(),
83+
signUpData.userId
84+
)
85+
86+
val result = stateMachine.stateTransitions
87+
.onStart {
88+
val event = AuthenticationEvent(AuthenticationEvent.EventType.SignInRequested(signInData))
89+
stateMachine.send(event)
90+
}
91+
.transformWhile { authState ->
92+
val authNState = authState.authNState
93+
val authZState = authState.authZState
94+
when {
95+
authNState is AuthenticationState.SigningIn -> {
96+
val signInState = authNState.signInState
97+
if (signInState is SignInState.Error) {
98+
throw CognitoAuthExceptionConverter.lookup(signInState.exception, "Sign in failed.")
99+
}
100+
true
101+
}
102+
authNState is AuthenticationState.SignedIn &&
103+
authZState is AuthorizationState.SessionEstablished -> {
104+
// There are never any next steps for autoSignIn - if it succeeds then the user is fully
105+
// signed in
106+
val authSignInResult = AuthSignInResult(
107+
true,
108+
AuthNextSignInStep(
109+
AuthSignInStep.DONE,
110+
mapOf(),
111+
null,
112+
null,
113+
null,
114+
null
115+
)
116+
)
117+
emit(authSignInResult)
118+
hubEmitter.sendHubEvent(AuthChannelEventName.SIGNED_IN.toString())
119+
false
120+
}
121+
else -> true
122+
}
123+
}.first()
124+
return result
125+
}
126+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,15 @@ class AWSCognitoAuthPluginTest {
841841
}
842842
}
843843

844+
@Test
845+
fun `auto sign in`() {
846+
val useCase = authPlugin.useCaseFactory.autoSignIn()
847+
authPlugin.autoSignIn({}, {})
848+
coVerify(timeout = CHANNEL_TIMEOUT) {
849+
useCase.execute()
850+
}
851+
}
852+
844853
@Test
845854
fun verifyPluginKey() {
846855
assertEquals("awsCognitoAuthPlugin", authPlugin.pluginKey)

0 commit comments

Comments
 (0)