Skip to content

Commit e7db5a7

Browse files
authored
chore(auth): Move SignOut to usecase (#3146)
1 parent 37568a3 commit e7db5a7

File tree

10 files changed

+533
-187
lines changed

10 files changed

+533
-187
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,12 @@ class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthService>() {
404404
override fun signOut(onComplete: Consumer<AuthSignOutResult>) = enqueue(
405405
onComplete,
406406
onError = ::throwIt
407-
) { queueFacade.signOut() }
407+
) { useCaseFactory.signOut().execute() }
408408

409409
override fun signOut(options: AuthSignOutOptions, onComplete: Consumer<AuthSignOutResult>) = enqueue(
410410
onComplete,
411411
onError = ::throwIt
412-
) { queueFacade.signOut(options) }
412+
) { useCaseFactory.signOut().execute(options) }
413413

414414
override fun deleteUser(onSuccess: Action, onError: Consumer<AuthException>) = enqueue(onSuccess, onError) {
415415
useCaseFactory.deleteUser().execute()
@@ -523,7 +523,7 @@ class AWSCognitoAuthPlugin : AuthPlugin<AWSCognitoAuthService>() {
523523
* @param onError Error callback
524524
*/
525525
fun clearFederationToIdentityPool(onSuccess: Action, onError: Consumer<AuthException>) =
526-
enqueue(onSuccess, onError) { queueFacade.clearFederationToIdentityPool() }
526+
enqueue(onSuccess, onError) { useCaseFactory.clearFederationToIdentityPool().execute() }
527527

528528
fun fetchMFAPreference(onSuccess: Consumer<UserMFAPreference>, onError: Consumer<AuthException>) =
529529
enqueue(onSuccess, onError) { useCaseFactory.fetchMfaPreference().execute() }

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

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ import com.amplifyframework.auth.AuthSession
2222
import com.amplifyframework.auth.cognito.options.FederateToIdentityPoolOptions
2323
import com.amplifyframework.auth.cognito.result.FederateToIdentityPoolResult
2424
import com.amplifyframework.auth.options.AuthFetchSessionOptions
25-
import com.amplifyframework.auth.options.AuthSignOutOptions
2625
import com.amplifyframework.auth.options.AuthWebUISignInOptions
2726
import com.amplifyframework.auth.result.AuthSignInResult
28-
import com.amplifyframework.auth.result.AuthSignOutResult
2927
import kotlin.coroutines.resume
3028
import kotlin.coroutines.resumeWithException
3129
import kotlin.coroutines.suspendCoroutine
@@ -92,15 +90,6 @@ internal class KotlinAuthFacadeInternal(private val delegate: RealAWSCognitoAuth
9290
{ continuation.resumeWithException(it) }
9391
)
9492
}
95-
96-
suspend fun signOut(): AuthSignOutResult = suspendCoroutine { continuation ->
97-
delegate.signOut { continuation.resume(it) }
98-
}
99-
100-
suspend fun signOut(options: AuthSignOutOptions): AuthSignOutResult = suspendCoroutine { continuation ->
101-
delegate.signOut(options) { continuation.resume(it) }
102-
}
103-
10493
suspend fun federateToIdentityPool(
10594
providerToken: String,
10695
authProvider: AuthProvider,
@@ -114,11 +103,4 @@ internal class KotlinAuthFacadeInternal(private val delegate: RealAWSCognitoAuth
114103
{ continuation.resumeWithException(it) }
115104
)
116105
}
117-
118-
suspend fun clearFederationToIdentityPool() = suspendCoroutine { continuation ->
119-
delegate.clearFederationToIdentityPool(
120-
{ continuation.resume(Unit) },
121-
{ continuation.resumeWithException(it) }
122-
)
123-
}
124106
}

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

