Firebase Auth

AuthenticationGoogleMobileWebScalableOAuthMulti-factorReal-time

Library

Firebase Auth

Overview

Firebase Auth is a comprehensive authentication service provided by Google, optimized for mobile and web applications. Offers a generous free tier with up to 50,000 MAU (Monthly Active Users), excellent for real-time applications and prototype development. Renowned in 2025 for exceptional developer experience simplicity, achieving enterprise-level scalability through deep integration with Google Cloud Platform (GCP) ecosystem. However, mid-scale to enterprise companies are increasingly considering alternatives due to vendor lock-in concerns.

Details

Firebase Auth 2025 provides SDKs across wide platforms including iOS, Android, Web, Flutter, Unity, establishing itself as an industry-standard authentication platform. Supports email/password, phone number authentication, anonymous authentication, plus 20+ social login providers including Google, Facebook, Apple, GitHub, Microsoft, Twitter, Yahoo as standard. Also supports SAML (Web) and OpenID Connect custom providers, comprehensively providing multi-factor authentication (SMS-based 2FA), real-time session management, and user profile management in an integrated manner.

Key Features

  • Comprehensive Platform Support: Complete SDKs for iOS, Android, Flutter, Web, C++, Unity
  • Rich Social Login Options: 20+ OAuth providers and custom provider creation
  • Scalable Free Tier: Completely free up to 50,000 MAU, SMS authentication pay-per-use
  • Real-time Synchronization: Fast authentication state sync via Google Cloud infrastructure
  • Multi-Factor Authentication: Comprehensive security with SMS-based 2FA and TOTP support
  • GCP Ecosystem Integration: Seamless integration with Firebase Database, Cloud Functions, Analytics

Pros and Cons

Pros

  • Overwhelming development speed enables authentication system implementation in minutes for mobile apps
  • Free tier (50,000 MAU) allows building production-ready authentication systems with zero initial cost
  • Google Cloud Platform's powerful infrastructure supports global scale applications
  • Real-time database integration provides instant user information synchronization
  • Rich SDKs and UI libraries provide platform-optimized experiences
  • Firebase Console offers visual user management and analytics features

Cons

  • Completely cloud-based making self-hosting impossible, vendor lock-in risk
  • Unexpected costs as user numbers grow (especially SMS authentication: $0.01-$0.34/message)
  • Limited custom authentication flow constraints, difficult to deviate from standard patterns
  • Cannot be used in enterprises with strict data protection requirements or on-premise mandates
  • Limited enterprise features (detailed RBAC, audit logs, custom SLA)
  • Complex direct integration with other databases like PostgreSQL or MySQL

Reference Pages

Code Examples

Basic Setup

# Install Firebase SDK
npm install firebase

# Additional packages (for React)
npm install react-firebase-hooks

# Environment variable setup (optional)
npm install dotenv

Firebase Initial Configuration and Auth Initialization

// lib/firebase.ts
import { initializeApp, getApps } from 'firebase/app'
import { getAuth, connectAuthEmulator } from 'firebase/auth'

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
}

// Initialize Firebase App (prevent duplicate initialization)
const app = getApps().length > 0 ? getApps()[0] : initializeApp(firebaseConfig)

// Initialize Firebase Auth
export const auth = getAuth(app)

// Emulator setup for development environment (optional)
if (process.env.NODE_ENV === 'development' && !auth.config.emulator) {
  connectAuthEmulator(auth, 'http://localhost:9099')
}

export default app

Email/Password Authentication and User Registration

// hooks/useAuth.ts
import { useState } from 'react'
import {
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
  sendEmailVerification,
  User
} from 'firebase/auth'
import { auth } from '@/lib/firebase'

