Add sign-in to your iOS app with FirebaseUI

FirebaseUI 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

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.

  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.

    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.

Email address and password

  1. From the Security > Authentication > Sign-in method section of the Firebase console, enable the Email/Password provider.

  2. Register the provider in your AuthService instance:

    let authService = AuthService()
      .withEmailSignIn()
    

To use passwordless email link sign-in:

  1. From the Security > Authentication > Sign-in method 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:

    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.

    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.

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 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. 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. 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. 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 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 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:

    let authService = AuthService()
      .withAppleSignIn()
    

Google

To use Google Sign-In:

  1. From the Security > Authentication > Sign-in method 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):

  4. Register the provider in your AuthService instance:

    let authService = AuthService()
      .withGoogleSignIn()
    

Facebook

To use Facebook Login:

  1. Set up the Facebook Login for iOS SDK by following the instructions on the Meta for Developers site. Skip the final step, "Add Facebook Login to Your Code".

  2. From the Security > Authentication > Sign-in method 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:

    let authService = AuthService()
     .withFacebookSignIn()
    

Phone number

To use phone authentication:

  1. From the Security > Authentication > Sign-in method section of the Firebase console, enable the Phone provider.

  2. Configure APNs for your app as instructed in the 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 in the Firebase console.

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

  4. In the same AppDelegate in which you call FirebaseApp.configure(), add APNs token handlers:

    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:

    let authService = AuthService()
      .withPhoneSignIn()
    

Twitter (X)

To use Twitter Login:

  1. Generate X API credentials by following the instructions on the X Developers site.

  2. From the Security > Authentication > Sign-in method 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:

    let authService = AuthService()
      .withTwitterSignIn()
    

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.

 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:

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)

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:

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:

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.

  • 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

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:

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:

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:

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:

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 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:

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:):

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:):

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:

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 for detailed setup instructions.

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)
  }
}