useSimpleAuth Hook
A simplified authentication hook for Firebase auth state monitoring and Multi-Factor Authentication (MFA) detection.
Overview
useSimpleAuth is a focused authentication hook designed specifically for sign-in pages that need to detect and handle MFA requirements. It monitors Firebase authentication state changes and provides utilities to detect when Firebase requests Multi-Factor Authentication during sign-in.
What it handles:
- Firebase authentication state monitoring via
onAuthStateChanged - MFA requirement detection from Firebase auth errors
- Auth status tracking for UI rendering decisions
- MFA resolver management for verification components
What it does NOT handle:
- Backend user verification or profile loading
- Company selection after authentication
- Complete auth flow orchestration
The hook is called “Simple” because it’s a subset of a planned full useAuth hook. It focuses solely on Firebase auth state and MFA detection, making it perfect for sign-in pages without the overhead of complete authentication management.
Responsibilities
The hook has four core responsibilities:
- Monitors Firebase authentication state - Subscribes to Firebase
onAuthStateChangedto track when users sign in, sign out, or their session changes - Detects MFA requirements - Identifies when Firebase throws an MFA-required error during sign-in attempts
- Provides auth status for UI rendering - Maintains current authentication status to help components decide what to render
- Tracks MFA resolver - Stores the Firebase
MultiFactorResolverneeded by MFA verification components
State Management
AUTH_STATUS Constants
The hook uses five status constants to represent the authentication state:
| Status | Value | Description | When Set |
|---|---|---|---|
INITIALIZING_FIREBASE_SESSION | 'initializing.firebase_session' | Firebase auth state is being determined | On hook mount, before Firebase callback |
UNAUTHENTICATED | 'unauthenticated' | No user is signed in | Firebase onAuthStateChanged returns null |
AUTHENTICATED | 'authenticated' | User successfully signed in (normal flow) | Firebase onAuthStateChanged returns user object |
AUTHENTICATED_VIA_MFA | 'authenticated.via_mfa' | User completed MFA and is now authenticated | Firebase onAuthStateChanged returns user after MFA_REQUIRED state |
MFA_REQUIRED | 'mfa.required' | MFA verification needed | detectMfaRequest() detects auth/multi-factor-auth-required error |
AuthState Type
The internal state shape managed by the hook:
type AuthState = { status: AuthStatus, user: ?firebase.User, mfaResolver: ?MultiFactorResolver,}Field population:
status- Always set, starts asINITIALIZING_FIREBASE_SESSIONuser- Populated when status isAUTHENTICATEDorAUTHENTICATED_VIA_MFA,nullotherwisemfaResolver- Only populated when status isMFA_REQUIRED,nullafter authentication or on sign-out
API Reference
Return Values
const { // State (read-only) status, user, mfaResolver,
// Derived boolean helpers isInitializing, isUnauthenticated, isAuthenticated, mfaRequired,
// Utility functions detectMfaRequest,} = useSimpleAuth();State Properties
| Property | Type | Description |
|---|---|---|
status | AuthStatus | Current authentication status (one of five AUTH_STATUS constants) |
user | ?firebase.User | Firebase user object when authenticated, null otherwise |
mfaResolver | ?MultiFactorResolver | Firebase MFA resolver when MFA is required, null otherwise |
Derived Helpers
Boolean helpers for common status checks:
| Helper | Type | Description |
|---|---|---|
isInitializing | boolean | true when status is INITIALIZING_FIREBASE_SESSION |
isUnauthenticated | boolean | true when status is UNAUTHENTICATED |
isAuthenticated | boolean | true when status is AUTHENTICATED or AUTHENTICATED_VIA_MFA |
mfaRequired | boolean | true when status is MFA_REQUIRED |
Utility Functions
detectMfaRequest(error: Error): boolean
Detects if a Firebase error indicates MFA is required.
- Parameters: Firebase error object from sign-in attempt
- Returns:
trueif MFA was detected,falseotherwise - Side Effects: If MFA detected, updates state to
MFA_REQUIREDand stores the resolver - Usage: Call in the
catchblock of sign-in attempts
Usage Examples
Basic Sign-in with MFA Detection
import useSimpleAuth from '../../hooks/auth/useSimpleAuth';import firebase from 'firebase/app';
function SignInPage() { const { detectMfaRequest, mfaRequired, mfaResolver } = useSimpleAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState('');
const handleSignIn = async () => { try { await firebase.auth().signInWithEmailAndPassword(email, password); // Success - Firebase will trigger onAuthStateChanged // which updates the hook state to AUTHENTICATED } catch (error) { // Check if this is an MFA request if (detectMfaRequest(error)) { // Hook state automatically updated to MFA_REQUIRED // UI will re-render to show MFA form return; }
// Handle other errors (wrong password, user not found, etc.) console.error('Sign-in error:', error.message); } };
return ( <> {!mfaRequired && <SignInForm onSubmit={handleSignIn} />} {mfaRequired && <MfaVerificationForm mfaResolver={mfaResolver} />} </> );}Conditional Rendering Based on Status
import { AUTH_STATUS } from '../../hooks/auth/auth';import useSimpleAuth from '../../hooks/auth/useSimpleAuth';
function AuthPage() { const { status, isAuthenticated, mfaRequired } = useSimpleAuth();
// Show loading while Firebase initializes if (status === AUTH_STATUS.INITIALIZING_FIREBASE_SESSION) { return <LoadingSpinner />; }
// Show MFA verification form if (mfaRequired) { return <MfaVerificationForm />; }
// User is authenticated, show next step if (isAuthenticated) { return <CompanySelection />; }
// Default: show sign-in form return <SignInForm />;}Integration Patterns
Sign-in Page Integration
The typical integration pattern for sign-in pages:
- Import the hook at the top of your component
- Call
detectMfaRequest()in the catch block of sign-in attempts - Conditionally render MFA UI based on the
mfaRequiredboolean - Pass
mfaResolverto the MFA verification component
The hook automatically manages state transitions, so you only need to react to the current state.
State Transitions
The hook handles these state transitions automatically:
INITIALIZING_FIREBASE_SESSION ├─→ UNAUTHENTICATED (Firebase returns no user) └─→ AUTHENTICATED (Firebase returns existing user session)
UNAUTHENTICATED ├─→ AUTHENTICATED (successful sign-in) └─→ MFA_REQUIRED (detectMfaRequest() called with MFA error)
MFA_REQUIRED └─→ AUTHENTICATED_VIA_MFA (user completes MFA verification)
AUTHENTICATED or AUTHENTICATED_VIA_MFA └─→ UNAUTHENTICATED (user signs out)Key insight: The distinction between AUTHENTICATED and AUTHENTICATED_VIA_MFA helps track whether the current session came through MFA, which can be useful for analytics or security auditing.
Relationship with useMfa
useSimpleAuth and useMfa work together but have distinct responsibilities:
| Hook | Responsibility | Focus |
|---|---|---|
useSimpleAuth | Detection & State Management | Monitors Firebase auth state, detects MFA requirements, provides status |
useMfa | MFA Verification Logic | Handles reCAPTCHA, SMS sending, code verification, error mapping |
Why separate?
- Single Responsibility: Each hook has one clear purpose
- Reusability:
useMfacan be used independently in different contexts - Testability: Easier to test each layer separately
Data Flow:
useSimpleAuthdetects MFA requirement (viadetectMfaRequest())- Provides
mfaResolverto components useMfa(insideMfaPhoneVerification) uses the resolver for verification- On success, Firebase triggers
onAuthStateChanged useSimpleAuthupdates toAUTHENTICATED_VIA_MFA
Usage in Codebase
The hook is currently integrated in these sign-in flows:
/views/SignIn/index.js- Password-based sign-in with MFA detection/pages/sign-in-confirmation.js- Email link sign-in with MFA support
Both implementations follow the same pattern: catch Firebase errors, call detectMfaRequest(), and conditionally render MFA UI.
Related Documentation
- MFA Documentation - Complete MFA implementation details including components and flows
hooks/auth/README.md- Quick reference for all authentication hooks