Lines changed: 0 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,9 @@ import com.amplifyframework.auth.cognito.exceptions.service.InvalidAccountTypeEx
3535
import com.amplifyframework.auth.cognito.exceptions.service.UserCancelledException
3636
import com.amplifyframework.auth.cognito.helpers.HostedUIHelper
3737
import com.amplifyframework.auth.cognito.helpers.identityProviderName
38-
import com.amplifyframework.auth.cognito.options.AWSCognitoAuthSignOutOptions
3938
import com.amplifyframework.auth.cognito.options.AWSCognitoAuthWebUISignInOptions
4039
import com.amplifyframework.auth.cognito.options.FederateToIdentityPoolOptions
41-
import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult
4240
import com.amplifyframework.auth.cognito.result.FederateToIdentityPoolResult
43-
import com.amplifyframework.auth.cognito.result.GlobalSignOutError
44-
import com.amplifyframework.auth.cognito.result.HostedUIError
45-
import com.amplifyframework.auth.cognito.result.RevokeTokenError
4641
import com.amplifyframework.auth.exceptions.ConfigurationException
4742
import com.amplifyframework.auth.exceptions.InvalidStateException
4843
import com.amplifyframework.auth.exceptions.NotAuthorizedException
@@ -51,13 +46,10 @@ import com.amplifyframework.auth.exceptions.SessionExpiredException
5146
import com.amplifyframework.auth.exceptions.SignedOutException
5247
import com.amplifyframework.auth.exceptions.UnknownException
5348
import com.amplifyframework.auth.options.AuthFetchSessionOptions
54-
import com.amplifyframework.auth.options.AuthSignOutOptions
5549
import com.amplifyframework.auth.options.AuthWebUISignInOptions
5650
import com.amplifyframework.auth.result.AuthSignInResult
57-
import com.amplifyframework.auth.result.AuthSignOutResult
5851
import com.amplifyframework.auth.result.step.AuthNextSignInStep
5952
import com.amplifyframework.auth.result.step.AuthSignInStep
60-
import com.amplifyframework.core.Action
6153
import com.amplifyframework.core.Amplify
6254
import com.amplifyframework.core.Consumer
6355
import com.amplifyframework.hub.HubChannel
@@ -69,7 +61,6 @@ import com.amplifyframework.statemachine.codegen.data.FederatedToken
6961
import com.amplifyframework.statemachine.codegen.data.HostedUIErrorData
7062
import com.amplifyframework.statemachine.codegen.data.SignInData
7163
import com.amplifyframework.statemachine.codegen.data.SignInMethod
72-
import com.amplifyframework.statemachine.codegen.data.SignOutData
7364
import com.amplifyframework.statemachine.codegen.errors.SessionError
7465
import com.amplifyframework.statemachine.codegen.events.AuthEvent
7566
import com.amplifyframework.statemachine.codegen.events.AuthenticationEvent
@@ -493,114 +484,6 @@ internal class RealAWSCognitoAuthPlugin(
493484
)
494485
}
495486

