← Files FirebaseARCHIVED FILE

skills/firebase-auth-basics/references/flutter_setup.md

5.46 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

# Firebase Auth & Google Sign-In for Flutter

When integrating Firebase Authentication and Google Sign-In into Flutter apps
targeting cross-platform environments (like Mobile + Web), you must navigate
several breaking changes introduced in `google_sign_in` 7.x+ and some
platform-specific quirks.

## 1. `google_sign_in` 7.2.0 API Changes

- **Method Renamed**: The `signIn()` method is deprecated/removed and has been
  replaced with `authenticate()`.
- **Token Separation**: The `GoogleSignInAuthentication` object no longer
  packages both identity and authorization tokens together. Initial
  authentication now only provides the `idToken`. If an `accessToken` is
  required for Google APIs, you must explicitly request server authorization
  separately.

## 2. Initialization & Web Hang/Crash Pitfalls

- **Initialization Requirement**: In 7.x, you must call
  `await GoogleSignIn.instance.initialize();` globally before using the plugin.
- **Web Client ID Constraint**: On Flutter Web, if you call `initialize()`
  without passing a `clientId` argument OR specifying the
  `<meta name="google-signin-client_id" ... />` tag in `web/index.html`, the
  Dart Web Debug Service (DWDS) and the app will throw an assertion error and
  **hang infinitely**, resulting in a blank screen.
- **Common Workaround**: If you intend to use Firebase Auth's
  `signInWithPopup(GoogleAuthProvider())` for the web, you can conditionally
  skip the local `GoogleSignIn` package initialization entirely:
  ```dart
  import 'package:flutter/foundation.dart' show kIsWeb;

  if (!kIsWeb) {
    await GoogleSignIn.instance.initialize();
  }
  ```

## 3. Web Logout Crashes

- If you bypassed `GoogleSignIn` initialization on the web (as demonstrated
  above), you cannot call its `signOut()` method later. Attempting to execute
  `await GoogleSignIn.instance.signOut();` during the user's logout flow on the
  Web platform evaluates against an uninitialized context or unsupported
  environment, crashing the app.
- **Solution**: Conditionally separate the logout logic for Web to rely entirely
  on `FirebaseAuth`:
  ```dart
  if (!kIsWeb) {
      await GoogleSignIn.instance.signOut();
  }
  await FirebaseAuth.instance.signOut();
  ```

## 4. Prototyping Workaround: Bypassing Firestore Composite Indices

*Note: This is a Firestore consideration frequently encountered while fetching
user-specific auth data.*

When querying data via `FirebaseFirestore.instance`, using
`.where('userId', isEqualTo: uid)` combined with a sort on a different field
like `.orderBy('createdAt', descending: true)` mandates a custom composite
index.

- **Quick Alternative**: During local development, you can avoid defining
  indexes by pulling the data using only `.where()` and applying the `.sort()`
  operation client-side on the resulting `List` in Dart.

## 5. Robust `AuthService` Boilerplate

Here is a comprehensive `AuthService` implementation that properly handles the
initialization and platform differences between Flutter Web and Mobile:

```dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  AuthService() {
    if (!kIsWeb) {
      GoogleSignIn.instance.initialize();
    }
  }

  // Stream to listen to auth state changes
  Stream<User?> get authStateChanges => _auth.authStateChanges();

  // Get current user
  User? get currentUser => _auth.currentUser;

  // Google Sign-In
  Future<UserCredential?> signInWithGoogle() async {
    try {
      if (kIsWeb) {
        // Web uses popup to avoid DWDS hangs and manual client ID config
        GoogleAuthProvider authProvider = GoogleAuthProvider();
        return await _auth.signInWithPopup(authProvider);
      } else {
        // Mobile uses standard flow
        final GoogleSignInAccount? googleUser = await GoogleSignIn.instance.authenticate();
        if (googleUser == null) return null; // Cancelled

        final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
        
        final AuthCredential credential = GoogleAuthProvider.credential(
          idToken: googleAuth.idToken,
        );
        
        return await _auth.signInWithCredential(credential);
      }
    } catch (e) {
      print("Error during Google Sign-In: \$e");
      return null;
    }
  }

  // Sign out
  Future<void> signOut() async {
    try {
      if (!kIsWeb) {
        await GoogleSignIn.instance.signOut();
      }
      await _auth.signOut();
    } catch (e) {
      print("Error signing out: \$e");
    }
  }
}
```

## 6. Troubleshooting `auth/unauthorized-domain` on Flutter Web

When running Flutter Web locally and using `signInWithPopup`, you might
encounter a situation where the Google Sign-In popup opens and immediately
closes.

- **Symptom**: The console shows
  `Sign-in failed: [firebase_auth/unauthorized-domain] This domain is not authorized for OAuth operation for your Firebase project.`
- **Cause**: The domain (usually `localhost` during local testing) is not listed
  in the Authorized Domains in the Firebase Console.
- **Solution**: Add `localhost` to the Authorized Domains list in the Firebase
  Console (Authentication > Settings > Authorized domains) or in your
  `firebase.json` auth config.
- **CRITICAL**: Do NOT include the protocol or port number when adding the
  domain (e.g., use `localhost`, NOT `http://localhost:9090`). Flutter Web often
  runs on random ports or specific ports, but Firebase Auth only cares about the
  domain.

SHA-256: 181fdba6d78f2d2da85bfccb30ac560b85c61c55454381763725cfe4f2467110