diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt index 737611ee2..7f952212d 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt @@ -316,12 +316,12 @@ class FirebaseAuthUI private constructor( /** * Updates the internal authentication state. - * This method is intended for internal use by authentication operations. + * This method can be used to manually trigger state updates when the Firebase Auth state + * listener doesn't automatically detect changes (e.g., after reloading user properties). * * @param state The new [AuthState] to emit - * @suppress This is an internal API */ - internal fun updateAuthState(state: AuthState) { + fun updateAuthState(state: AuthState) { _authStateFlow.value = state } diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt b/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt index 097cf9c0a..ffc697698 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt @@ -154,6 +154,12 @@ abstract class AuthProvider(open val providerId: String, open val providerName: * A list of custom password validation rules. */ val passwordValidationRules: List, + + /** + * Optional custom button label to differentiate between multiple email providers. + * If null, uses the default string from stringProvider. Defaults to null. + */ + val buttonLabel: String? = null, ) : AuthProvider(providerId = Provider.EMAIL.id, providerName = Provider.EMAIL.providerName) { companion object { const val SESSION_ID_LENGTH = 10 diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt b/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt index b0ad943db..5186860b2 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt @@ -153,6 +153,10 @@ internal fun resolveProviderLabel( context: android.content.Context ): String = when (provider) { is AuthProvider.GenericOAuth -> provider.buttonLabel + is AuthProvider.Email -> { + // Use custom button label if provided, otherwise use default + provider.buttonLabel ?: stringProvider.signInWithEmail + } is AuthProvider.Apple -> { // Use Apple-specific locale if provided, otherwise use default stringProvider if (provider.locale != null) { diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt index 48864c3c6..0d5891987 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt @@ -322,8 +322,32 @@ fun FirebaseAuthScreen( onReloadUser = { coroutineScope.launch { try { + // Reload user to get fresh data from server authUI.getCurrentUser()?.reload() authUI.getCurrentUser()?.getIdToken(true) + + // Check the user's email verification status after reload + val user = authUI.getCurrentUser() + if (user != null) { + // If email is now verified, transition to Success state + if (user.isEmailVerified) { + authUI.updateAuthState( + AuthState.Success( + result = null, + user = user, + isNewUser = false + ) + ) + } else { + // Email still not verified, keep showing verification screen + authUI.updateAuthState( + AuthState.RequiresEmailVerification( + user = user, + email = user.email ?: "" + ) + ) + } + } } catch (e: Exception) { Log.e("FirebaseAuthScreen", "Failed to refresh user", e) } diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt index 2e9ab97e3..7f8ce7293 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt @@ -310,7 +310,8 @@ fun EmailAuthScreen( } else { DefaultEmailAuthContent( configuration = configuration, - state = state + state = state, + onCancel = onCancel ) } } @@ -318,7 +319,8 @@ fun EmailAuthScreen( @Composable private fun DefaultEmailAuthContent( configuration: AuthUIConfiguration, - state: EmailAuthContentState + state: EmailAuthContentState, + onCancel: () -> Unit ) { when (state.mode) { EmailAuthMode.SignIn -> { @@ -332,7 +334,8 @@ private fun DefaultEmailAuthContent( onPasswordChange = state.onPasswordChange, onSignInClick = state.onSignInClick, onGoToSignUp = state.onGoToSignUp, - onGoToResetPassword = state.onGoToResetPassword + onGoToResetPassword = state.onGoToResetPassword, + onNavigateBack = onCancel ) } diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt index c3c335a23..fb3cf4ca7 100644 --- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt @@ -25,10 +25,14 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -73,6 +77,7 @@ fun SignInUI( onSignInClick: () -> Unit, onGoToSignUp: () -> Unit, onGoToResetPassword: () -> Unit, + onNavigateBack: (() -> Unit)? = null, ) { val provider = configuration.providers.filterIsInstance().first() val stringProvider = LocalAuthUIStringProvider.current @@ -138,6 +143,16 @@ fun SignInUI( modifier = Modifier.semantics { heading() } ) }, + navigationIcon = { + if (onNavigateBack != null) { + IconButton(onClick = onNavigateBack) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringProvider.backAction + ) + } + } + }, colors = AuthUITheme.topAppBarColors ) }, diff --git a/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt b/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt index 0992ca793..b7204a02d 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt +++ b/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt @@ -56,6 +56,23 @@ class HighLevelApiDemoActivity : ComponentActivity() { serverClientId = "771411398215-o39fujhds88bs4mb5ai7u6o73g86fspp.apps.googleusercontent.com", ) ) + // Email/Password Sign-in + provider( + AuthProvider.Email( + isDisplayNameRequired = true, + isEmailLinkSignInEnabled = false, + isNewAccountsAllowed = true, + minimumPasswordLength = 8, + passwordValidationRules = listOf( + PasswordRule.MinimumLength(8), + PasswordRule.RequireLowercase, + PasswordRule.RequireUppercase, + ), + emailLinkActionCodeSettings = null, + buttonLabel = "Sign in with Email" + ) + ) + // Email Link Sign-in (passwordless) provider( AuthProvider.Email( isDisplayNameRequired = true, @@ -76,7 +93,8 @@ class HighLevelApiDemoActivity : ComponentActivity() { PasswordRule.MinimumLength(8), PasswordRule.RequireLowercase, PasswordRule.RequireUppercase, - ) + ), + buttonLabel = "Sign in with Email Link" ) ) provider(