amplify-android icon indicating copy to clipboard operation
amplify-android copied to clipboard

WebUI reopens after successful sign up

Open dawidhyzy opened this issue 1 year ago • 10 comments

Before opening, please confirm:

Language and Async Model

Kotlin - Coroutines

Amplify Categories

Authentication

Gradle script dependencies

// Put output below this line
amplify = "2.25.1"

amplifyframework-core = { module = "com.amplifyframework:core-kotlin", version.ref = "amplify" }
amplifyframework-aws-auth-cognito = { module = "com.amplifyframework:aws-auth-cognito", version.ref = "amplify" }

Environment information

# Put output below this line
------------------------------------------------------------
Gradle 8.11.1
------------------------------------------------------------

Build time:    2024-11-20 16:56:46 UTC
Revision:      481cb05a490e0ef9f8620f7873b83bd8a72e7c39

Kotlin:        2.0.20
Groovy:        3.0.22
Ant:           Apache Ant(TM) version 1.10.14 compiled on August 16 2023
Launcher JVM:  17.0.12 (JetBrains s.r.o. 17.0.12+1-b1207.37)
Daemon JVM:    /Users/dawidhyzy/Library/Java/JavaVirtualMachines/jbr-17.0.12-1/Contents/Home (no JDK specified, using current Java home)
OS:            Mac OS X 15.1.1 aarch64

Please include any relevant guides or documentation you're referencing

No response

Describe the bug

After a successful signup, the WebUI closes and then reopens. I can see in my logs that I received a successful result. I can also see logs from AuthClient

2024-12-20 11:45:43.990  9663-9663  AuthClient  com.xxx.yyy.zzz     D  Handling auth redirect response
2024-12-20 11:45:44.037  9663-9663  AuthClient  com.xxx.yyy.zzz     D  CustomTabsManagerActivity was created with a null state.

Reproduction steps (if applicable)

No response

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line


amplifyconfiguration.json

No response

GraphQL Schema

// Put your schema below this line


Additional information and screenshots

No response

dawidhyzy avatar Dec 20 '24 10:12 dawidhyzy

Can you show sample code for how you are calling signInWithWebUI and how you have your redirects set in the manifest? Also, what launchMode is set for the activity you are calling signInWithWebUI in?

tylerjroach avatar Dec 20 '24 14:12 tylerjroach

Sure! We have a single Activity with default launch options.

// Manifest

<application>
        <activity
            android:name="com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="com.smith" />
            </intent-filter>
        </activity>
    </application>

// UI

@Composable
internal fun LoginScreen(
    state: LoginUiState,
    navigateBack: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val activity = LocalContext.current as Activity

    Box(
        modifier.fillMaxSize()
    ) {
        when (state) {
            LoginUiState.Loading, LoginUiState.LoginStarted -> Loading(Modifier.align(Alignment.Center))
            is LoginUiState.Ready -> {
                LaunchedEffect(Unit) {
                    state.startAuthentication(activity)
                }
            }
            LoginUiState.LoggedIn -> LoggedIn(navigateBack = navigateBack)
            LoginUiState.Failed -> LoginFailed(navigateBack = navigateBack)
        }
    }
}

//ViewModel


@Immutable
sealed interface LoginUiState {
    @Stable
    data object Loading : LoginUiState

    @Stable
    data class Ready(
        val startAuthentication: suspend (activity: Activity) -> Unit,
    ) : LoginUiState

    @Stable
    data object LoginStarted : LoginUiState

    @Stable
    data object LoggedIn : LoginUiState

    @Stable
    data object Failed : LoginUiState
}

private enum class LoginResult {
    None,
    Started,
    Success,
    Failed
}

class LoginViewModel @Inject constructor() : ViewModel() {

    private val signInState = MutableStateFlow(LoginResult.None)

