Skip to content
Back to skills

Firebase Auth

ASecurity

Use when setting up auth, managing auth state, implementing email/password or social sign-in, handling auth errors, or managing users.

  • 639 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added August 31, 2026
ai-agentsrustgogcptestingapidatabasesecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned August 31, 2026

npx -y skills add evanca/flutter-ai-rules --skill firebase-auth --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Firebase Auth?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Firebase Auth
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/evanca-firebase-auth/badge)](https://www.skillsdirectory.com/skills/evanca-firebase-auth)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: firebase-auth
description: "Use when setting up auth, managing auth state, implementing email/password or social sign-in, handling auth errors, or managing users."
license: MIT
---

# Firebase Authentication Skill

This skill defines how to correctly use Firebase Authentication in Flutter applications.

## When to Use

Use this skill when:

* Setting up Firebase Authentication in a Flutter project.
* Listening to authentication state changes.
* Implementing email/password, phone number, or social sign-in.
* Managing user profiles, account linking, or MFA.
* Handling authentication errors (including iOS `recaptcha-sdk-not-linked` for phone auth).
* Applying security best practices for auth flows.

---

## 1. Setup and Configuration

```
flutter pub add firebase_auth
```

```dart
import 'package:firebase_auth/firebase_auth.dart';
```

- Enable desired authentication providers in the **Firebase console** before using them.
- Initialize Firebase before using any Firebase Authentication features.

**Local emulator for testing:**

```dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
  // ...
}
```

---

## 2. Authentication State Management

Use the appropriate stream based on what you need to observe:

| Stream | Fires when |
|---|---|
| `authStateChanges()` | User signs in or out |
| `idTokenChanges()` | ID token changes (including custom claims) |
| `userChanges()` | User data changes (e.g., profile updates) |

```dart
FirebaseAuth.instance
  .authStateChanges()
  .listen((User? user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');
    }
  });
```

- Listen to these streams **immediately** when the app starts to handle the initial auth state.
- Custom claims are only available after sign-in, re-authentication, token expiration, or manual token refresh.

---

## 3. Email and Password Authentication

**Create a new account:**

```dart
try {
  final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
    email: emailAddress,
    password: password,
  );
} on FirebaseAuthException catch (e) {
  if (e.code == 'weak-password') {
    print('The password provided is too weak.');
  } else if (e.code == 'email-already-in-use') {
    print('The account already exists for that email.');
  }
} catch (e) {
  print(e);
}
```

**Sign in:**

```dart
try {
  final credential = await FirebaseAuth.instance.signInWithEmailAndPassword(
    email: emailAddress,
    password: password,
  );
} on FirebaseAuthException catch (e) {
  if (e.code == 'invalid-credential') {
    // Email enumeration protection enabled (default since Sep 2023):
    // replaces 'user-not-found' and 'wrong-password'.
    print('Invalid email or password.');
  } else if (e.code == 'user-not-found') {
    print('No user found for that email.');
  } else if (e.code == 'wrong-password') {
    print('Wrong password provided for that user.');
  }
}
```

- Verify the user's email address after account creation.
- Firebase rate-limits new email/password sign-ups from the same IP to protect against abuse.
- On iOS/macOS, authentication state persists between app re-installs via the system keychain.
- Since September 2023, Firebase enables **email enumeration protection** by default on new projects, replacing `user-not-found` and `wrong-password` with `invalid-credential`. Manage this in the Firebase console under **Authentication > Settings**.
- When email enumeration protection is enabled, `sendPasswordResetEmail()` may complete without an error even if the email is not registered. Treat this as expected behavior and do not use password-reset responses to infer whether an email exists.

**Share authentication state between Apple apps:**

On Apple platforms, share auth state between apps in the same developer account by storing it in a shared Keychain access group. Enable the Keychain Sharing capability for each app with the same access group, then configure Firebase Auth with the fully qualified access group:

```dart
await FirebaseAuth.instance.setSettings(
  userAccessGroup: 'TEAMID.com.example.group1',
);
```

Switching from the default Keychain to a shared access group signs out the existing user unless migrated. Pass `migrateCurrentUser: true` (requires a non-null `userAccessGroup`) to preserve the current session, including an anonymous one — safe to call at app startup before any other Auth call, since it reads the existing session straight from the Keychain before Auth restores it. Migration can overwrite a user already stored in the destination access group.

---

## 4. Social Authentication

**Google Sign-In (native platforms):**

```dart
Future<UserCredential> signInWithGoogle() async {
  final GoogleSignInAccount? googleUser = await GoogleSignIn.instance.authenticate();
  final GoogleSignInAuthentication googleAuth = googleUser.authentication;
  final credential = GoogleAuthProvider.credential(idToken: googleAuth.idToken);
  return await FirebaseAuth.instance.signInWithCredential(credential);
}
```

**Google Sign-In (web):**

```dart
Future<UserCredential> signInWithGoogle() async {
  GoogleAuthProvider googleProvider = GoogleAuthProvider();
  googleProvider.addScope('https://www.googleapis.com/auth/contacts.readonly');
  googleProvider.setCustomParameters({'login_hint': 'user@example.com'});
  return await FirebaseAuth.instance.signInWithPopup(googleProvider);
}
```

- Configure platform-specific settings for each provider (e.g., SHA1 key for Google Sign-In on Android).
- If a user signs in with a social provider after registering with the same email manually, Firebase's trusted provider concept will automatically change their authentication provider.
- On Android, `signInWithProvider` opens a Chrome Custom Tab. If `AndroidManifest.xml` contains `android:taskAffinity=""` (Flutter's default), the tab closes when the user switches apps (e.g., to use a password manager), causing a `web-context-already-presented` error. Remove `android:taskAffinity=""` to fix this.
- When signing in with Apple, add the `email` and `name` scopes to present the full first-time sign-in UI (including "Share/Hide email"):
  ```dart
  final appleProvider = AppleAuthProvider();
  appleProvider.addScope('email');
  appleProvider.addScope('name');
  ```
- To revoke Apple auth tokens after sign-in, use the appropriate API per platform:
  - **Apple platforms** (iOS/macOS/web): use `revokeTokenWithAuthorizationCode()` with the authorization code from `userCredential.additionalUserInfo?.authorizationCode`.
  - **Android**: use `revokeAccessToken()` with the access token from `userCredential.credential?.accessToken`.
  ```dart
  // Apple platforms (iOS/macOS/web)
  final authCode = userCredential.additionalUserInfo?.authorizationCode;
  if (authCode != null) {
    await FirebaseAuth.instance.revokeTokenWithAuthorizationCode(authCode);
  }

  // Android
  final accessToken = userCredential.credential?.accessToken;
  if (accessToken != null) {
    await FirebaseAuth.instance.revokeAccessToken(accessToken);
  }
  ```

---

## 5. Phone Number Authentication

Before using phone authentication, ensure platform-specific prerequisites are met:

- **Android**: SHA-1 hashes must be configured in the Firebase console and Google Play Integrity API enabled.
- **iOS**: APNs authentication key must be configured with FCM and background modes for remote notifications enabled.
- **Web**: Add your application's domain to the Firebase console under **OAuth redirect domains**.

Phone number sign-in is only supported on real devices and the web. Testing on device emulators is not supported.

**iOS: `recaptcha-sdk-not-linked` error**

On iOS, `verifyPhoneNumber` can throw `FirebaseAuthException` with code `recaptcha-sdk-not-linked` when Identity Platform expects reCAPTCHA Enterprise but the native SDK is not linked. This cannot be resolved from Dart — fix it at the native iOS or GCP level:

- **Recommended**: Link the reCAPTCHA Enterprise iOS SDK in Xcode following [Google's guide](https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps).
- **Alternative**: Disable reCAPTCHA SMS defense via the Identity Toolkit [`projects.updateConfig`](https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects/updateConfig) REST API (set `recaptchaConfig.phoneEnforcementState` to `OFF` and `recaptchaConfig.useSmsTollFraudProtection` to `false`). See the [official steps](https://cloud.google.com/identity-platform/docs/recaptcha-tfp#disable_recaptcha_sms_defense). This reduces fraud protection — prefer linking the SDK.
- If the SDK uses a Safari view controller-hosted challenge, handle the return URL using `uni_links`/`app_links` or `application:openURL:` in the iOS runner.

---

## 6. Error Handling

- Always use `try-catch` with `FirebaseAuthException`.
- Check `e.code` to identify specific error types.
- Handle `account-exists-with-different-credential` by fetching sign-in methods for the email and guiding users through the correct flow.
- Handle `too-many-requests` with retry logic or user feedback.
- Handle `operation-not-allowed` by ensuring the provider is enabled in the Firebase console.
- On iOS, `recaptcha-sdk-not-linked` during `verifyPhoneNumber` is raised by the native Firebase iOS Auth SDK and requires native setup or GCP configuration changes — it cannot be fixed from Dart code alone.

---

## 7. User Management

```dart
// Update profile
await FirebaseAuth.instance.currentUser?.updateProfile(
  displayName: "Jane Q. User",
  photoURL: "https://example.com/jane-q-user/profile.jpg",
);

// Update email (sends verification to new address first)
await user?.verifyBeforeUpdateEmail("newemail@example.com");
```

- Use `verifyBeforeUpdateEmail()` — **not** `updateEmail()` — to change a user's email. The email only updates after the user verifies it.
- Store only essential info in the auth profile; use a database for additional user data.
- Use `linkWithCredential()` to connect multiple auth providers to a single account.
- Verify the user's identity before linking new credentials.
- Use `fetchSignInMethodsForEmail()` when handling account linking.

---

## 8. Security Best Practices

- Never store sensitive authentication credentials in client-side code.
- Monitor auth state changes for proper session management.
- Validate user input before submitting authentication requests to prevent injection attacks.
- Call `FirebaseAuth.instance.signOut()` when users exit the app.
- For sensitive operations, re-authenticate users with `reauthenticateWithCredential()`.
- Enforce strong password policies for email/password auth.
- In Realtime Database and Cloud Storage Security Rules, use the `auth` variable to get the signed-in user's UID for access control.
- Use multi-factor authentication for sensitive applications.

---

## 9. Multi-Factor Authentication

> **Security warning:** Avoid SMS-based MFA. SMS is insecure and easy to compromise or spoof.

> **Platform limitation:** Windows does not support MFA. MFA with multiple tenants is not supported on Flutter.

- Enable at least one MFA-compatible provider before implementing MFA.

---

## 10. Email Link Authentication

> **Important:** Firebase Dynamic Links is deprecated for email link authentication. Firebase Hosting is now used to send sign-in links.

- Set `handleCodeInApp: true` in `ActionCodeSettings` — sign-in must always be completed in the app.
- Store the user's email locally (e.g., `SharedPreferences`) when sending the sign-in link.
- **Never** pass the user's email in redirect URL parameters — this enables session injection attacks.
- Use HTTPS URLs in production to prevent link interception.
- Configure the app to detect incoming links and parse the underlying deep link for sign-in completion.

---

## References

- [Firebase Authentication Flutter documentation](https://firebase.google.com/docs/auth/flutter/start)
- [Email/password authentication](https://firebase.google.com/docs/auth/flutter/password-auth)
- [Federated identity & social sign-in](https://firebase.google.com/docs/auth/flutter/federated-auth)
- [Multi-factor authentication](https://firebase.google.com/docs/auth/flutter/multi-factor)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.