![](https://firebase.google.com/static/docs/auth/images/firebaseui-ios.png)

[FirebaseUI](https://github.com/firebase/firebaseui-ios) for SwiftUI is a
modern, SwiftUI-first library built on top of Firebase Authentication that provides
prebuilt sign-in flows to your app.

FirebaseUI for SwiftUI has the following benefits:

- **Opinionated default UI** : add a complete sign-in flow with `AuthPickerView`.
- **Customizable**: render the default buttons in your own layout, or build a fully custom experience.
- **Anonymous account linking**: optionally upgrade anonymous users instead of replacing them.
- **Account management**: built-in flows for sign-up, password recovery, and account management.
- **Multiple providers**: email/password, email link, phone authentication, Apple, Google, Facebook, Twitter, and standard OAuth2/OIDC providers.
- **Modern auth features**: built-in support for multi-factor authentication (MFA) and async/await APIs.

## Before you begin

- [Add Firebase to your Apple project](https://firebase.google.com/docs/ios/setup.md.txt). Be sure to complete the
  SwiftUI steps.

- Make sure your app targets iOS 17 or later.

## Installation

FirebaseUI for SwiftUI is provided as a Swift package. To install the package in
your Xcode project, do the following:

1. In Xcode, click **File \> Add Package Dependencies**.

2. Enter the package URL:

       https://github.com/firebase/FirebaseUI-iOS

3. In the **Dependency Rule** menu, select **Up to Next Major Version** and set
   the minimum version to
   [the latest release](https://github.com/firebase/FirebaseUI-iOS/releases).

4. Click **Add Package**, then select the libraries to add to your project.

   The following library is always required. It includes the core dependencies
   as well as support for email-password sign-in and email link sign-in.
   - `FirebaseAuthSwiftUI`

   If you want to support other sign-in methods, also select one or more of the
   following libraries:
   - `FirebaseAppleSwiftUI` (Sign in with Apple)
   - `FirebaseGoogleSwiftUI` (Sign in with Google)
   - `FirebaseFacebookSwiftUI` (Sign in with Facebook)
   - `FirebasePhoneAuthSwiftUI` (Phone authentication)
   - `FirebaseTwitterSwiftUI` (Sign in with Twitter)
   - `FirebaseOAuthSwiftUI` (Standard OAuth and OIDC providers like GitHub, Microsoft, Yahoo)
5. Click **Add Package** to install the selected libraries.

6. Configure FirebaseUI's `AuthService` in the initializer of your top-level
   `View` and pass the service to child views using the environment.

   ```swift
   import FirebaseAuthSwiftUI
   import SwiftUI

   struct ContentView: View {
     let authService: AuthService

     init() {
       let configuration = AuthConfiguration()

       authService = AuthService(configuration: configuration)
         .withEmailSignIn() // Or whatever sign-in methods you want to support.
                            // See the next section.
     }

     var body: some View {
       AuthPickerView { // AuthPickerView (the prebuilt View) or a custom View.
           Text("Welcome to your app!")
       }
       .environment(authService)
     }
   }
   ```

## Set up sign-in methods

Each of the supported sign-in methods require some additional setup steps.
Expand the sections below and follow the instructions to set up individual
providers.

<br />

<br />

### Email address and password

1. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable the **Email/Password** provider.

2. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
     .withEmailSignIn()
   ```

   <br />

<br />

<br />

<br />

### Email link authentication

To use passwordless email link sign-in:

1. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable **Email/Password**, then enable
   email link sign-in. Note that email or password sign-in must be enabled to
   use email link sign-in.

2. Add the link domain to **Authorized domains**.

3. Configure `ActionCodeSettings`, pass it into `AuthConfiguration` and
   register the provider:

   ```swift
   let actionCodeSettings = ActionCodeSettings()
   actionCodeSettings.handleCodeInApp = true
   actionCodeSettings.url = URL(string: "https://yourapp.firebaseapp.com")

   guard let bundleID = Bundle.main.bundleIdentifier else {
     fatalError("Missing bundle identifier for email link authentication setup.")
   }
   actionCodeSettings.setIOSBundleID(bundleID)

   let configuration = AuthConfiguration(
     emailLinkSignInActionCodeSettings: actionCodeSettings
   )

   let authService = AuthService(configuration: configuration)
     .withEmailLinkSignIn()
   ```
4. In the same `AppDelegate` in which you call `FirebaseApp.configure()`, add
   logic to the `application(_:open:options:)` method that returns `true`
   when the URL opened is a Firebase Authentication sign-in link. This logic
   indicates that the link was handled by FirebaseUI and shouldn't be processed
   by other link handlers.

   ```swift
   import FacebookCore
   import FirebaseAuth
   import UIKit

   class AppDelegate: NSObject, UIApplicationDelegate {
     func application(
       _ application: UIApplication,
       didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
     ) -> Bool {
       FirebaseApp.configure()
       return true
     }

     func application(
   _ application: UIApplication,
   open url: URL,
   options: \[UIApplication.OpenURLOptionsKey: Any\] = \[:\]
   ) -\> Bool {
   if Auth.auth().canHandle(url) {
   return true
   }
   return false
   }
   }
   ```
5. If you build custom views, call `authService.handleSignInLink(url:)` when the link opens your app.

<br />

<br />

<br />

### Apple

To use Sign in with Apple:

1. Configure Sign In with Apple:

   1. Enable Sign In with Apple for your app on the [Certificates, Identifiers \& Profiles](https://developer.apple.com/account/resources) page of Apple's developer site.
   2. Associate your website with your app as described in the first section of [Configure Sign In with Apple for the web](https://developer.apple.com/help/account/configure-app-capabilities/configure-sign-in-with-apple-for-the-web). When prompted, register the following URL as a Return URL:

      ```
      https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
      ```
      You can get your Firebase project ID on the [Firebase console settings page](https://console.firebase.google.com/project/_/settings/general/). When you're done, take note of your new Service ID, which you'll need in the next section.
   3. [Create a Sign In with Apple private key](https://developer.apple.com/help/account/configure-app-capabilities/create-a-sign-in-with-apple-private-key/). You'll need your new private key and key ID in the next section.
   4. If you use any of Firebase Authentication's features that send emails to users, including email link sign-in, email address verification, account change revocation, and others, [configure the Apple private email relay service](https://developer.apple.com/help/account/configure-app-capabilities/configure-private-email-relay-service/) and register `noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com` (or your customized email template domain) so Apple can relay emails sent by Firebase Authentication to anonymized Apple email addresses.
2. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable **Apple**.

   - Specify the Service ID you created in the previous section.
   - In the **OAuth code flow configuration section**, specify your Apple Team ID and the private key and key ID you created in the previous section.
3. In Xcode, open the **Signing \& Capabilities** section of the project editor
   and add the **Sign in with Apple** capability.

4. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
     .withAppleSignIn()
   ```

   <br />

<br />

<br />

<br />

### Google

To use Google Sign-In:

1. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable the **Google** provider.

2. Download a new copy of your project's `GoogleService-Info.plist` file and
   copy it to your Xcode project. Overwrite any existing versions with the new
   one.

3. Add custom URL schemes to your Xcode project:

   1. Open your project configuration: click the project name in the left tree
      view. Select your app from the **TARGETS** section, then
      select the **Info** tab, and expand the **URL Types** section.

   2. Click the **+** button, and add a URL scheme for your reversed
      client ID. To find this value, open the
      `` `GoogleService-Info.plist` `` configuration file, and look for
      the `REVERSED_CLIENT_ID` key. Copy the value of that key,
      and paste it into the **URL Schemes** box on the configuration page.
      Leave the other fields untouched.

      When completed, your config should look something similar to the
      following (but with your application-specific values):

      ![](https://firebase.google.com/static/docs/auth/images/xcode_infotab_url_type_values.png)
4. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
     .withGoogleSignIn()
   ```

   <br />

<br />

<br />

<br />

### Facebook

To use Facebook Login:

1. Set up the Facebook Login for iOS SDK by following the
   [instructions](https://developers.facebook.com/documentation/facebook-login/ios)
   on the Meta for Developers site.
   Skip the final step, "Add Facebook Login to Your Code".

2. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable the **Facebook** provider.
   You will need the Facebook App ID and App Secret from the Meta for
   Developers site.

3. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
    .withFacebookSignIn()
   ```

   <br />

<br />

<br />

<br />

### Phone number

To use phone authentication:

1. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable the **Phone** provider.

2. Configure APNs for your app as instructed in the
   [Start receiving silent notifications](https://firebase.google.com/docs/auth/ios/phone-auth.md.txt#start-receiving-silent-notifications)
   section.

3. Add custom URL schemes to your Xcode project:

   1. Open your project configuration: click the project name in the left tree
      view. Select your app from the **TARGETS** section, then
      select the **Info** tab, and expand the **URL Types** section.

   2. Click the **+** button, and add your Encoded App ID as a URL scheme.
      To find this value, open
      **Settings \>** [**General**](https://console.firebase.google.com/project/_/settings/general?useAutoProject=true)
      in the Firebase console.

      When completed, your config should look something similar to the
      following (but with your application-specific values):

      ![](https://firebase.google.com/static/docs/auth/images/xcode_infotab_url_type_values.png)
4. In the same `AppDelegate` in which you call `FirebaseApp.configure()`, add
   APNs token handlers:

   ```swift
   import FacebookCore
   import FirebaseAuth
   import UIKit

   class AppDelegate: NSObject, UIApplicationDelegate {
     func application(
       _ application: UIApplication,
       didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
     ) -> Bool {
       FirebaseApp.configure()
       return true
     }

     func application(
   _ application: UIApplication,
   didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
   ) {
   #if DEBUG
   Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
   #else
   Auth.auth().setAPNSToken(deviceToken, type: .prod)
   #endif
   }
   }
   ```
5. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
     .withPhoneSignIn()
   ```

   <br />

<br />

<br />

<br />

### Twitter (X)

To use Twitter Login:

1. Generate X API credentials by following the
   [instructions](https://docs.x.com/fundamentals/developer-portal#getting-started)
   on the X Developers site.

2. From the **Security \> Authentication \>** [**Sign-in method**](https://console.firebase.google.com/project/_/authentication/providers?useAutoProject=true)
   section of the Firebase console, enable the **Twitter** provider.
   You will need the API key and API secret from the X Developer console.

3. Register the provider in your `AuthService` instance:

   ```swift
   let authService = AuthService()
     .withTwitterSignIn()
   ```

   <br />

<br />

<br />

<br />

### Standard OAuth2 and OIDC providers

FirebaseUI also supports built-in OAuth providers such as GitHub, Microsoft,
and Yahoo, as well as custom OIDC providers configured in Firebase
Authentication.

```swift
 let authService = AuthService()
  .withOAuthSignIn(OAuthProviderSwift.github())
  .withOAuthSignIn(OAuthProviderSwift.microsoft())
  .withOAuthSignIn(OAuthProviderSwift.yahoo())
```

For custom OIDC providers, configure the provider first in Firebase
Authentication, then create an `OAuthProviderSwift` with your provider ID and
button configuration:

```swift
let lineProvider = OAuthProviderSwift(
  providerId: "oidc.line",
  buttonLabel: "Sign in with LINE",
  displayName: "LINE",
  iconSystemName: "person.crop.circle.badge.checkmark",
  buttonBackgroundColor: .green,
  buttonForegroundColor: .white
)

let authService = AuthService()
  .withOAuthSignIn(lineProvider)
```

<br />

<br />

## Using the prebuilt authentication view

FirebaseUI for SwiftUI provides `AuthPickerView`, a prebuilt, opinionated
authentication UI that handles the entire authentication flow for you. This is
the easiest way to add authentication to your app.

### Example

Here's an example of `AuthPickerView` in use, with multiple providers and
configuration options:

```swift
import FirebaseAppleSwiftUI
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    // Create configuration with options
    let configuration = AuthConfiguration(
      tosUrl: URL(string: "https://example.com/tos"),
      privacyPolicyUrl: URL(string: "https://example.com/privacy"),
      shouldAutoUpgradeAnonymousUsers: true
    )

    // Initialize AuthService with multiple providers
    authService = AuthService(configuration: configuration)
      .withEmailSignIn()
      .withAppleSignIn()
      .withGoogleSignIn()
  }

  var body: some View {
    AuthPickerView {
      authenticatedContent
    }
    .environment(authService)
  }

  var authenticatedContent: some View {
    NavigationStack {
      VStack(spacing: 20) {
        if authService.authenticationState == .authenticated {
          Text("Authenticated")
          
          Button("Manage Account") {
            authService.isPresented = true
          }
          .buttonStyle(.bordered)
          
          Button("Sign Out") {
            Task {
              try? await authService.signOut()
            }
          }
          .buttonStyle(.borderedProminent)
        } else {
          Text("Not Authenticated")
          
          Button("Sign In") {
            authService.isPresented = true
          }
          .buttonStyle(.borderedProminent)
        }
      }
      .navigationTitle("My App")
    }
    .onChange(of: authService.authenticationState) { _, newValue in
      // Automatically show auth UI when not authenticated
      if newValue != .authenticating {
        authService.isPresented = (newValue == .unauthenticated)
      }
    }
  }
}
```

### Typical customization

Although all of the `AuthConfiguration` parameters are optional, most apps
customize the following settings at a minimum:

```swift
let configuration = AuthConfiguration(
    logo: ImageResource.exampleLogoAsset,
    customStringsBundle: .main,
    tosUrl: URL(string: "https://example.com/tos"),
    privacyPolicyUrl: URL(string: "https://example.com/privacy"),
)
```

- `logo`: the logo image displayed on the authentication sheet.
  For information on including your own image asset, see
  [Adding images to your Xcode project](https://developer.apple.com/documentation/xcode/adding-images-to-your-xcode-project).

- `customStringsBundle`: use custom strings from the specified Bundle. This is
  used for localization, but also for customizing the default strings used by
  `AuthPickerView`. For example, to set the message displayed at the top of the
  authentication sheet, create `Localizable.strings` with the contents:

      "Sign in with Firebase" = "Sign in to use ExampleApp";

### What's included in the prebuilt view

When you use `AuthPickerView`, you get:

1. **Sheet Presentation**: Authentication UI appears as a modal sheet
2. **Built-in Navigation**: Automatic navigation between sign-in, password recovery, MFA, email link, and phone verification screens
3. **Authentication State Management** : Automatically switches between auth UI and your content based on `authService.authenticationState`
4. **Control via `isPresented`** : Control when the auth sheet appears by setting `authService.isPresented = true/false`

#### Opinionated behaviors

The default `AuthPickerView` is opinionated about how to handle several complex
scenarios:

##### 1. **Account conflict resolution**

When an account conflict occurs (e.g., signing in with a credential that's already linked to another account), `AuthPickerView` automatically handles it:

- **Anonymous Upgrade Conflicts** : If `shouldAutoUpgradeAnonymousUsers` is enabled and a conflict occurs during anonymous upgrade, the system automatically signs out the anonymous user and signs in with the new credential.
- **Other Conflicts**: For credential conflicts between non-anonymous accounts, the system stores the pending credential and attempts to link it after successful sign-in.

This is handled by the `AccountConflictModifier` applied at the NavigationStack level.

##### 2. **Multi-factor authentication (MFA)**

When MFA is enabled in your configuration:

- Automatically detects when MFA is required during sign-in
- Presents appropriate MFA resolution screens (SMS or TOTP)
- Handles MFA enrollment and management flows
- Supports both SMS-based and Time-based One-Time Password (TOTP) factors

##### 3. **Error handling**

The default views include built-in error handling:

- Displays user-friendly error messages in alert dialogs
- Automatically filters out errors that are handled internally (e.g., cancellation errors, auto-handled conflicts)
- Uses localized error messages via `StringUtils`
- Errors are propagated through the `reportError` environment key

##### 4. **Email link sign-in**

When email link sign-in is configured:

- Automatically stores the email address in app storage
- Handles deep link navigation from email
- Manages the complete email verification flow
- Supports anonymous user upgrades via email link

##### 5. **Anonymous user auto-upgrade**

When `shouldAutoUpgradeAnonymousUsers` is enabled:

- Automatically attempts to link anonymous accounts with new sign-in credentials
- Preserves user data by upgrading instead of replacing anonymous sessions
- Handles upgrade conflicts gracefully

##### 6. Reauthentication in default views

Sensitive operations like deleting accounts, updating passwords, or unenrolling MFA factors require recent authentication. When using default views, reauthentication is handled automatically based on the user's sign-in provider.

When a sensitive operation requires reauthentication, the default views automatically:

- **OAuth Providers (Google, Apple, Facebook, Twitter, etc.)**: Display an alert asking the user to confirm, then automatically obtain fresh credentials and complete the operation.

- **Email/Password**: Present a sheet prompting the user to enter their password before continuing.

- **Email Link**: Show an alert asking to send a verification email, then present a sheet with instructions to check email. The user taps the link in their email to complete reauthentication.

- **Phone**: Show an alert explaining verification is needed, then present a sheet for SMS code verification.

The operation automatically retries after successful reauthentication. No additional code is required when using `AuthPickerView` or the built-in account management views (`UpdatePasswordView`, `SignedInView`, etc.).

## Advanced: Building custom auth views

If you need more control over the UI or navigation flow, you can build your own
custom authentication views while still leveraging the `AuthService` for
authentication logic.

You can mix elements of FirebaseUI with custom logic in many ways. The following
sections contain examples of some of the customization approaches you might use.

### Approach 1: Custom buttons with `registerProvider()`

For complete control over button appearance, you can create your own custom `AuthProviderUI` implementation that wraps any provider and returns your custom button view.

#### Creating a custom provider UI

Here's how to create a custom Twitter button as an example:

```swift
import FirebaseAuthSwiftUI
import FirebaseTwitterSwiftUI
import SwiftUI

// Step 1: Create your custom button view
struct CustomTwitterButton: View {
  let provider: TwitterProviderSwift
  @Environment(AuthService.self) private var authService
  @Environment(\.mfaHandler) private var mfaHandler

  var body: some View {
    Button {
      Task {
        do {
          let outcome = try await authService.signIn(provider)

          // Handle MFA if required
          if case let .mfaRequired(mfaInfo) = outcome,
             let onMFA = mfaHandler {
            onMFA(mfaInfo)
          }
        } catch {
          // Do Something Else
        }
      }
    } label: {
      HStack { // Your custom icon
        Text("Sign in with Twitter")
          .fontWeight(.semibold)
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(
        LinearGradient(
          colors: [Color.blue, Color.cyan],
          startPoint: .leading,
          endPoint: .trailing
        )
      )
      .foregroundColor(.white)
      .cornerRadius(12)
      .shadow(radius: 4)
    }
  }
}

// Step 2: Create a custom AuthProviderUI wrapper
class CustomTwitterProviderAuthUI: AuthProviderUI {
  private let typedProvider: TwitterProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "twitter.com"

  init(provider: TwitterProviderSwift = TwitterProviderSwift()) {
    typedProvider = provider
  }

  @MainActor func authButton() -> AnyView {
    AnyView(CustomTwitterButton(provider: typedProvider))
  }
}

// Step 3: Use it in your app
struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)

    // Register your custom provider UI
    authService.registerProvider(
      providerWithButton: CustomTwitterProviderAuthUI()
    )
    authService.isPresented = true
  }

  var body: some View {
    AuthPickerView {
      usersApp
    }
    .environment(authService)
  }

  var usersApp: some View {
    NavigationStack {
      VStack {
        Button {
          authService.isPresented = true
        } label: {
          Text("Authenticate")
        }
      }
    }
  }
}
```

#### Simplified custom button example

You can also create simpler custom buttons for any provider:

```swift
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

// Custom Google Provider UI
class CustomGoogleProviderAuthUI: AuthProviderUI {
  private let typedProvider: GoogleProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "google.com"
  
  init() {
    typedProvider = GoogleProviderSwift()
  }
  
  @MainActor func authButton() -> AnyView {
    AnyView(CustomGoogleButton(provider: typedProvider))
  }
}

struct CustomGoogleButton: View {
  let provider: GoogleProviderSwift
  @Environment(AuthService.self) private var authService
  
  var body: some View {
    Button {
      Task {
        try? await authService.signIn(provider)
      }
    } label: {
      HStack {
        Image(systemName: "g.circle.fill")
        Text("My Custom Google Button")
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(Color.purple) // Your custom color
      .foregroundColor(.white)
      .cornerRadius(10)
    }
  }
}

// Then use it
struct ContentView: View {
  let authService: AuthService
  
  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)
      .withAppleSignIn() // Use default Apple button
    
    // Use custom Google button
    authService.registerProvider(
      providerWithButton: CustomGoogleProviderAuthUI()
    )
  }
  
  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}
```

This approach works for all providers: Google, Apple, Twitter, Facebook, Phone, and OAuth providers. Simply create your custom button view and wrap it in a class conforming to `AuthProviderUI`.

### Approach 2: Default buttons with custom views

You can use `AuthService.renderButtons()` and bypass `AuthPickerView` to render the default authentication buttons while providing your own layout and navigation:

```swift
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

struct CustomAuthView: View {
  @Environment(AuthService.self) private var authService

  var body: some View {
    VStack(spacing: 30) {
      // Your custom logo/branding
      Image("app-logo")
        .resizable()
        .frame(width: 150, height: 150)
      
      Text("Welcome to My App")
        .font(.largeTitle)
        .fontWeight(.bold)
      
      Text("Sign in to continue")
        .font(.subheadline)
        .foregroundStyle(.secondary)
      
      // Render default auth buttons
      authService.renderButtons(spacing: 12)
        .padding()
    }
    .padding()
  }
}

struct ContentView: View {
  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withAppleSignIn()
  }
  
  let authService: AuthService

  var body: some View {
    NavigationStack {
      if authService.authenticationState == .authenticated {
        Text("Authenticated!")
      } else {
        CustomAuthView()
      }
    }
    .environment(authService)
  }
}
```

### Approach 3: Custom views with custom navigation

For complete control over the entire flow, you can bypass `AuthPickerView` and build your own navigation system:

```swift
import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

enum CustomAuthRoute {
  case signIn
  case phoneVerification
  case mfaResolution
}

struct ContentView: View {
  private let authService: AuthService
  @State private var navigationPath: [CustomAuthRoute] = []
  @State private var errorMessage: String?

  init() {
    let configuration = AuthConfiguration()
    self.authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withPhoneSignIn()
  }

  var body: some View {
    NavigationStack(path: $navigationPath) {
      Group {
        if authService.authenticationState == .authenticated {
          authenticatedView
        } else {
          customSignInView
        }
      }
      .navigationDestination(for: CustomAuthRoute.self) { route in
        switch route {
        case .signIn:
          customSignInView
        case .phoneVerification:
          customPhoneVerificationView
        case .mfaResolution:
          customMFAView
        }
      }
    }
    .environment(authService)
    .alert("Error", isPresented: .constant(errorMessage != nil)) {
      Button("OK") {
        errorMessage = nil
      }
    } message: {
      Text(errorMessage ?? "")
    }
  }

  var customSignInView: some View {
    VStack(spacing: 20) {
      Text("Custom Sign In")
        .font(.title)
      
      Button("Sign in with Google") {
        Task {
          do {
            let provider = GoogleProviderSwift(clientID: Auth.auth().app?.options.clientID ?? "")
            let outcome = try await authService.signIn(provider)
            
            // Handle MFA if required
            if case .mfaRequired = outcome {
              navigationPath.append(.mfaResolution)
            }
          } catch {
            errorMessage = error.localizedDescription
          }
        }
      }
      .buttonStyle(.borderedProminent)
      
      Button("Phone Sign In") {
        navigationPath.append(.phoneVerification)
      }
      .buttonStyle(.bordered)
    }
    .padding()
  }

  var customPhoneVerificationView: some View {
    Text("Custom Phone Verification View")
    // Implement your custom phone auth UI here
  }

  var customMFAView: some View {
    Text("Custom MFA Resolution View")
    // Implement your custom MFA UI here
  }

  var authenticatedView: some View {
    VStack(spacing: 20) {
      Text("Welcome!")
      Text("Email: \(authService.currentUser?.email ?? "N/A")")
      
      Button("Sign Out") {
        Task {
          try? await authService.signOut()
        }
      }
      .buttonStyle(.borderedProminent)
    }
  }
}
```

### Important considerations for custom views

When building custom views, you need to handle several things yourself that `AuthPickerView` handles automatically:

1. **Account Conflicts** : Implement your own conflict resolution strategy using `AuthServiceError.accountConflict`
2. **MFA Handling** : Check `SignInOutcome` for `.mfaRequired` and handle MFA resolution manually
3. **Anonymous User Upgrades** : Handle the linking of anonymous accounts if `shouldAutoUpgradeAnonymousUsers` is enabled
4. **Navigation State**: Manage navigation between different auth screens (phone verification, password recovery, etc.)
5. **Loading States** : Show loading indicators during async authentication operations by observing `authService.authenticationState`
6. **Reauthentication** : Handle reauthentication errors for sensitive operations (see [Reauthentication in Custom Views](https://firebase.google.com/docs/auth/ios/firebaseui.md.txt#reauth) below)

### Reauthentication in custom views

When building custom views, handle reauthentication by catching specific errors and implementing your own flow. Sensitive operations throw four types of reauthentication errors, each containing context information.

#### Implementation patterns

**OAuth Providers (Google, Apple, Facebook, Twitter, etc.):**

Catch the error and call `reauthenticate(context:)` which automatically handles the OAuth flow:

```swift
do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .oauthReauthenticationRequired(let context) = error {
    try await authService.reauthenticate(context: context)
    try await authService.deleteUser() // Retry operation
  }
}
```

**Email/Password:**

Catch the error, prompt for password, create credential, and call `reauthenticate(with:)`:

```swift
do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailReauthenticationRequired(let context) = error {
    // Show your password prompt UI
    let password = await promptUserForPassword()
    let credential = EmailAuthProvider.credential(
      withEmail: context.email,
      password: password
    )
    try await authService.reauthenticate(with: credential)
    try await authService.updatePassword(to: newPassword) // Retry
  }
}
```

**Phone:**

Catch the error, verify phone, create credential, and call `reauthenticate(with:)`:

```swift
do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .phoneReauthenticationRequired(let context) = error {
    // Send verification code
    let verificationId = try await authService.verifyPhoneNumber(
      phoneNumber: context.phoneNumber
    )
    // Show your SMS code input UI
    let code = await promptUserForSMSCode()
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: code
    )
    try await authService.reauthenticate(with: credential)
    try await authService.deleteUser() // Retry
  }
}
```

**Email Link:**

Catch the error, send verification email, and handle the incoming URL:

```swift
do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailLinkReauthenticationRequired(let context) = error {
    // Send verification email
    try await authService.sendEmailSignInLink(
      email: context.email,
      isReauth: true
    )
    // Show your "Check your email" UI
    await showCheckEmailUI()
    // When user taps the link, it opens your app with a URL
    // Handle it in your URL handler:
    // try await authService.handleSignInLink(url: url)
    // The handleSignInLink method automatically completes reauthentication
    try await authService.updatePassword(to: newPassword) // Retry
  }
}
```

All reauthentication context objects include a `.displayMessage` property for user-facing text.

### Custom OAuth providers

You can create custom OAuth providers for services beyond the built-in ones:
> **⚠️ Important:** OIDC (OpenID Connect) providers must be configured in your Firebase project's Authentication settings before they can be used. In the Firebase Console, go to **Authentication → Sign-in method** and add your OIDC provider with the required credentials (Client ID, Client Secret, Issuer URL). You must also register the OAuth redirect URI provided by Firebase in your provider's developer console. See the [Firebase OIDC documentation](https://firebase.google.com/docs/auth/ios/openid-connect.md.txt) for detailed setup instructions.

```swift
import FirebaseAuthSwiftUI
import FirebaseOAuthSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.line",  // LINE OIDC provider
          scopes: ["profile", "openid", "email"],  // LINE requires these scopes
          displayName: "Sign in with LINE",
          buttonIcon: Image("line-logo"),
          buttonBackgroundColor: .green,
          buttonForegroundColor: .white
        )
      )
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.custom-provider",
          scopes: ["profile", "openid"],
          displayName: "Sign in with Custom",
          buttonIcon: Image(systemName: "person.circle"),
          buttonBackgroundColor: .purple,
          buttonForegroundColor: .white
        )
      )
  }

  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}
```