Authentication

The AI Ingredient Scanner uses Firebase Authentication for user management, with Google Sign-In as the primary authentication method and optional guest mode for anonymous usage.


Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    AUTHENTICATION FLOW                          β”‚
β”‚                                                                 β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚     β”‚  User  β”‚                                                  β”‚
β”‚     β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                                                  β”‚
β”‚         β”‚                                                       β”‚
β”‚         β–Ό                                                       β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                            β”‚
β”‚   β”‚  Auth State?  β”‚                                            β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                                            β”‚
β”‚           β”‚                                                     β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚     β–Ό           β–Ό                  β–Ό                           β”‚
β”‚ [Not Signed] [Guest Mode]    [Signed In]                       β”‚
β”‚     β”‚           β”‚                  β”‚                           β”‚
β”‚     β–Ό           β”‚                  β”‚                           β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚                  β”‚                           β”‚
β”‚ β”‚  Login    β”‚   β”‚                  β”‚                           β”‚
β”‚ β”‚  Screen   β”‚   β”‚                  β”‚                           β”‚
β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜   β”‚                  β”‚                           β”‚
β”‚       β”‚         β”‚                  β”‚                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”    β”‚                  β”‚                           β”‚
β”‚  β–Ό         β–Ό    β”‚                  β”‚                           β”‚
β”‚[Google] [Guest] β”‚                  β”‚                           β”‚
β”‚  β”‚         β”‚    β”‚                  β”‚                           β”‚
β”‚  β–Ό         β”‚    β”‚                  β”‚                           β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚    β”‚                  β”‚                           β”‚
β”‚β”‚ Firebase β”‚β”‚    β”‚                  β”‚                           β”‚
β”‚β”‚   Auth   β”‚β”‚    β”‚                  β”‚                           β”‚
β”‚β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜β”‚    β”‚                  β”‚                           β”‚
β”‚     β”‚      β”‚    β”‚                  β”‚                           β”‚
β”‚     β–Ό      β”‚    β”‚                  β”‚                           β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚    β”‚                  β”‚                           β”‚
β”‚β”‚ Firestoreβ”‚β”‚    β”‚                  β”‚                           β”‚
β”‚β”‚   Sync   β”‚β”‚    β”‚                  β”‚                           β”‚
β”‚β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜β”‚    β”‚                  β”‚                           β”‚
β”‚     β”‚      β”‚    β”‚                  β”‚                           β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                  β”‚                                              β”‚
β”‚                  β–Ό                                              β”‚
β”‚            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚            β”‚   Home   β”‚                                        β”‚
β”‚            β”‚  Screen  β”‚                                        β”‚
β”‚            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Features

FeatureDescription
Google Sign-InOAuth 2.0 authentication via Firebase
Guest ModeAnonymous usage without account
Profile SyncFirestore persistence across devices
Preferences SyncAllergies, skin type, theme saved to cloud
Account DeletionGDPR-compliant data removal

Authentication Flow

1. Initial State

When the app launches, it checks for an existing Firebase session:

// AuthContext.tsx
useEffect(() => {
  const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
    setUser(firebaseUser);
    if (firebaseUser) {
      await syncUserToFirestore(firebaseUser);
    }
    setLoading(false);
  });
  return () => unsubscribe();
}, []);

2. Login Screen Options

  • Continue with Google: Full authentication with profile sync
  • Continue as Guest: Anonymous mode (no data persistence)

3. Google Sign-In

Platform-specific implementation for web and native:

const signInWithGoogle = async () => {
  if (Platform.OS === 'web') {
    // Web: Firebase popup
    await signInWithPopup(auth, googleProvider);
  } else {
    // Native: Expo Auth Session
    await promptAsync();
  }
};

4. Firestore Profile Sync

On successful sign-in, user data syncs to Firestore:

// User document structure
interface FirestoreUser {
  uid: string;
  email: string | null;
  displayName: string | null;
  photoURL: string | null;
  provider: string;
  createdAt: Timestamp;
  lastLoginAt: Timestamp;
  preferences?: {
    allergies: string[];
    skinType: SkinType;
    expertise: ExpertiseLevel;
    theme: ThemeMode;
  };
}