export function useAuth() {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const signUp = async (email: string, password: string) => {
    setLoading(true)
    setError(null)
    
    try {
      const userCredential = await createUserWithEmailAndPassword(auth, email, password)
      const user = userCredential.user
      
      // Send email verification
      await sendEmailVerification(user)
      
      return {
        user,
        message: 'Confirmation email sent. Please check your email.'
      }
    } catch (error: any) {
      setError(error.message)
      throw error
    } finally {
      setLoading(false)
    }
  }

  const signIn = async (email: string, password: string) => {
    setLoading(true)
    setError(null)
    
    try {
      const userCredential = await signInWithEmailAndPassword(auth, email, password)
      
      if (!userCredential.user.emailVerified) {
        throw new Error('Email address not verified. Please check your confirmation email.')
      }
      
      return userCredential.user
    } catch (error: any) {
      setError(error.message)
      throw error
    } finally {
      setLoading(false)
    }
  }

  const logout = async () => {
    try {
      await signOut(auth)
    } catch (error: any) {
      setError(error.message)
      throw error
    }
  }

  return {
    signUp,
    signIn,
    logout,
    loading,
    error
  }
}

Social Login Implementation (Google, GitHub)

// components/SocialAuth.tsx
'use client'
import { useState } from 'react'
import {
  signInWithPopup,
  GoogleAuthProvider,
  GithubAuthProvider,
  UserCredential
} from 'firebase/auth'
import { auth } from '@/lib/firebase'

export function SocialAuth() {
  const [loading, setLoading] = useState<string | null>(null)

  const handleGoogleSignIn = async () => {
    setLoading('google')
    
    try {
      const provider = new GoogleAuthProvider()
      provider.addScope('profile')
      provider.addScope('email')
      
      const result = await signInWithPopup(auth, provider)
      const credential = GoogleAuthProvider.credentialFromResult(result)
      const token = credential?.accessToken
      
      console.log('Google Access Token:', token)
      console.log('User:', result.user)
      
      return result.user
    } catch (error: any) {
      console.error('Google sign-in error:', error)
      throw error
    } finally {
      setLoading(null)
    }
  }

  const handleGitHubSignIn = async () => {
    setLoading('github')
    
    try {
      const provider = new GithubAuthProvider()
      provider.addScope('repo')
      
      const result = await signInWithPopup(auth, provider)
      const credential = GithubAuthProvider.credentialFromResult(result)
      const token = credential?.accessToken
      
      console.log('GitHub Access Token:', token)
      console.log('User:', result.user)
      
      return result.user
    } catch (error: any) {
      console.error('GitHub sign-in error:', error)
      throw error
    } finally {
      setLoading(null)
    }
  }

  return (
    <div className="space-y-3">
      <button
        onClick={handleGoogleSignIn}
        disabled={loading !== null}
        className="w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50"
      >
        {loading === 'google' ? (
          'Signing in with Google...'
        ) : (
          <>
            <svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
              <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
              <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
              <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
              <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
            </svg>
            Sign in with Google
          </>
        )}
      </button>

      <button
        onClick={handleGitHubSignIn}
        disabled={loading !== null}
        className="w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50"
      >
        {loading === 'github' ? (
          'Signing in with GitHub...'
        ) : (
          <>
            <svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
              <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
            </svg>
            Sign in with GitHub
          </>
        )}
      </button>
    </div>
  )
}

Phone Number Authentication and reCAPTCHA Setup

// components/PhoneAuth.tsx
'use client'
import { useState, useEffect } from 'react'
import {
  signInWithPhoneNumber,
  RecaptchaVerifier,
  ConfirmationResult,
  PhoneAuthProvider,
  signInWithCredential
} from 'firebase/auth'
import { auth } from '@/lib/firebase'