    val uiState = stateStream().stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(StateFlowTimeouts.SHARING),
        LoginUiState.Loading
    )

    private fun stateStream(): Flow<LoginUiState> =
        signInState
            .map { loginResult ->
                when (loginResult) {
                    LoginResult.None -> {
                        LoginUiState.Ready(
                            startAuthentication = { activity -> 
                                loginStarted()
                                requireAuth().startAuthentication(activity)
                            }
                        )
                    }

                    LoginResult.Started -> LoginUiState.LoginStarted
                    LoginResult.Success -> LoginUiState.LoggedIn
                    LoginResult.Failed -> LoginUiState.Failed
                }
            }

    private fun Authentication.startAuthentication(activity: Activity) {
        viewModelScope.launch {
            val result = Amplify.Auth.signInWithSocialWebUI(AuthProvider.custom("XYZ"), activity)
            when (result) {
                is AuthSignInResult -> {
                    if (result.isSignedIn) {
                        signInState.emit(LoginResult.Success)
                    } else {
                        signInState.emit(LoginResult.Failed)
                    }
                }

                else -> signInState.emit(LoginResult.Failed)
            }
        }
    }

   private fun loginStarted() {
        viewModelScope.launch {
            signInState.emit(LoginResult.Started)
        }
    }
}

dawidhyzy avatar Dec 20 '24 15:12 dawidhyzy

I can't replicate this but the only thing I'm curious about is the custom auth provider. Who is the provider? My suspicion is that it is related to that. In a test environment, do you have standard signInWithWebUI also configured. I'd be curious if you are seeing the same issue, or seeing the issue on a different provider (if you have multiple).

tylerjroach avatar Dec 20 '24 20:12 tylerjroach

Sorry for the delay but I was away for the holidays.

The provider is used to support legacy service users. This setup works on other platforms: iOS & web and used to work on Android signInWithWebUI and an intermediate web page with provider selection. I am going to downgrade amplify-android till the version that worked and see if the issue persist.

dawidhyzy avatar Jan 06 '25 12:01 dawidhyzy

@tylerjroach downgrading amplify-android didn't fix the issue so I pulled a commit where it worked. I found that at that point we were missing SignInRedirectURI & SignOutRedirectURI in the configuration. It looks like it became required in a higher version than we used at that time 2.14.6.

This got me thinking that the problem is in redirects. Could you point me to documentation about those? Do they need to match the package?

dawidhyzy avatar Jan 07 '25 10:01 dawidhyzy

Hi @dawidhyzy, redirects have always been required as far as I am aware. Redirects are what allows Amplify to capture the code from the browser to exchange for a valid Auth token within the client.

The documentation for adding the redirect to the manifest is here: https://docs.amplify.aws/gen1/android/build-a-backend/auth/add-social-provider/#update-androidmanifestxml

The redirect scheme within the manifest, just needs to match the redirect provided to the Amplify Auth service.

tylerjroach avatar Jan 07 '25 15:01 tylerjroach

We are creating AmplifyConfiguration from Json and it didn't throw an exception if redirects were not present.

I was debugging it more and I can see that JSONObject is escaping the redirect strings for example it contains com.smith:\/\/oauth instead of com.smith://oauth. Could that be a problem?

dawidhyzy avatar Jan 10 '25 07:01 dawidhyzy

Yes the string would need to be "com.smith://oauth"

tylerjroach avatar Jan 10 '25 15:01 tylerjroach

With the help of the Chrome inspection tool, I was able to find out that there was an error shown in the console that navigation was not possible. The problem was that we are using schema environment suffixes for different build variants. After defining the full schema per build variant the redirect works correctly. Is it possible to set a debug flag that would display such an error on the authentication page? That would help with debugging in the future.

dawidhyzy avatar Feb 13 '25 13:02 dawidhyzy

Thank you for the update. I'll mark this as a feature request to see if there is any type of logging info that we can provide when this happens. We don't have control of the web page from a client perspective so any messages would have to try and come within our logs.

tylerjroach avatar Feb 18 '25 14:02 tylerjroach