Preferences Sync

User preferences are managed by PreferencesContext and automatically synced:

Authenticated Users
Debounced save to Firestore β†’ Synced across devices
Guest Users
Save to AsyncStorage β†’ Local only

Synced Preferences

PreferenceDescriptionDefault
allergiesKnown allergens to flag[]
skinTypeSkin type for cosmetic analysisnormal
expertiseExplanation complexitybeginner
themeLight or dark modelight

Auto-Save with Debouncing

Preferences are saved with a 1-second debounce to prevent excessive writes:

const debouncedSave = useCallback((newPrefs: UserPreferences) => {
  if (saveTimeoutRef.current) {
    clearTimeout(saveTimeoutRef.current);
  }

  saveTimeoutRef.current = setTimeout(async () => {
    if (user) {
      await setDoc(doc(db, 'users', user.uid),
        { preferences: newPrefs },
        { merge: true }
      );
    }
  }, 1000);
}, [user]);

Firebase Configuration

Setup

Firebase is configured in src/config/firebase.ts:

const firebaseConfig = {
  apiKey: 'your-api-key',
  authDomain: 'your-project.firebaseapp.com',
  projectId: 'your-project-id',
  storageBucket: 'your-project.appspot.com',
  messagingSenderId: 'your-sender-id',
  appId: 'your-app-id',
  measurementId: 'your-measurement-id',
};

OAuth Configuration

For Google Sign-In to work:

  1. Firebase Console: Enable Google provider in Authentication
  2. Google Cloud Console: Configure OAuth consent screen
  3. Web Client ID: Add to AuthContext.tsx
const [request, response, promptAsync] = Google.useAuthRequest({
  webClientId: 'your-oauth-client-id.apps.googleusercontent.com',
});

User Management

Sign Out

const signOut = async () => {
  await firebaseSignOut(auth);
  setUserProfile(null);
};

Account Deletion (GDPR-Compliant)

Deletion removes all user data from Firestore and Firebase Auth:

const deleteAccount = async () => {
  // 1. Delete scan history subcollection
  const scansRef = collection(db, 'users', user.uid, 'scans');
  const scansSnap = await getDocs(scansRef);

  const batch = writeBatch(db);
  scansSnap.docs.forEach((scanDoc) => {
    batch.delete(scanDoc.ref);
  });

  // 2. Delete user document
  batch.delete(doc(db, 'users', user.uid));
  await batch.commit();

  // 3. Delete Firebase auth user
  await deleteUser(user);
};

Firestore Security Rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Users can only access their own data
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;

      // Scan history subcollection
      match /scans/{scanId} {
        allow read, write: if request.auth != null && request.auth.uid == userId;
      }
    }
  }
}

UI Components

Login Screen

Premium gradient design with:

  • App branding and logo
  • Feature pills (Scan Labels, AI Analysis, Allergy Alerts)
  • Google Sign-In button
  • Guest mode option
  • Privacy policy link

Profile Section

In Settings, authenticated users see:

  • ProfileAvatar: Google photo or colored initial fallback
  • Display name and email address
  • Sign Out button
  • Privacy Policy (in-app modal)
  • Collapsible Danger Zone with Delete Account option

Guest users see:

  • Guest mode avatar with "G" initial
  • Sign In with Google button
  • Privacy Policy link

Danger Zone Pattern

Account deletion is protected by a collapsible section to prevent accidental clicks:

const [showDangerZone, setShowDangerZone] = useState(false);

// Toggle with smooth animation
const toggleDangerZone = () => {
  LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
  setShowDangerZone(!showDangerZone);
};

Privacy Policy

The privacy policy is displayed in-app via PrivacyPolicyModal. It covers:

  • Data collection and usage
  • User rights (access, update, delete)
  • Third-party services (Firebase, Google)
  • Data retention policies

Related Documentation