export function PhoneAuth() {
  const [phoneNumber, setPhoneNumber] = useState('')
  const [verificationCode, setVerificationCode] = useState('')
  const [confirmationResult, setConfirmationResult] = useState<ConfirmationResult | null>(null)
  const [loading, setLoading] = useState(false)
  const [recaptchaVerifier, setRecaptchaVerifier] = useState<RecaptchaVerifier | null>(null)

  useEffect(() => {
    // Initialize reCAPTCHA Verifier
    const verifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
      size: 'normal',
      callback: () => {
        console.log('reCAPTCHA authentication completed')
      },
      'expired-callback': () => {
        console.log('reCAPTCHA expired')
      }
    })
    
    setRecaptchaVerifier(verifier)

    return () => {
      verifier.clear()
    }
  }, [])

  const sendVerificationCode = async () => {
    if (!recaptchaVerifier) return
    
    setLoading(true)
    
    try {
      const confirmation = await signInWithPhoneNumber(auth, phoneNumber, recaptchaVerifier)
      setConfirmationResult(confirmation)
      alert('Verification code sent. Please check your SMS.')
    } catch (error: any) {
      console.error('Phone authentication error:', error)
      alert('Failed to send verification code: ' + error.message)
      recaptchaVerifier.clear()
    } finally {
      setLoading(false)
    }
  }

  const confirmVerificationCode = async () => {
    if (!confirmationResult) return
    
    setLoading(true)
    
    try {
      const userCredential = await confirmationResult.confirm(verificationCode)
      console.log('Phone authentication successful:', userCredential.user)
      alert('Authentication successful!')
    } catch (error: any) {
      console.error('Verification code confirmation error:', error)
      alert('Verification code is incorrect: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
      <h2 className="text-2xl font-bold mb-6 text-center">Phone Authentication</h2>
      
      {!confirmationResult ? (
        <div className="space-y-4">
          <div>
            <label className="block text-sm font-medium mb-1">
              Phone Number
            </label>
            <input
              type="tel"
              value={phoneNumber}
              onChange={(e) => setPhoneNumber(e.target.value)}
              placeholder="+1 555-123-4567"
              className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
            />
            <p className="text-sm text-gray-500 mt-1">
              Enter in international format (e.g., +15551234567)
            </p>
          </div>

          <div id="recaptcha-container" className="flex justify-center"></div>

          <button
            onClick={sendVerificationCode}
            disabled={loading || !phoneNumber}
            className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            {loading ? 'Sending...' : 'Send Verification Code'}
          </button>
        </div>
      ) : (
        <div className="space-y-4">
          <div>
            <label className="block text-sm font-medium mb-1">
              Verification Code (6 digits)
            </label>
            <input
              type="text"
              value={verificationCode}
              onChange={(e) => setVerificationCode(e.target.value)}
              placeholder="123456"
              maxLength={6}
              className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
            />
          </div>

          <button
            onClick={confirmVerificationCode}
            disabled={loading || verificationCode.length !== 6}
            className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 disabled:opacity-50"
          >
            {loading ? 'Verifying...' : 'Complete Authentication'}
          </button>

          <button
            onClick={() => {
              setConfirmationResult(null)
              setVerificationCode('')
            }}
            className="w-full text-gray-600 py-2 px-4 border border-gray-300 rounded-md hover:bg-gray-50"
          >
            Change Phone Number
          </button>
        </div>
      )}
    </div>
  )
}

Authentication State Management and Protected Routes

// components/AuthProvider.tsx
'use client'
import { createContext, useContext, useEffect, useState } from 'react'
import { User, onAuthStateChanged } from 'firebase/auth'
import { auth } from '@/lib/firebase'

interface AuthContextType {
  user: User | null
  loading: boolean
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  loading: true
})

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user)
      setLoading(false)
    })

    return () => unsubscribe()
  }, [])

  return (
    <AuthContext.Provider value={{ user, loading }}>
      {children}
    </AuthContext.Provider>
  )
}

export const useAuthContext = () => useContext(AuthContext)

// components/ProtectedRoute.tsx
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
import { useAuthContext } from './AuthProvider'

interface ProtectedRouteProps {
  children: React.ReactNode
  redirectTo?: string
}