496-
fun signOut(onComplete: Consumer<AuthSignOutResult>) {
497-
signOut(AuthSignOutOptions.builder().build(), onComplete)
498-
}
499-
500-
fun signOut(options: AuthSignOutOptions, onComplete: Consumer<AuthSignOutResult>) {
501-
authStateMachine.getCurrentState { authState ->
502-
when (authState.authNState) {
503-
is AuthenticationState.NotConfigured ->
504-
onComplete.accept(AWSCognitoAuthSignOutResult.CompleteSignOut)
505-
// Continue sign out and clear auth or guest credentials
506-
is AuthenticationState.SignedIn, is AuthenticationState.SignedOut -> {
507-
// Send SignOut event here instead of OnSubscribedCallback handler to ensure we do not fire
508-
// onComplete immediately, which would happen if calling signOut while signed out
509-
val event = AuthenticationEvent(
510-
AuthenticationEvent.EventType.SignOutRequested(
511-
SignOutData(
512-
options.isGlobalSignOut,
513-
(options as? AWSCognitoAuthSignOutOptions)?.browserPackage
514-
)
515-
)
516-
)
517-
authStateMachine.send(event)
518-
_signOut(onComplete = onComplete)
519-
}
520-
is AuthenticationState.FederatedToIdentityPool -> {
521-
onComplete.accept(
522-
AWSCognitoAuthSignOutResult.FailedSignOut(
523-
InvalidStateException(
524-
"The user is currently federated to identity pool. " +
525-
"You must call clearFederationToIdentityPool to clear credentials."
526-
)
527-
)
528-
)
529-
}
530-
else -> onComplete.accept(
531-
AWSCognitoAuthSignOutResult.FailedSignOut(InvalidStateException())
532-
)
533-
}
534-
}
535-
}
536-
537-
private fun _signOut(sendHubEvent: Boolean = true, onComplete: Consumer<AuthSignOutResult>) {
538-
val token = StateChangeListenerToken()
539-
var cancellationException: UserCancelledException? = null
540-
authStateMachine.listen(
541-
token,
542-
{ authState ->
543-
if (authState is AuthState.Configured) {
544-
val (authNState, authZState) = authState
545-
when {
546-
authNState is AuthenticationState.SignedOut && authZState is AuthorizationState.Configured -> {
547-
authStateMachine.cancel(token)
548-
if (authNState.signedOutData.hasError) {
549-
val signedOutData = authNState.signedOutData
550-
onComplete.accept(
551-
AWSCognitoAuthSignOutResult.PartialSignOut(
552-
hostedUIError = signedOutData.hostedUIErrorData?.let { HostedUIError(it) },
553-
globalSignOutError = signedOutData.globalSignOutErrorData?.let {
554-
GlobalSignOutError(it)
555-
},
556-
revokeTokenError = signedOutData.revokeTokenErrorData?.let {
557-
RevokeTokenError(
558-
it
559-
)
560-
}
561-
)
562-
)
563-
if (sendHubEvent) {
564-
sendHubEvent(AuthChannelEventName.SIGNED_OUT.toString())
565-
}
566-
} else {
567-
onComplete.accept(AWSCognitoAuthSignOutResult.CompleteSignOut)
568-
if (sendHubEvent) {
569-
sendHubEvent(AuthChannelEventName.SIGNED_OUT.toString())
570-
}
571-
}
572-
}
573-
authNState is AuthenticationState.Error -> {
574-
authStateMachine.cancel(token)
575-
onComplete.accept(
576-
AWSCognitoAuthSignOutResult.FailedSignOut(
577-
CognitoAuthExceptionConverter.lookup(authNState.exception, "Sign out failed.")
578-
)
579-
)
580-
}
581-
authNState is AuthenticationState.SigningOut -> {
582-
val state = authNState.signOutState
583-
if (state is SignOutState.Error && state.exception is UserCancelledException) {
584-
cancellationException = state.exception
585-
}
586-
}
587-
authNState is AuthenticationState.SignedIn && cancellationException != null -> {
588-
authStateMachine.cancel(token)
589-
cancellationException?.let {
590-
onComplete.accept(AWSCognitoAuthSignOutResult.FailedSignOut(it))
591-
}
592-
}
593-
else -> {
594-
// No - op
595-
}
596-
}
597-
}
598-
},
599-
{
600-
}
601-
)
602-
}
603-
604487
private fun addAuthStateChangeListener() {
605488
authStateMachine.listen(
606489
StateChangeListenerToken(),
@@ -732,46 +615,6 @@ internal class RealAWSCognitoAuthPlugin(
732615
)
733616
}
734617

735-
fun clearFederationToIdentityPool(onSuccess: Action, onError: Consumer<AuthException>) {
736-
authStateMachine.getCurrentState { authState ->
737-
val authNState = authState.authNState
738-
val authZState = authState.authZState
739-
when {
740-
authState is AuthState.Configured &&
741-
(
742-
authNState is AuthenticationState.FederatedToIdentityPool &&
743-
authZState is AuthorizationState.SessionEstablished
744-
) ||
745-
(
746-
authZState is AuthorizationState.Error &&
747-
authZState.exception is SessionError &&
748-
authZState.exception.amplifyCredential is AmplifyCredential.IdentityPoolFederated
749-
) -> {
750-
val event = AuthenticationEvent(AuthenticationEvent.EventType.ClearFederationToIdentityPool())
751-
authStateMachine.send(event)
752-
_clearFederationToIdentityPool(onSuccess, onError)
753-
}
754-
else -> {
755-
onError.accept(InvalidStateException("Clearing of federation failed."))
756-
}
757-
}
758-
}
759-
}
760-
761-
private fun _clearFederationToIdentityPool(onSuccess: Action, onError: Consumer<AuthException>) {
762-
_signOut(sendHubEvent = false) {
763-
when (it) {
764-
is AWSCognitoAuthSignOutResult.FailedSignOut -> {
765-
onError.accept(it.exception)
766-
}
767-
else -> {
768-
onSuccess.call()
769-
sendHubEvent(AWSCognitoAuthChannelEventName.FEDERATION_TO_IDENTITY_POOL_CLEARED.toString())
770-
}
771-
}
772-
}
773-
}
774-
775618
private fun sendHubEvent(eventName: String) {
776619
Amplify.Hub.publish(HubChannel.AUTH, HubEvent.create(eventName))
777620
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,13 @@ internal class AuthUseCaseFactory(
167167
)
168168

169169
fun confirmSignIn() = ConfirmSignInUseCase(stateMachine = stateMachine)
170+
171+
fun signOut() = SignOutUseCase(
172+
stateMachine = stateMachine
173+
)
174+
175+
fun clearFederationToIdentityPool() = ClearFederationToIdentityPoolUseCase(
176+
stateMachine = stateMachine,
177+
signOut = signOut()
178+
)
170179
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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.cognito.AWSCognitoAuthChannelEventName
19+
import com.amplifyframework.auth.cognito.AuthStateMachine
20+
import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult
21+
import com.amplifyframework.auth.exceptions.InvalidStateException
22+
import com.amplifyframework.auth.plugins.core.AuthHubEventEmitter
23+
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
24+
import com.amplifyframework.statemachine.codegen.errors.SessionError
25+
import com.amplifyframework.statemachine.codegen.events.AuthenticationEvent
26+
import com.amplifyframework.statemachine.codegen.states.AuthState
27+
import com.amplifyframework.statemachine.codegen.states.AuthenticationState
28+
import com.amplifyframework.statemachine.codegen.states.AuthorizationState
29+
30+
internal class ClearFederationToIdentityPoolUseCase(
31+
private val stateMachine: AuthStateMachine,
32+
private val signOut: SignOutUseCase,
33+
private val emitter: AuthHubEventEmitter = AuthHubEventEmitter()
34+
) {
35+
suspend fun execute() {
36+
val authState = stateMachine.getCurrentState()
37+
38+
when {
39+
authState.isFederatedToIdentityPool() -> {
40+
val event = AuthenticationEvent(AuthenticationEvent.EventType.ClearFederationToIdentityPool())
41+
when (val result = signOut.completeSignOut(event = event, sendHubEvent = false)) {
42+
is AWSCognitoAuthSignOutResult.FailedSignOut -> throw result.exception
43+
else -> emitter.sendHubEvent(
44+
AWSCognitoAuthChannelEventName.FEDERATION_TO_IDENTITY_POOL_CLEARED.toString()
45+
)
46+
}
47+
}
48+
else -> throw InvalidStateException("Clearing of federation failed.")
49+
}
50+
}
51+
52+
private fun AuthState.isFederatedToIdentityPool(): Boolean {
53+
val authNState = this.authNState
54+
val authZState = this.authZState
55+
56+
return this is AuthState.Configured &&
57+
(
58+
authNState is AuthenticationState.FederatedToIdentityPool &&
59+
authZState is AuthorizationState.SessionEstablished
60+
) ||
61+
(
62+
authZState is AuthorizationState.Error &&
63+
authZState.exception is SessionError &&
64+
authZState.exception.amplifyCredential is AmplifyCredential.IdentityPoolFederated
65+
)
66+
}
67+
}

0 commit comments

Comments
 (0)