From 5583dca6940f311459162e9ab0253ef05d9750a5 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Mon, 27 Jul 2026 11:47:59 +0100 Subject: [PATCH 1/4] fix(auth): pre-fill email when "Continue as" button is tapped (#2423) The "Continue as..." button displayed the saved identifier but discarded it on click, sending the user to a blank email form. Add an onContinueAsSelected callback to AuthMethodPicker that carries the identifier through to EmailAuthScreen, which now initializes the email field with the saved address. --- .../ui/auth/ui/method_picker/AuthMethodPicker.kt | 8 +++++++- .../firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt | 11 ++++++++++- .../ui/auth/ui/screens/email/EmailAuthScreen.kt | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt index feb04fb7c..d93c42059 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt @@ -82,6 +82,9 @@ class MethodPickerTermsConfiguration( * @param providers The list of providers to display. * @param logo An optional logo to display. * @param onProviderSelected A callback when a provider is selected. + * @param onContinueAsSelected A callback when the "Continue as..." button is selected, with the + * provider and saved identifier (email, phone number, etc.). Falls back to [onProviderSelected] + * if not provided. * @param customLayout An optional custom layout composable for the provider buttons. * @param termsOfServiceUrl The URL for the Terms of Service. * @param privacyPolicyUrl The URL for the Privacy Policy. @@ -97,12 +100,15 @@ fun AuthMethodPicker( providers: List, logo: AuthUIAsset? = null, onProviderSelected: (AuthProvider) -> Unit, + onContinueAsSelected: ((AuthProvider, String?) -> Unit)? = null, termsOfServiceUrl: String? = null, privacyPolicyUrl: String? = null, lastSignInPreference: SignInPreferenceManager.SignInPreference? = null, customLayout: (@Composable (List, (AuthProvider) -> Unit) -> Unit)? = null, termsConfiguration: MethodPickerTermsConfiguration? = null, ) { + val continueAsHandler: (AuthProvider, String?) -> Unit = + onContinueAsSelected ?: { provider, _ -> onProviderSelected(provider) } val context = LocalContext.current val inPreview = LocalInspectionMode.current val stringProvider = LocalAuthUIStringProvider.current @@ -148,7 +154,7 @@ fun AuthMethodPicker( provider = lastProvider, identifier = preference.identifier, enabled = providerButtonsEnabled, - onClick = { onProviderSelected(lastProvider) } + onClick = { continueAsHandler(lastProvider, preference.identifier) } ) Spacer(modifier = Modifier.height(24.dp)) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt index fffcf5905..78c9b8ab7 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt @@ -147,6 +147,7 @@ fun FirebaseAuthScreen( val pendingReauthState = remember { mutableStateOf(null) } val pendingReauthOperation = remember { mutableStateOf<(suspend (android.content.Context) -> Unit)?>(null) } val emailLinkFromDifferentDevice = remember { mutableStateOf(null) } + val prefillEmail = remember { mutableStateOf(null) } val lastSignInPreference = remember { mutableStateOf(null) } val startRoute = remember(configuration.providers, configuration.isProviderChoiceAlwaysShown) { @@ -218,7 +219,14 @@ fun FirebaseAuthScreen( lastSignInPreference = lastSignInPreference.value, customLayout = customMethodPickerLayout, termsConfiguration = customMethodPickerTermsConfiguration, - onProviderSelected = onProviderSelected, + onProviderSelected = { provider -> + prefillEmail.value = null + onProviderSelected(provider) + }, + onContinueAsSelected = { provider, identifier -> + prefillEmail.value = identifier + onProviderSelected(provider) + }, ) } } @@ -228,6 +236,7 @@ fun FirebaseAuthScreen( context = context, configuration = configuration, authUI = authUI, + prefillEmail = prefillEmail.value, credentialForLinking = pendingLinkingCredential.value, emailLinkFromDifferentDevice = emailLinkFromDifferentDevice.value, onContinueWithProvider = continueWithProvider, diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt index 667fc364b..468830443 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt @@ -129,6 +129,7 @@ fun EmailAuthScreen( context: Context, configuration: AuthUIConfiguration, authUI: FirebaseAuthUI, + prefillEmail: String? = null, credentialForLinking: AuthCredential? = null, emailLinkFromDifferentDevice: String? = null, onContinueWithProvider: (String) -> Unit = {}, @@ -150,7 +151,7 @@ fun EmailAuthScreen( } val mode = rememberSaveable { mutableStateOf(initialMode) } val displayNameValue = rememberSaveable { mutableStateOf("") } - val emailTextValue = rememberSaveable { mutableStateOf("") } + val emailTextValue = rememberSaveable { mutableStateOf(prefillEmail ?: "") } val passwordTextValue = rememberSaveable { mutableStateOf("") } val confirmPasswordTextValue = rememberSaveable { mutableStateOf("") } From 6268a0d1f81bffc5600f6a123166538a7d304959 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Mon, 27 Jul 2026 11:57:29 +0100 Subject: [PATCH 2/4] fix(auth): only pre-fill email for Email provider, not phone/other --- .../java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt index 78c9b8ab7..1a161099b 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt @@ -224,7 +224,7 @@ fun FirebaseAuthScreen( onProviderSelected(provider) }, onContinueAsSelected = { provider, identifier -> - prefillEmail.value = identifier + prefillEmail.value = if (provider is AuthProvider.Email) identifier else null onProviderSelected(provider) }, ) From 59df1d677d85babae08e62e5704af0344fdec8f3 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Mon, 27 Jul 2026 12:10:07 +0100 Subject: [PATCH 3/4] fix(auth): move new parameters to end of signature to preserve API compat --- .../firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt | 8 ++++---- .../firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt index d93c42059..456464f56 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt @@ -82,15 +82,15 @@ class MethodPickerTermsConfiguration( * @param providers The list of providers to display. * @param logo An optional logo to display. * @param onProviderSelected A callback when a provider is selected. - * @param onContinueAsSelected A callback when the "Continue as..." button is selected, with the - * provider and saved identifier (email, phone number, etc.). Falls back to [onProviderSelected] - * if not provided. * @param customLayout An optional custom layout composable for the provider buttons. * @param termsOfServiceUrl The URL for the Terms of Service. * @param privacyPolicyUrl The URL for the Privacy Policy. * @param lastSignInPreference The last sign-in preference to show a "Continue as..." button. * @param termsConfiguration Optional configuration for a custom ToS/Privacy Policy footer. * When provided, replaces the default "By continuing..." text. See [MethodPickerTermsConfiguration]. + * @param onContinueAsSelected A callback when the "Continue as..." button is selected, with the + * provider and saved identifier (email, phone number, etc.). Falls back to [onProviderSelected] + * if not provided. * * @since 10.0.0 */ @@ -100,12 +100,12 @@ fun AuthMethodPicker( providers: List, logo: AuthUIAsset? = null, onProviderSelected: (AuthProvider) -> Unit, - onContinueAsSelected: ((AuthProvider, String?) -> Unit)? = null, termsOfServiceUrl: String? = null, privacyPolicyUrl: String? = null, lastSignInPreference: SignInPreferenceManager.SignInPreference? = null, customLayout: (@Composable (List, (AuthProvider) -> Unit) -> Unit)? = null, termsConfiguration: MethodPickerTermsConfiguration? = null, + onContinueAsSelected: ((AuthProvider, String?) -> Unit)? = null, ) { val continueAsHandler: (AuthProvider, String?) -> Unit = onContinueAsSelected ?: { provider, _ -> onProviderSelected(provider) } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt index 468830443..cb4d8e05e 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt @@ -129,13 +129,13 @@ fun EmailAuthScreen( context: Context, configuration: AuthUIConfiguration, authUI: FirebaseAuthUI, - prefillEmail: String? = null, credentialForLinking: AuthCredential? = null, emailLinkFromDifferentDevice: String? = null, onContinueWithProvider: (String) -> Unit = {}, onSuccess: (AuthResult) -> Unit, onError: (AuthException) -> Unit, onCancel: () -> Unit, + prefillEmail: String? = null, content: @Composable ((EmailAuthContentState) -> Unit)? = null, ) { val provider = configuration.providers.filterIsInstance().first() From 10809b005349a0220b6cb2e03eb09b296eac61e9 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi <17249207+just1and0@users.noreply.github.com> Date: Wed, 29 Jul 2026 10:05:20 +0100 Subject: [PATCH 4/4] fix(auth): address review feedback for prefill email feature - Update kDoc to say "email address" instead of "phone number" since phone pre-fill is not wired up in this PR - Add SignInUITest with assertions for email pre-fill behavior --- .../auth/ui/method_picker/AuthMethodPicker.kt | 4 +- .../ui/auth/ui/screens/email/SignInUITest.kt | 119 ++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 auth/src/test/java/com/firebase/ui/auth/ui/screens/email/SignInUITest.kt diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt index d0d8cb89f..26c2feaed 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt @@ -89,7 +89,7 @@ class MethodPickerTermsConfiguration( * @param termsConfiguration Optional configuration for a custom ToS/Privacy Policy footer. * When provided, replaces the default "By continuing..." text. See [MethodPickerTermsConfiguration]. * @param onContinueAsSelected A callback when the "Continue as..." button is selected, with the - * provider and saved identifier (email, phone number, etc.). Falls back to [onProviderSelected] + * provider and saved identifier (email address). Falls back to [onProviderSelected] * if not provided. * * @since 10.0.0 @@ -224,7 +224,7 @@ fun AuthMethodPicker( * A prominent "Continue as..." button that shows the last-used provider and identifier. * * @param provider The authentication provider - * @param identifier The user identifier (email, phone number, etc.) + * @param identifier The user identifier (email address) * @param onClick Callback when the button is clicked */ @Composable diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/SignInUITest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/SignInUITest.kt new file mode 100644 index 000000000..1b016419d --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/SignInUITest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.firebase.ui.auth.ui.screens.email + +import android.content.Context +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.test.assertTextContains +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.LocalAuthUIStringProvider +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@Config(sdk = [34]) +@RunWith(RobolectricTestRunner::class) +class SignInUITest { + + @get:Rule + val composeTestRule = createComposeRule() + + private lateinit var applicationContext: Context + private lateinit var stringProvider: AuthUIStringProvider + + @Before + fun setUp() { + applicationContext = ApplicationProvider.getApplicationContext() + stringProvider = DefaultAuthUIStringProvider(applicationContext) + } + + @Test + fun `email field is pre-filled when initial email value is provided`() { + val prefillEmail = "user@example.com" + val provider = AuthProvider.Email( + isDisplayNameRequired = false, + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + val configuration = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + } + + composeTestRule.setContent { + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + SignInUI( + configuration = configuration, + isLoading = false, + emailSignInLinkSent = false, + email = prefillEmail, + password = "", + onEmailChange = { }, + onPasswordChange = { }, + onRetrievedCredential = { }, + onSignInClick = { }, + onGoToSignUp = { }, + onGoToResetPassword = { }, + onGoToEmailLinkSignIn = { }, + ) + } + } + + composeTestRule.onNodeWithText(prefillEmail).assertExists() + } + + @Test + fun `email field is empty when no initial email value is provided`() { + val provider = AuthProvider.Email( + isDisplayNameRequired = false, + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + val configuration = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + } + + composeTestRule.setContent { + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + SignInUI( + configuration = configuration, + isLoading = false, + emailSignInLinkSent = false, + email = "", + password = "", + onEmailChange = { }, + onPasswordChange = { }, + onRetrievedCredential = { }, + onSignInClick = { }, + onGoToSignUp = { }, + onGoToResetPassword = { }, + onGoToEmailLinkSignIn = { }, + ) + } + } + + composeTestRule.onNodeWithText("user@example.com").assertDoesNotExist() + } +}