export function ProtectedRoute({ 
  children, 
  redirectTo = '/login' 
}: ProtectedRouteProps) {
  const { user, loading } = useAuthContext()
  const router = useRouter()

  useEffect(() => {
    if (!loading && !user) {
      router.push(redirectTo)
    }
  }, [user, loading, router, redirectTo])

  if (loading) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600"></div>
      </div>
    )
  }

  if (!user) {
    return null
  }

  return <>{children}</>
}

User Profile Management and ID Token Retrieval

// components/UserProfile.tsx
'use client'
import { useState, useEffect } from 'react'
import {
  updateProfile,
  updateEmail,
  updatePassword,
  sendEmailVerification,
  deleteUser,
  User
} from 'firebase/auth'
import { useAuthContext } from './AuthProvider'

export function UserProfile() {
  const { user } = useAuthContext()
  const [displayName, setDisplayName] = useState('')
  const [email, setEmail] = useState('')
  const [newPassword, setNewPassword] = useState('')
  const [loading, setLoading] = useState(false)
  const [idToken, setIdToken] = useState<string | null>(null)

  useEffect(() => {
    if (user) {
      setDisplayName(user.displayName || '')
      setEmail(user.email || '')
      
      // Get ID token
      user.getIdToken().then(setIdToken)
    }
  }, [user])

  const handleUpdateProfile = async () => {
    if (!user) return
    
    setLoading(true)
    
    try {
      await updateProfile(user, {
        displayName: displayName
      })
      
      alert('Profile updated successfully!')
    } catch (error: any) {
      console.error('Profile update error:', error)
      alert('Update failed: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  const handleUpdateEmail = async () => {
    if (!user) return
    
    setLoading(true)
    
    try {
      await updateEmail(user, email)
      await sendEmailVerification(user)
      alert('Email address updated. Please check your confirmation email.')
    } catch (error: any) {
      console.error('Email update error:', error)
      alert('Update failed: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  const handleUpdatePassword = async () => {
    if (!user || !newPassword) return
    
    setLoading(true)
    
    try {
      await updatePassword(user, newPassword)
      setNewPassword('')
      alert('Password updated successfully!')
    } catch (error: any) {
      console.error('Password update error:', error)
      alert('Update failed: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  const handleDeleteAccount = async () => {
    if (!user) return
    
    if (!confirm('Are you sure you want to delete your account? This action cannot be undone.')) {
      return
    }
    
    setLoading(true)
    
    try {
      await deleteUser(user)
      alert('Account deleted successfully.')
    } catch (error: any) {
      console.error('Account deletion error:', error)
      alert('Deletion failed: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  if (!user) {
    return <div>Authentication required</div>
  }

  return (
    <div className="max-w-2xl mx-auto p-6 space-y-6">
      <h1 className="text-2xl font-bold mb-6">Profile Settings</h1>

      {/* User information display */}
      <div className="bg-gray-50 p-4 rounded-lg">
        <h3 className="font-semibold mb-2">User Information</h3>
        <div className="space-y-2 text-sm">
          <p><strong>UID:</strong> {user.uid}</p>
          <p><strong>Created:</strong> {user.metadata.creationTime}</p>
          <p><strong>Last Sign In:</strong> {user.metadata.lastSignInTime}</p>
          <p><strong>Email Verified:</strong> {user.emailVerified ? 'Yes' : 'No'}</p>
          <p><strong>Providers:</strong> {user.providerData.map(p => p.providerId).join(', ')}</p>
        </div>
      </div>

      {/* Display name update */}
      <div className="p-4 border rounded-lg">
        <h3 className="font-semibold mb-2">Display Name</h3>
        <div className="flex gap-2">
          <input
            type="text"
            value={displayName}
            onChange={(e) => setDisplayName(e.target.value)}
            placeholder="Display name"
            className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
          />
          <button
            onClick={handleUpdateProfile}
            disabled={loading}
            className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            Update
          </button>
        </div>
      </div>

      {/* Email address update */}
      <div className="p-4 border rounded-lg">
        <h3 className="font-semibold mb-2">Email Address</h3>
        <div className="flex gap-2">
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="Email address"
            className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
          />
          <button
            onClick={handleUpdateEmail}
            disabled={loading}
            className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            Update
          </button>
        </div>
      </div>

      {/* Password update */}
      <div className="p-4 border rounded-lg">
        <h3 className="font-semibold mb-2">Change Password</h3>
        <div className="flex gap-2">
          <input
            type="password"
            value={newPassword}
            onChange={(e) => setNewPassword(e.target.value)}
            placeholder="New password"
            className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
          />
          <button
            onClick={handleUpdatePassword}
            disabled={loading || !newPassword}
            className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            Change
          </button>
        </div>
      </div>

      {/* ID Token display */}
      {idToken && (
        <div className="p-4 bg-gray-50 rounded-lg">
          <h3 className="font-semibold mb-2">ID Token</h3>
          <textarea
            value={idToken}
            readOnly
            rows={4}
            className="w-full px-3 py-2 text-xs font-mono bg-white border border-gray-300 rounded-md"
          />
        </div>
      )}

      {/* Dangerous operations */}
      <div className="p-4 border border-red-200 bg-red-50 rounded-lg">
        <h3 className="font-semibold text-red-800 mb-2">Dangerous Operations</h3>
        <button
          onClick={handleDeleteAccount}
          disabled={loading}
          className="bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 disabled:opacity-50"
        >
          Delete Account
        </button>
        <p className="text-sm text-red-600 mt-2">
          This action cannot be undone. Please proceed with caution.
        </p>
      </div>
    </div>
  )
}

Multi-Factor Authentication (MFA) Implementation

// components/MultiFactorAuth.tsx
'use client'
import { useState, useEffect } from 'react'
import {
  multiFactor,
  TotpMultiFactorGenerator,
  TotpSecret,
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  RecaptchaVerifier
} from 'firebase/auth'
import { useAuthContext } from './AuthProvider'
import { auth } from '@/lib/firebase'

export function MultiFactorAuth() {
  const { user } = useAuthContext()
  const [totpSecret, setTotpSecret] = useState<TotpSecret | null>(null)
  const [verificationCode, setVerificationCode] = useState('')
  const [qrCodeUrl, setQrCodeUrl] = useState('')
  const [enrolledFactors, setEnrolledFactors] = useState<any[]>([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    if (user) {
      loadEnrolledFactors()
    }
  }, [user])

  const loadEnrolledFactors = () => {
    if (!user) return
    
    const multiFactorUser = multiFactor(user)
    setEnrolledFactors(multiFactorUser.enrolledFactors)
  }

  const enrollTOTP = async () => {
    if (!user) return
    
    setLoading(true)
    
    try {
      const multiFactorUser = multiFactor(user)
      const session = await multiFactorUser.getSession()
      
      // Generate TOTP secret
      const totpSecret = await TotpMultiFactorGenerator.generateSecret(session)
      setTotpSecret(totpSecret)
      
      // Generate QR code URL
      const qrCodeUrl = totpSecret.generateQrCodeUrl(
        user.email || '[email protected]',
        'My App'
      )
      setQrCodeUrl(qrCodeUrl)
      
    } catch (error: any) {
      console.error('TOTP enrollment error:', error)
      alert('MFA setup error: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  const verifyAndFinalizeTOTP = async () => {
    if (!totpSecret || !verificationCode) return
    
    setLoading(true)
    
    try {
      const multiFactorUser = multiFactor(user!)
      
      // Create TOTP assertion
      const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
        totpSecret,
        verificationCode
      )
      
      // Finalize MFA
      await multiFactorUser.enroll(multiFactorAssertion, 'TOTP App')
      
      alert('TOTP multi-factor authentication is now enabled!')
      setTotpSecret(null)
      setVerificationCode('')
      setQrCodeUrl('')
      loadEnrolledFactors()
      
    } catch (error: any) {
      console.error('TOTP verification error:', error)
      alert('Verification code is incorrect: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  const unenrollFactor = async (factorUid: string) => {
    if (!user) return
    
    if (!confirm('Are you sure you want to disable this multi-factor authentication?')) {
      return
    }
    
    setLoading(true)
    
    try {
      const multiFactorUser = multiFactor(user)
      await multiFactorUser.unenroll(factorUid)
      
      alert('Multi-factor authentication disabled.')
      loadEnrolledFactors()
      
    } catch (error: any) {
      console.error('Unenroll error:', error)
      alert('Disable operation failed: ' + error.message)
    } finally {
      setLoading(false)
    }
  }

  if (!user) {
    return <div>Authentication required</div>
  }

  return (
    <div className="max-w-2xl mx-auto p-6 space-y-6">
      <h1 className="text-2xl font-bold mb-6">Multi-Factor Authentication (MFA)</h1>

      {/* Current configuration status */}
      <div className="p-4 bg-gray-50 rounded-lg">
        <h3 className="font-semibold mb-2">Current Configuration</h3>
        {enrolledFactors.length === 0 ? (
          <p className="text-gray-600">No multi-factor authentication configured</p>
        ) : (
          <div className="space-y-2">
            {enrolledFactors.map((factor) => (
              <div key={factor.uid} className="flex items-center justify-between p-2 bg-white rounded border">
                <div>
                  <p className="font-medium">{factor.displayName || factor.factorId}</p>
                  <p className="text-sm text-gray-500">Enrolled: {factor.enrollmentTime}</p>
                </div>
                <button
                  onClick={() => unenrollFactor(factor.uid)}
                  disabled={loading}
                  className="text-red-600 hover:text-red-800 text-sm disabled:opacity-50"
                >
                  Disable
                </button>
              </div>
            ))}
          </div>
        )}
      </div>

      {/* TOTP setup */}
      {!totpSecret ? (
        <div className="p-4 border rounded-lg">
          <h3 className="font-semibold mb-2">Authenticator App 2FA</h3>
          <p className="text-gray-600 mb-4">
            Enhance security using authenticator apps like Google Authenticator, Microsoft Authenticator, etc.
          </p>
          <button
            onClick={enrollTOTP}
            disabled={loading}
            className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            {loading ? 'Setting up...' : 'Setup Authenticator App'}
          </button>
        </div>
      ) : (
        <div className="p-4 border rounded-lg">
          <h3 className="font-semibold mb-4">Authenticator App Setup</h3>
          
          <div className="space-y-4">
            <div>
              <p className="text-sm text-gray-600 mb-2">
                1. Scan the following QR code with your authenticator app:
              </p>
              {qrCodeUrl && (
                <div className="text-center">
                  <img 
                    src={`https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrCodeUrl)}`}
                    alt="QR Code"
                    className="mx-auto border"
                  />
                </div>
              )}
            </div>

            <div>
              <p className="text-sm text-gray-600 mb-2">
                2. Enter the 6-digit code displayed in your authenticator app:
              </p>
              <div className="flex gap-2">
                <input
                  type="text"
                  value={verificationCode}
                  onChange={(e) => setVerificationCode(e.target.value)}
                  placeholder="123456"
                  maxLength={6}
                  className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
                <button
                  onClick={verifyAndFinalizeTOTP}
                  disabled={loading || verificationCode.length !== 6}
                  className="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 disabled:opacity-50"
                >
                  {loading ? 'Verifying...' : 'Verify'}
                </button>
              </div>
            </div>

            <button
              onClick={() => {
                setTotpSecret(null)
                setVerificationCode('')
                setQrCodeUrl('')
              }}
              className="text-gray-600 text-sm hover:text-gray-800"
            >
              Cancel
            </button>
          </div>
        </div>
      )}
    </div>
  )
}