Demo

FirebaseUI Auth is a modern, Compose-based authentication library that provides drop-in UI components for Firebase Authentication. It eliminates boilerplate code and promotes best practices for user authentication on Android.
Built entirely with Jetpack Compose and Material Design 3, FirebaseUI Auth offers:
Equivalent FirebaseUI libraries are available for iOS and Web.

Ensure your application is configured for use with Firebase. See the Firebase documentation for setup instructions.
Minimum Requirements:
Add the FirebaseUI Auth library dependency to your build.gradle.kts (Module):
dependencies {
// FirebaseUI for Auth
implementation("com.firebaseui:firebase-ui-auth:10.0.0-beta01")
// Required: Firebase Auth
implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
implementation("com.google.firebase:firebase-auth")
// Required: Jetpack Compose
implementation(platform("androidx.compose:compose-bom:2024.01.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
// Optional: Facebook Login (if using FacebookAuthProvider)
implementation("com.facebook.android:facebook-login:16.3.0")
}
Localization Support:
To optimize APK size, configure resource filtering for only the languages your app supports:
android {
defaultConfig {
resourceConfigurations += listOf("en", "es", "fr") // Add your supported languages
}
}
Google Sign-In configuration is automatically provided by the google-services Gradle plugin. Ensure you have enabled Google Sign-In in the Firebase Console.
If using Facebook Login, add your Facebook App ID to strings.xml:
<resources>
<string name="facebook_application_id" translatable="false">YOUR_FACEBOOK_APP_ID</string>
<string name="facebook_login_protocol_scheme" translatable="false">fbYOUR_FACEBOOK_APP_ID</string>
</resources>
See the Facebook for Developers documentation for setup instructions.
Twitter, GitHub, Microsoft, Yahoo, and Apple providers require configuration in the Firebase Console but no additional Android-specific setup. See the Firebase Auth documentation for provider-specific instructions.
Here's the simplest way to add authentication to your app with Email and Google Sign-In:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
val configuration = authUIConfiguration {
providers = listOf(
AuthProvider.Email(),
AuthProvider.Google()
)
}
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { result ->
Toast.makeText(this, "Welcome!", Toast.LENGTH_SHORT).show()
// Navigate to main app screen
},
onSignInFailure = { exception ->
Toast.makeText(this, "Error: ${exception.message}", Toast.LENGTH_SHORT).show()
},
onSignInCancelled = {
finish()
}
)
}
}
}
}
That's it! This provides a complete authentication flow with:
Before showing the authentication UI, check if a user is already signed in:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authUI = FirebaseAuthUI.getInstance()
if (authUI.isSignedIn()) {
// User is already signed in, navigate to main app
startActivity(Intent(this, MainAppActivity::class.java))
finish()
} else {
// Show authentication UI
setContent {
FirebaseAuthScreen(/* ... */)
}
}
}
}
Or observe authentication state changes reactively:
@Composable
fun AuthGate() {
val authUI = remember { FirebaseAuthUI.getInstance() }
val authState by authUI.authStateFlow().collectAsState(initial = AuthState.Idle)
when {
authState is AuthState.Success -> {
// User is signed in
MainAppScreen()
}
else -> {
// Show authentication
FirebaseAuthScreen(/* ... */)
}
}
}
FirebaseAuthUI is the central class that coordinates all authentication operations. It manages UI state and provides methods for signing in, signing up, and managing user accounts.
// Get the default instance
val authUI = FirebaseAuthUI.getInstance()
// Or get an instance for a specific Firebase app
val customApp = Firebase.app("secondary")
val authUI = FirebaseAuthUI.getInstance(customApp)
// Or create with custom auth (for multi-tenancy)
val customAuth = Firebase.auth(customApp)
val authUI = FirebaseAuthUI.create(auth = customAuth)
Key Methods:
| Method | Return Type | Description |
|---|---|---|
isSignedIn() |
Boolean |
Checks if a user is currently signed in |
getCurrentUser() |
FirebaseUser? |
Returns the current user, if signed in |
authStateFlow() |
Flow<AuthState> |
Observes authentication state changes |
createAuthFlow(config) |
AuthFlowController |
Creates a sign-in flow controller |
signOut(context) |
suspend fun |
Signs out the current user |
delete(context) |
suspend fun |
Deletes the current user account |
AuthUIConfiguration defines all settings for your authentication flow. Use the DSL builder function for easy configuration:
val configuration = authUIConfiguration {
// Required: List of authentication providers
providers = listOf(
AuthProvider.Email(),
AuthProvider.Google(),
AuthProvider.Phone()
)
// Optional: Theme configuration
theme = AuthUITheme.fromMaterialTheme()
// Optional: Terms of Service and Privacy Policy URLs
tosUrl = "https://example.com/terms"
privacyPolicyUrl = "https://example.com/privacy"
// Optional: App logo
logo = Icons.Default.AccountCircle
// Optional: Enable MFA (default: true)
isMfaEnabled = true
// Optional: Enable Credential Manager (default: true)
isCredentialManagerEnabled = true
// Optional: Allow anonymous user upgrade (default: false)
isAnonymousUpgradeEnabled = true
// Optional: Require display name on sign-up (default: true)
isDisplayNameRequired = true
// Optional: Allow new email accounts (default: true)
isNewEmailAccountsAllowed = true
// Optional: Always show provider choice even with one provider (default: false)
isProviderChoiceAlwaysShown = false
// Optional: Custom string provider for localization
stringProvider = MyCustomStringProvider()
// Optional: Locale override
locale = Locale.FRENCH
}
AuthFlowController manages the lifecycle of an authentication flow programmatically. This is the low-level API for advanced use cases.
val controller = authUI.createAuthFlow(configuration)
lifecycleScope.launch {
// Start the flow
val state = controller.start()
when (state) {
is AuthState.Success -> {
// Handle success
val user = state.result.user
}
is AuthState.Error -> {
// Handle error
Log.e(TAG, "Auth failed", state.exception)
}
is AuthState.Cancelled -> {
// User cancelled
}
else -> {
// Handle other states (RequiresMfa, RequiresEmailVerification, etc.)
}
}
}
// Cancel the flow if needed
controller.cancel()
// Clean up when done
override fun onDestroy() {
super.onDestroy()
controller.dispose()
}
AuthState represents the current state of authentication:
sealed class AuthState {
object Idle : AuthState()
data class Loading(val message: String?) : AuthState()
data class Success(val result: AuthResult, val isNewUser: Boolean) : AuthState()
data class Error(val exception: AuthException, val isRecoverable: Boolean) : AuthState()
data class RequiresMfa(val resolver: MultiFactorResolver) : AuthState()
data class RequiresEmailVerification(val user: FirebaseUser) : AuthState()
data class RequiresProfileCompletion(val user: FirebaseUser) : AuthState()
object Cancelled : AuthState()
}
Configure email/password authentication with optional customization:
val emailProvider = AuthProvider.Email(
// Optional: Require display name (default: true)
isDisplayNameRequired = true,
// Optional: Enable email link sign-in (default: false)
isEmailLinkSignInEnabled = true,
// Optional: Force email link on same device (default: true)
isEmailLinkForceSameDeviceEnabled = true,
// Optional: Action code settings for email link
emailLinkActionCodeSettings = actionCodeSettings {
url = "https://example.com/auth"
handleCodeInApp = true
setAndroidPackageName(packageName, true, null)
},
// Optional: Allow new accounts (default: true)
isNewAccountsAllowed = true,
// Optional: Minimum password length (default: 6)
minimumPasswordLength = 8,
// Optional: Custom password validation rules
passwordValidationRules = listOf(
PasswordRule.MinimumLength(8),
PasswordRule.RequireUppercase,
PasswordRule.RequireLowercase,
PasswordRule.RequireDigit,
PasswordRule.RequireSpecialCharacter
)
)
val configuration = authUIConfiguration {
providers = listOf(emailProvider)
}
Configure phone number authentication with SMS verification:
val phoneProvider = AuthProvider.Phone(
// Optional: Default phone number in international format
defaultNumber = "+15551234567",
// Optional: Default country code (ISO alpha-2 format)
defaultCountryCode = "US",
// Optional: Allowed countries
allowedCountries = listOf("US", "CA", "GB"),
// Optional: SMS code length (default: 6)
smsCodeLength = 6,
// Optional: Timeout for SMS delivery in seconds (default: 60)
timeout = 60L,
// Optional: Enable instant verification (default: true)
isInstantVerificationEnabled = true
)
val configuration = authUIConfiguration {
providers = listOf(phoneProvider)
}
Configure Google Sign-In with optional scopes and server client ID:
val googleProvider = AuthProvider.Google(
// Required: Scopes to request
scopes = listOf("https://www.googleapis.com/auth/drive.file"),
// Optional: Server client ID for backend authentication
serverClientId = "YOUR_SERVER_CLIENT_ID.apps.googleusercontent.com",
// Optional: Custom OAuth parameters
customParameters = mapOf("prompt" to "select_account")
)
val configuration = authUIConfiguration {
providers = listOf(googleProvider)
}
Configure Facebook Login with optional permissions:
val facebookProvider = AuthProvider.Facebook(
// Optional: Facebook application ID (reads from strings.xml if not provided)
applicationId = "YOUR_FACEBOOK_APP_ID",
// Optional: Permissions to request (default: ["email", "public_profile"])
scopes = listOf("email", "public_profile", "user_friends"),
// Optional: Custom OAuth parameters
customParameters = mapOf("display" to "popup")
)
val configuration = authUIConfiguration {
providers = listOf(facebookProvider)
}
FirebaseUI supports Twitter, GitHub, Microsoft, Yahoo, and Apple:
// Twitter
val twitterProvider = AuthProvider.Twitter(
// Required: Custom OAuth parameters
customParameters = mapOf("lang" to "en")
)
// GitHub
val githubProvider = AuthProvider.Github(
// Optional: Scopes to request (default: ["user:email"])
scopes = listOf("user:email", "read:user"),
// Required: Custom OAuth parameters
customParameters = mapOf("allow_signup" to "false")
)
// Microsoft
val microsoftProvider = AuthProvider.Microsoft(
// Optional: Scopes to request (default: ["openid", "profile", "email"])
scopes = listOf("openid", "profile", "email", "User.Read"),
// Optional: Tenant ID for Azure Active Directory
tenant = "YOUR_TENANT_ID",
// Required: Custom OAuth parameters
customParameters = mapOf("prompt" to "consent")
)
// Yahoo
val yahooProvider = AuthProvider.Yahoo(
// Optional: Scopes to request (default: ["openid", "profile", "email"])
scopes = listOf("openid", "profile", "email"),
// Required: Custom OAuth parameters
customParameters = mapOf("language" to "en-us")
)
// Apple
val appleProvider = AuthProvider.Apple(
// Optional: Scopes to request (default: ["name", "email"])
scopes = listOf("name", "email"),
// Optional: Locale for the sign-in page
locale = "en_US",
// Required: Custom OAuth parameters
customParameters = mapOf("ui_locales" to "en-US")
)
val configuration = authUIConfiguration {
providers = listOf(
twitterProvider,
githubProvider,
microsoftProvider,
yahooProvider,
appleProvider
)
}
Enable anonymous authentication to let users use your app without signing in:
val configuration = authUIConfiguration {
providers = listOf(
AuthProvider.Anonymous()
)
// Enable anonymous user upgrade
isAnonymousUpgradeEnabled = true
}
Support any OAuth provider configured in the Firebase Console:
val lineProvider = AuthProvider.GenericOAuth(
// Required: Provider name
providerName = "LINE",
// Required: Provider ID as configured in Firebase Console
providerId = "oidc.line",
// Required: Scopes to request
scopes = listOf("profile", "openid", "email"),
// Required: Custom OAuth parameters
customParameters = mapOf("prompt" to "consent"),
// Required: Button label
buttonLabel = "Sign in with LINE",
// Optional: Custom button icon
buttonIcon = AuthUIAsset.Resource(R.drawable.ic_line),
// Optional: Custom button background color
buttonColor = Color(0xFF06C755),
// Optional: Custom button content color
contentColor = Color.White
)
val configuration = authUIConfiguration {
providers = listOf(lineProvider)
}
The high-level API provides a complete, opinionated authentication experience with minimal code:
@Composable
fun AuthenticationScreen() {
val configuration = authUIConfiguration {
providers = listOf(
AuthProvider.Email(),
AuthProvider.Google(),
AuthProvider.Facebook(),
AuthProvider.Phone()
)
tosUrl = "https://example.com/terms"
privacyPolicyUrl = "https://example.com/privacy"
logo = Icons.Default.Lock
}
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { result ->
val user = result.user
val isNewUser = result.additionalUserInfo?.isNewUser ?: false
if (isNewUser) {
// First-time user
navigateToOnboarding()
} else {
// Returning user
navigateToHome()
}
},
onSignInFailure = { exception ->
when (exception) {
is AuthException.NetworkException -> {
showSnackbar("No internet connection")
}
is AuthException.TooManyRequestsException -> {
showSnackbar("Too many attempts. Please try again later.")
}
else -> {
showSnackbar("Authentication failed: ${exception.message}")
}
}
},
onSignInCancelled = {
navigateBack()
}
)
}
For maximum control, use the AuthFlowController:
class AuthActivity : ComponentActivity() {
private lateinit var controller: AuthFlowController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authUI = FirebaseAuthUI.getInstance()
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email(), AuthProvider.Google())
}
controller = authUI.createAuthFlow(configuration)
lifecycleScope.launch {
val state = controller.start()
handleAuthState(state)
}
}
private fun handleAuthState(state: AuthState) {
when (state) {
is AuthState.Success -> {
// Successfully signed in
val user = state.result.user
startActivity(Intent(this, MainActivity::class.java))
finish()
}
is AuthState.Error -> {
// Handle error
AlertDialog.Builder(this)
.setTitle("Authentication Failed")
.setMessage(state.exception.message)
.setPositiveButton("OK", null)
.show()
}
is AuthState.RequiresMfa -> {
// User needs to complete MFA challenge
showMfaChallengeDialog(state.resolver)
}
is AuthState.RequiresEmailVerification -> {
// Email verification needed
showEmailVerificationScreen(state.user)
}
is AuthState.Cancelled -> {
// User cancelled authentication
finish()
}
else -> {
// Handle other states
}
}
}
override fun onDestroy() {
super.onDestroy()
controller.dispose()
}
}
For complete UI control while keeping authentication logic, use content slots:
@Composable
fun CustomEmailAuth() {
val emailConfig = AuthProvider.Email(
passwordValidationRules = listOf(
PasswordRule.MinimumLength(8),
PasswordRule.RequireDigit
)
)
EmailAuthScreen(
configuration = emailConfig,
onSuccess = { /* ... */ },
onError = { /* ... */ },
onCancel = { /* ... */ }
) { state ->
// Custom UI with full control
when (state.mode) {
EmailAuthMode.SignIn -> {
CustomSignInUI(state)
}
EmailAuthMode.SignUp -> {
CustomSignUpUI(state)
}
EmailAuthMode.ResetPassword -> {
CustomResetPasswordUI(state)
}
}
}
}
@Composable
fun CustomSignInUI(state: EmailAuthContentState) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Welcome Back!",
style = MaterialTheme.typography.headlineLarge
)
Spacer(modifier = Modifier.height(32.dp))
OutlinedTextField(
value = state.email,
onValueChange = state.onEmailChange,
label = { Text("Email") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = state.password,
onValueChange = state.onPasswordChange,
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
modifier = Modifier.fillMaxWidth()
)
if (state.error != null) {
Text(
text = state.error!!,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(top = 8.dp)
)
}
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = state.onSignInClick,
enabled = !state.isLoading,
modifier = Modifier.fillMaxWidth()
) {
if (state.isLoading) {
CircularProgressIndicator(modifier = Modifier.size(24.dp))
} else {
Text("Sign In")
}
}
TextButton(onClick = state.onGoToResetPassword) {
Text("Forgot Password?")
}
TextButton(onClick = state.onGoToSignUp) {
Text("Create Account")
}
}
}
Similarly, create custom phone authentication UI:
@Composable
fun CustomPhoneAuth() {
val phoneConfig = AuthProvider.Phone(defaultCountryCode = "US")
PhoneAuthScreen(
configuration = phoneConfig,
onSuccess = { /* ... */ },
onError = { /* ... */ },
onCancel = { /* ... */ }
) { state ->
when (state.step) {
PhoneAuthStep.EnterPhoneNumber -> {
CustomPhoneNumberInput(state)
}
PhoneAuthStep.EnterVerificationCode -> {
CustomVerificationCodeInput(state)
}
}
}
}
Enable and configure Multi-Factor Authentication:
val mfaConfig = MfaConfiguration(
// Allowed MFA factors (default: [Sms, Totp])
allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp),
// Optional: Require MFA enrollment (default: false)
requireEnrollment = false,
// Optional: Enable recovery codes (default: true)
enableRecoveryCodes = true
)
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email())
isMfaEnabled = true
}
Prompt users to enroll in MFA after sign-in:
@Composable
fun MfaEnrollmentFlow() {
val currentUser = FirebaseAuth.getInstance().currentUser
if (currentUser != null) {
val mfaConfig = MfaConfiguration(
allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp)
)
MfaEnrollmentScreen(
user = currentUser,
configuration = mfaConfig,
onEnrollmentComplete = {
Toast.makeText(context, "MFA enrolled successfully!", Toast.LENGTH_SHORT).show()
navigateToHome()
},
onSkip = {
navigateToHome()
}
)
}
}
Or with custom UI:
MfaEnrollmentScreen(
user = currentUser,
configuration = mfaConfig,
onEnrollmentComplete = { /* ... */ },
onSkip = { /* ... */ }
) { state ->
when (state.step) {
MfaEnrollmentStep.SelectFactor -> {
CustomFactorSelectionUI(state)
}
MfaEnrollmentStep.ConfigureSms -> {
CustomSmsConfigurationUI(state)
}
MfaEnrollmentStep.ConfigureTotp -> {
CustomTotpConfigurationUI(state)
}
MfaEnrollmentStep.VerifyFactor -> {
CustomVerificationUI(state)
}
MfaEnrollmentStep.ShowRecoveryCodes -> {
CustomRecoveryCodesUI(state)
}
}
}
Handle MFA challenges during sign-in. The challenge is automatically detected:
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { result ->
navigateToHome()
},
onSignInFailure = { exception ->
// MFA challenges are handled automatically by FirebaseAuthScreen
// But you can also handle them manually:
if (exception is AuthException.MfaRequiredException) {
showMfaChallengeScreen(exception.resolver)
}
}
)
Or handle manually:
@Composable
fun ManualMfaChallenge(resolver: MultiFactorResolver) {
MfaChallengeScreen(
resolver = resolver,
onChallengeComplete = { assertion ->
// Complete sign-in with the assertion
lifecycleScope.launch {
try {
val result = resolver.resolveSignIn(assertion)
navigateToHome()
} catch (e: Exception) {
showError(e)
}
}
},
onCancel = {
navigateBack()
}
)
}
FirebaseUI automatically inherits your app's Material Theme:
@Composable
fun App() {
MyAppTheme { // Your existing Material3 theme
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email())
theme = AuthUITheme.fromMaterialTheme() // Inherits MyAppTheme
}
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { /* ... */ }
)
}
}
Create a completely custom theme:
val customTheme = AuthUITheme(
colorScheme = darkColorScheme(
primary = Color(0xFF6200EE),
onPrimary = Color.White,
primaryContainer = Color(0xFF3700B3),
secondary = Color(0xFF03DAC6)
),
typography = Typography(
displayLarge = TextStyle(fontSize = 57.sp, fontWeight = FontWeight.Bold),
bodyLarge = TextStyle(fontSize = 16.sp)
),
shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(16.dp)
)
)
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email())
theme = customTheme
}
Customize individual provider button styling:
val customProviderStyles = mapOf(
"google.com" to AuthUITheme.ProviderStyle(
backgroundColor = Color.White,
contentColor = Color(0xFF757575),
iconTint = null, // Use original colors
shape = RoundedCornerShape(8.dp),
elevation = 4.dp
),
"facebook.com" to AuthUITheme.ProviderStyle(
backgroundColor = Color(0xFF1877F2),
contentColor = Color.White,
shape = RoundedCornerShape(12.dp),
elevation = 0.dp
)
)
val customTheme = AuthUITheme.Default.copy(
providerStyles = customProviderStyles
)
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Google(), AuthProvider.Facebook())
theme = customTheme
}
Seamlessly upgrade anonymous users to permanent accounts:
// 1. Configure anonymous authentication with upgrade enabled
val configuration = authUIConfiguration {
providers = listOf(
AuthProvider.Anonymous(),
AuthProvider.Email(),
AuthProvider.Google()
)
isAnonymousUpgradeEnabled = true
}
// 2. When user wants to create a permanent account, show auth UI
// The library automatically upgrades the anonymous account if one exists
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { result ->
// Anonymous account has been upgraded (if user was anonymous)!
Toast.makeText(this, "Account created!", Toast.LENGTH_SHORT).show()
}
)
Enable passwordless email link authentication:
val emailProvider = AuthProvider.Email(
isEmailLinkSignInEnabled = true,
emailLinkActionCodeSettings = actionCodeSettings {
url = "https://example.com/auth"
handleCodeInApp = true
setAndroidPackageName(packageName, true, "12")
},
passwordValidationRules = emptyList()
)
val configuration = authUIConfiguration {
providers = listOf(emailProvider)
}
High-Level API - Direct FirebaseAuthScreen usage:
// In your Activity that handles the deep link:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authUI = FirebaseAuthUI.getInstance()
val emailLink = if (authUI.canHandleIntent(intent)) {
intent.data?.toString()
} else {
null
}
if (emailLink != null) {
setContent {
FirebaseAuthScreen(
configuration = configuration,
emailLink = emailLink,
onSignInSuccess = { result ->
// Email link sign-in successful
},
onSignInFailure = { exception ->
// Handle error
},
onSignInCancelled = {
finish()
}
)
}
}
}
Low-Level API - Using AuthFlowController:
import com.firebase.ui.auth.util.EmailLinkConstants
// In your Activity that handles the deep link:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authUI = FirebaseAuthUI.getInstance()
val emailLink = if (authUI.canHandleIntent(intent)) {
intent.data?.toString()
} else {
null
}
if (emailLink != null) {
val controller = authUI.createAuthFlow(configuration)
val intent = controller.createIntent(this).apply {
putExtra(EmailLinkConstants.EXTRA_EMAIL_LINK, emailLink)
}
authLauncher.launch(intent)
}
}
// Handle result
private val authLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> {
// Email link sign-in successful
}
Activity.RESULT_CANCELED -> {
// Handle error or cancellation
}
}
}
Add the intent filter to your AndroidManifest.xml:
<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="https"
android:host="example.com"
android:pathPrefix="/auth" />
</intent-filter>
Enforce custom password requirements:
val emailProvider = AuthProvider.Email(
emailLinkActionCodeSettings = null,
minimumPasswordLength = 10,
passwordValidationRules = listOf(
PasswordRule.MinimumLength(10),
PasswordRule.RequireUppercase,
PasswordRule.RequireLowercase,
PasswordRule.RequireDigit,
PasswordRule.RequireSpecialCharacter,
PasswordRule.Custom(
regex = Regex("^(?!.*password).*$"),
errorMessage = "Password cannot contain the word 'password'"
)
)
)
FirebaseUI automatically integrates with Android's Credential Manager API to save and retrieve credentials. This enables:
Credential Manager is enabled by default. To disable:
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email())
isCredentialManagerEnabled = false
}
Sign Out:
@Composable
fun SettingsScreen() {
val context = LocalContext.current
val authUI = remember { FirebaseAuthUI.getInstance() }
Button(
onClick = {
lifecycleScope.launch {
authUI.signOut(context)
// User is signed out, navigate to auth screen
navigateToAuth()
}
}
) {
Text("Sign Out")
}
}
Delete Account:
Button(
onClick = {
lifecycleScope.launch {
try {
authUI.delete(context)
// Account deleted successfully
navigateToAuth()
} catch (e: Exception) {
when (e) {
is FirebaseAuthRecentLoginRequiredException -> {
// User needs to reauthenticate
showReauthenticationDialog()
}
else -> {
showError("Failed to delete account: ${e.message}")
}
}
}
}
}
) {
Text("Delete Account")
}
FirebaseUI includes default English strings. To add custom localization:
class SpanishStringProvider(context: Context) : AuthUIStringProvider {
override fun signInWithEmail() = "Iniciar sesión con correo"
override fun signInWithGoogle() = "Iniciar sesión con Google"
override fun signInWithFacebook() = "Iniciar sesión con Facebook"
override fun invalidEmail() = "Correo inválido"
override fun weakPassword() = "Contraseña débil"
// ... implement all other required methods
}
val configuration = authUIConfiguration {
providers = listOf(AuthProvider.Email())
stringProvider = SpanishStringProvider(context)
locale = Locale("es", "ES")
}
Or override individual strings in your strings.xml:
<resources>
<!-- Override FirebaseUI strings -->
<string name="fui_sign_in_with_google">Sign in with Google</string>
<string name="fui_sign_in_with_email">Sign in with Email</string>
<string name="fui_invalid_email_address">Invalid email address</string>
<!-- See auth/src/main/res/values/strings.xml for all available strings -->
</resources>
FirebaseUI provides a comprehensive exception hierarchy:
FirebaseAuthScreen(
configuration = configuration,
onSignInFailure = { exception ->
when (exception) {
is AuthException.NetworkException -> {
showSnackbar("No internet connection. Please check your network.")
}
is AuthException.InvalidCredentialsException -> {
showSnackbar("Invalid email or password.")
}
is AuthException.UserNotFoundException -> {
showSnackbar("No account found with this email.")
}
is AuthException.WeakPasswordException -> {
showSnackbar("Password is too weak. Please use a stronger password.")
}
is AuthException.EmailAlreadyInUseException -> {
showSnackbar("An account already exists with this email.")
}
is AuthException.TooManyRequestsException -> {
showSnackbar("Too many attempts. Please try again later.")
}
is AuthException.MfaRequiredException -> {
// Handled automatically by FirebaseAuthScreen
// or show custom MFA challenge
}
is AuthException.AccountLinkingRequiredException -> {
// Account needs to be linked
showAccountLinkingDialog(exception)
}
is AuthException.AuthCancelledException -> {
// User cancelled the flow
navigateBack()
}
is AuthException.UnknownException -> {
showSnackbar("An unexpected error occurred: ${exception.message}")
Log.e(TAG, "Auth error", exception)
}
}
}
)
Use the ErrorRecoveryDialog for automatic error handling:
var errorState by remember { mutableStateOf<AuthException?>(null) }
errorState?.let { error ->
ErrorRecoveryDialog(
error = error,
onRetry = {
// Retry the authentication
errorState = null
retryAuthentication()
},
onDismiss = {
errorState = null
},
onRecover = { exception ->
// Custom recovery logic for specific errors
when (exception) {
is AuthException.AccountLinkingRequiredException -> {
linkAccounts(exception)
}
}
}
)
}
The new Compose library has a completely different architecture. Here's how to migrate:
Old (9.x - View/Activity based):
// Old approach with startActivityForResult
Intent signInIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.EmailBuilder().build(),
new AuthUI.IdpConfig.GoogleBuilder().build()
))
.setTheme(R.style.AppTheme)
.build();
signInLauncher.launch(signInIntent);
New (10.x - Compose based):
// New approach with Composable
val configuration = authUIConfiguration {
providers = listOf(
AuthProvider.Email(),
AuthProvider.Google()
)
theme = AuthUITheme.fromMaterialTheme()
}
FirebaseAuthScreen(
configuration = configuration,
onSignInSuccess = { result -> /* ... */ },
onSignInFailure = { exception -> /* ... */ },
onSignInCancelled = { /* ... */ }
)
Key Changes:
authUIConfiguration {} instead of createSignInIntentBuilder()AuthProvider.Email() instead of IdpConfig.EmailBuilder().build()ActivityResultLauncherAuthUITheme instead of R.style theme resourcesFlow<AuthState> instead of AuthStateListenerMigration Checklist:
firebase-ui-auth:10.0.0-beta01FirebaseAuthScreenAuthUIThemeAuthExceptionActivityResultLauncher and use direct callbacksFor a complete migration example, see the migration guide.
Contributions are welcome! Please read our contribution guidelines before submitting PRs.
FirebaseUI Auth is available under the Apache 2.0 license.